Every application in Kubernetes runs under an identity. If you do not configure one, a workload uses the namespace's `default` ServiceAccount. This complicates security, auditing, and troubleshooting.
Therefore, use a separate ServiceAccount for each application. This lets you assign permissions precisely, disable API access for workloads that do not need it, and identify more quickly which workload performed an action.
- Create a dedicated ServiceAccount for each application or component.
- Disable `automountServiceAccountToken` if an application does not need to use the Kubernetes API.
- Assign permissions only after determining which API calls the application actually needs.
Create two ServiceAccounts: one with and one without API access
Step 1
Create a .yaml file, for example:
nano serviceaccounts-demo.yamlAdd the following content to the file:
apiVersion: v1
kind: Namespace
metadata:
name: kb-sa-test
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-api
namespace: kb-sa-test
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-noapi
namespace: kb-sa-test
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: configmap-reader
namespace: kb-sa-test
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-api-configmap-reader
namespace: kb-sa-test
subjects:
- kind: ServiceAccount
name: app-api
namespace: kb-sa-test
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: configmap-reader
---
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-config
namespace: kb-sa-test
data:
example: ok
---
apiVersion: v1
kind: Pod
metadata:
name: noapi-pod
namespace: kb-sa-test
spec:
serviceAccountName: app-noapi
automountServiceAccountToken: false
restartPolicy: Never
containers:
- name: busybox
image: busybox:1.36
command: ["/bin/sh", "-c", "sleep 3600"]Save the changes and close the file (ctrl + x > y > enter).
Step 2
Create the resources:
kubectl apply -f serviceaccounts-demo.yaml
kubectl -n kb-sa-test wait --for=condition=Ready pod/noapi-pod --timeout=180sYou now have one ServiceAccount with limited API permissions and one ServiceAccount without a token mount for workloads that do not need to use the API.
Check the differences between the two ServiceAccounts
Step 1
Check whether `app-api` is allowed to read ConfigMaps in its own namespace:
kubectl auth can-i list configmaps \
--as=system:serviceaccount:kb-sa-test:app-api \
-n kb-sa-testThe expected result is `yes`.
Step 2
Check that `app-noapi` is not allowed to perform the same action:
kubectl auth can-i list configmaps \
--as=system:serviceaccount:kb-sa-test:app-noapi \
-n kb-sa-testThe expected result is `no`.
Step 3
Then check that the pod without API access does not have a ServiceAccount token mounted:
kubectl -n kb-sa-test exec noapi-pod -- ls /var/run/secrets/kubernetes.io/serviceaccountYou should receive an error stating that the path does not exist. This is intentional: the pod cannot accidentally use the Kubernetes API.
Use the correct ServiceAccount in your Deployment
Always explicitly reference the correct ServiceAccount in a Deployment or StatefulSet:
spec:
serviceAccountName: app-api
automountServiceAccountToken: trueFor an application that does not need API access, use:
spec:
serviceAccountName: app-noapi
automountServiceAccountToken: falseThis keeps the behaviour of your workload predictable, even if someone later changes the namespace's `default` ServiceAccount.
Separate ServiceAccounts for each application make permissions, token mounts, and auditing much clearer. Always combine this with RBAC so that a ServiceAccount cannot do more than a workload actually requires.