Service Accounts - Kubernetes Security
A Service Account is an identity used by processes running inside a Pod to interact with the Kubernetes API Server.
Introduction
A Service Account is an identity used by processes running inside a Pod to interact with the Kubernetes API Server. Unlike user accounts used by humans, Service Accounts are specifically designed for applications and workloads running within the cluster.
Service Account Components
1. Service Account Object
A Service Account is a Kubernetes object representing an identity for processes running in a Pod.
1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: production
labels:
app: my-application
# Kubernetes 1.24+: Token is not automatically created as Secret
automountServiceAccountToken: true # Default: true
2. Token Types
Difference between Legacy vs Bound Tokens:
| Aspect | Legacy Token | Bound Token (1.24+) |
|---|---|---|
| Validity | Never expires | Has expiration time |
| Storage | Stored in Secret | Projected volume |
| Audience | No binding | Bound to specific audience |
| Security | High risk if leaked | More secure, auto-rotate |
| Rotation | Manual | Automatic |
3. Token Projection
Projected Service Account Token is a modern mechanism for providing tokens to Pods with better security features.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
serviceAccountName: my-app-sa
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: token
mountPath: /var/run/secrets/tokens
readOnly: true
volumes:
- name: token
projected:
sources:
- serviceAccountToken:
path: api-token
expirationSeconds: 3600 # 1 hour
audience: api # Audience binding
Service Account Security Best Practices
1. Disable Auto-Mount Token
By default, Kubernetes automatically mounts Service Account tokens to every Pod. This can be a security risk if the Pod doesn’t need API Server access.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Option 1: Disable at ServiceAccount level
apiVersion: v1
kind: ServiceAccount
metadata:
name: no-api-access-sa
namespace: production
automountServiceAccountToken: false
---
# Option 2: Disable at Pod level
apiVersion: v1
kind: Pod
metadata:
name: isolated-pod
spec:
serviceAccountName: default
automountServiceAccountToken: false # Override SA setting
containers:
- name: app
image: nginx
2. Use Dedicated Service Accounts
Don’t use the default Service Account. Create dedicated Service Accounts for each application with minimal permissions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# BAD: Using default service account
apiVersion: v1
kind: Pod
metadata:
name: bad-pod
spec:
# serviceAccountName: default (implicit)
containers:
- name: app
image: nginx
---
# GOOD: Using dedicated service account
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-sa
namespace: production
automountServiceAccountToken: false
---
apiVersion: v1
kind: Pod
metadata:
name: good-pod
spec:
serviceAccountName: nginx-sa
automountServiceAccountToken: false
containers:
- name: app
image: nginx
3. Principle of Least Privilege
Grant only the permissions that the application actually needs. Use Role and RoleBinding to limit access.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Service Account with minimal permissions
apiVersion: v1
kind: ServiceAccount
metadata:
name: configmap-reader-sa
namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: configmap-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["app-config"] # Only specific configmap
verbs: ["get", "watch"] # Only read operations
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: configmap-reader-binding
namespace: production
subjects:
- kind: ServiceAccount
name: configmap-reader-sa
namespace: production
roleRef:
kind: Role
name: configmap-reader
apiGroup: rbac.authorization.k8s.io
