Kubernetes Service Accounts Should Be Boring. Most Teams Make Them a Risk.
A service account is a machine user. When your pod or application needs to make API requests to the Kubernetes API server, it needs a service account with the appropriate Role or ClusterRole bound to it.
Every service account gets a token automatically mounted into the pod at:
/var/run/secrets/kubernetes.io/serviceaccount/tokenThat token is a valid credential. It proves to the Kubernetes API server that the pod is who it says it is.
Where the risk hides
In many clusters, the default service account is automatically attached to every new pod. This is where risk builds quietly.
Even if the default service account has no RBAC permissions today, the token is still an identity. And identities can be escalated.
Someone adds a ClusterRoleBinding to the default service account "just temporarily" during debugging and forgets to remove it. Now every pod in that namespace suddenly has elevated permissions, because they all share that service account and all have the token mounted.
Silent escalation across your entire namespace.
The fix
For pods that don't need Kubernetes API access, set automountServiceAccountToken: false in the pod spec.
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
automountServiceAccountToken: false
containers:
- name: app
image: my-app:latestNo token mounted. No attack surface.
Better: set it at the service account level
Per-pod works, but it's easy to miss when someone deploys a new workload. Set it on the service account itself instead. Any pod using that service account won't get a token mounted automatically.
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: my-namespace
automountServiceAccountToken: falseCleaner and harder to miss.
Audit your cluster today
Find every pod that has a service account token mounted but probably doesn't need it:
kubectl get pods --all-namespaces -o json | jq -r '
.items[]
| select(.spec.automountServiceAccountToken != false)
| "\(.metadata.namespace)/\(.metadata.name) → SA: \(.spec.serviceAccountName // "default")"
'If the list is longer than "things that actually call the Kubernetes API," you have work to do.
The principle that applies
Never forget the principle of least privilege. It applies to machines too, not just humans.
Have you audited which pods in your cluster have tokens mounted that they don't actually need?
Originally shared on LinkedIn.