Post

Service Accounts - Kubernetes Security

A Service Account is an identity used by processes running inside a Pod to interact with the Kubernetes API Server.

Service Accounts - Kubernetes Security

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:

AspectLegacy TokenBound Token (1.24+)
ValidityNever expiresHas expiration time
StorageStored in SecretProjected volume
AudienceNo bindingBound to specific audience
SecurityHigh risk if leakedMore secure, auto-rotate
RotationManualAutomatic

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

References

This post is licensed under CC BY 4.0 by the author.