43.2 Disabling the Default Service Account Token Automount
Right, let’s talk about one of Kubernetes’ more “helpful” defaults that is, frankly, a bit of a security nightmare. When you create a Pod, and you haven’t explicitly said which ServiceAccount it should use, Kubernetes, like a overly accommodating butler, says “Not to worry, sir/madam, I shall assign the default ServiceAccount from this namespace.” And then, without asking, it also automatically mounts that ServiceAccount’s API token into the Pod at /var/run/secrets/kubernetes.io/serviceaccount/token.
This is the automountServiceAccountToken: true default behavior. It’s convenient for quickly getting a Pod talking to the API server. It’s also horrifyingly broad in its permissions if you’ve never looked at what the default ServiceAccount can actually do. In many distributions, it has no permissions by default, which is good. But in others, or in clusters where RBAC was set up hastily, it might have more access than you’d like. The real problem is the principle of the thing: you’re handing out tokens without explicit consent. We need to break up with this default.
Why You Should Care
Think of that auto-mounted token as a master key. Any process inside your Pod, from your main application to a forgotten debugging script you kubectl exec’d in there six months ago, can potentially use that token to talk to the Kubernetes API. If that Pod gets compromised through a vulnerability, the first thing an attacker will do is cat that token and see what doors it opens. If your default ServiceAccount has any permissions at all—like being able to list Secrets in its namespace or, heaven forbid, create Pods—you’ve just handed an attacker a fantastic pivot point into your entire cluster. Our goal is to reduce the attack surface. No Pod gets a token unless it explicitly needs one.
How to Check If a Pod Is Automounting
Before you go disabling things, it’s wise to see the lay of the land. This is how you check if a specific pod has the token mounted.
# Get a high-fidelity view of a specific pod
kubectl get pod <pod-name> -o yaml | grep -A 5 -B 5 serviceaccount
# Or, to see it from inside the pod if you have shell access
kubectl exec -it <pod-name> -- ls /var/run/secrets/kubernetes.io/serviceaccount
# If you see 'token', 'ca.crt', and 'namespace', the automount is active.
The Nuclear Option: Disabling It at the ServiceAccount Level
The most blanket approach is to tell a specific ServiceAccount to never automount its token. This is a great policy for the default ServiceAccount itself. Pods using this account will not get a token unless they explicitly override and ask for one.
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: your-namespace
automountServiceAccountToken: false
Here’s the catch, and it’s a big one: this only works for new Pods. Existing Pods won’t be affected. You have to recreate them. Also, this setting is an instruction from the ServiceAccount; a Pod can still override it and say automountServiceAccountToken: true, which brings us to the more precise method.
The Surgical Strike: Disabling It at the Pod Level
This is my preferred method. You leave the global default alone (because maybe you have some legacy stuff that still needs it) and you explicitly opt-out on a per-Pod basis. This is the mark of a carefully crafted manifest. You declare your intentions precisely.
apiVersion: v1
kind: Pod
metadata:
name: my-security-conscious-pod
spec:
containers:
- name: app
image: nginx:latest
automountServiceAccountToken: false # <- This is the important line
# serviceAccountName: something-else # You can still assign a specific SA, it just won't get the token.
When you set this, the Pod gets created but the secret volume containing the token is never mounted. It’s the most explicit and secure way to handle it.
The Cluster-Wide Default: For the Truly Committed
If you’re building a new cluster or you’re feeling particularly brave (and have good change management), you can tell the API server to change this default behavior for the entire cluster. This is done by setting --automount-service-account-token=false on the kube-apiserver command line.
Warning: This is a sledgehammer. It will break every Pod that relies on this automount behavior unless you’ve explicitly configured it otherwise. This includes system Pods in kube-system! Do not do this on a running cluster without extensive testing. This is often a build-time option for secure Kubernetes distributions, not a casual runtime flag you flip on a Friday afternoon.
The Best Practice: A Service Account per Workload
The ultimate goal isn’t just to disable the default; it’s to use ServiceAccounts correctly. The default ServiceAccount is a crutch. You should create a dedicated ServiceAccount for each distinct application or workload.
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-account
namespace: my-app
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: my-app
name: my-app-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"] # Least privilege in action!
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-app-rolebinding
namespace: my-app
subjects:
- kind: ServiceAccount
name: my-app-account
roleRef:
kind: Role
name: my-app-role
apiGroup: rbac.authorization.k8s.io
Then, and this is the key, you explicitly assign that tightly scoped ServiceAccount to your Pod and you let it automount the token. This is the correct time to use automounting.
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
spec:
serviceAccountName: my-app-account # Explicitly use our custom SA
# automountServiceAccountToken is now optional. It defaults to true, which is what we WANT here.
containers:
- name: app
image: my-app:latest
So, to summarize: stop letting Kubernetes be so generous with the default account’s credentials. Default to disabling the automount, either globally or per-Pod, and then be intentional about creating dedicated ServiceAccounts with precisely scoped permissions when a Pod genuinely needs to talk to the API. It’s a few more lines of YAML, and it closes a gaping hole. Worth it.