skip to content

Most application Pods never call the Kubernetes API, yet by default each one carries a credential for it. How do you stop that credential from being mounted, which two places accept the setting, and which one wins when they disagree?

level: middleimportance: should knowfreq 45%

answer

  1. automountServiceAccountToken: false
  2. two places: ServiceAccount object and Pod spec
  3. Pod spec wins over ServiceAccount
  4. patch default SA per namespace = fail closed
  5. not covered by Pod Security Standards; needs admission policy

basics

~20 s

Set automountServiceAccountToken: false. It can go on the ServiceAccount object, which applies to every Pod using it, or in the Pod spec, which applies to that Pod. The Pod spec wins when both are set. Without a mounted token, a compromised container has no API credential to steal.

solid answer

~60 s

`automountServiceAccountToken: false` in two places: - On the **ServiceAccount** object, where it becomes the default for every Pod that uses that account. Hardening baselines set it on the `default` ServiceAccount in every namespace so that forgetting to name one fails closed. - In the **Pod spec** (`spec.automountServiceAccountToken`), which applies to that Pod only. When both are present the **Pod spec wins**, in either direction: a Pod can opt back in even if its ServiceAccount opts out, which is how you keep a namespace default of `false` while letting one controller mount a token. Why it matters: the projected token is the credential an attacker looks for first after getting code execution in a container, and it is the pivot from "one compromised process" to "an authenticated principal talking to the API server". Removing it also removes reconnaissance value, since even a permissionless token lets an attacker enumerate the API surface and confirm they are inside a cluster. A workload that only serves HTTP and talks to a database has nothing to lose by dropping it.

code

yaml · 20 lines
yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: payments
automountServiceAccountToken: false     # applies to every Pod using it
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reconciler
  namespace: payments
spec:
  template:
    spec:
      serviceAccountName: reconciler
      automountServiceAccountToken: true  # Pod spec overrides the ServiceAccount
      containers:
        - name: app
          image: reconciler:2.1

go deeper

for a junior

Know the field name and that most application Pods do not need an API token, so turning the mount off is safe.

for a middle

Give both locations, the Pod-spec-wins precedence, and what an attacker does with a mounted token after compromising a container.

for a senior

Make it a fleet default: patch the default ServiceAccount per namespace, enforce with admission policy since Pod Security Standards do not cover it, and use explicit projected volumes with an audience for the exceptions.

for a principal

Treat it as blast-radius policy: no workload holds a credential it does not use, exceptions are declared and reviewed, and the enforcement lives in admission rather than in per-team convention.

## The default and why it is wrong for most Pods Unless told otherwise, the kubelet mounts a projected ServiceAccount token into every container of every Pod at `/var/run/secrets/kubernetes.io/serviceaccount`. That default exists because in-cluster clients need zero configuration, but the overwhelming majority of workloads (web services, batch jobs, databases, queue consumers) never call the Kubernetes API at all. For those, the mount is pure downside: a bearer credential sitting in the filesystem of a process that has no use for it. What an attacker with code execution in the container does with it: - Confirms they are inside Kubernetes and learns the namespace from the `namespace` file. - Calls the API server (reachable at `kubernetes.default.svc` unless NetworkPolicy says otherwise) and enumerates what the identity can do with self-subject access reviews. Even with no permissions, discovery endpoints reveal the API surface, versions, and installed CRDs, which maps the environment. - Uses whatever the ServiceAccount was granted. Since permissions accumulate on shared accounts, this is frequently more than the compromised workload needed. - Exfiltrates the token for use elsewhere, though bound tokens limit this to the Pod's lifetime. Removing the mount cuts that entire branch. It is one of the cheapest hardening steps available, because for most workloads nothing breaks. ## The two places, and precedence `automountServiceAccountToken: false` is a top-level field on both the `ServiceAccount` object and the Pod spec. Set on the **ServiceAccount**, it becomes the default for all Pods referencing that account. This is the scalable form: create one ServiceAccount per workload with automounting off, or patch the `default` ServiceAccount in every namespace so any Pod that forgets to name an account also gets no token. Set in the **Pod spec**, it applies to that Pod only, and it takes precedence over the ServiceAccount setting in both directions. So a namespace can default to `false` while a specific controller sets `true` in its Pod template and still gets a token. That asymmetry is what makes the fail-closed default practical. Note what the setting does *not* do. It does not remove the identity: the Pod still runs as its ServiceAccount, RBAC bindings still apply to that subject, and `imagePullSecrets` from the ServiceAccount are still used. It only stops the automatic volume from being added. ## Getting a token anyway, deliberately A workload that needs a token for something other than the Kubernetes API (federating to cloud IAM, authenticating to Vault) should keep automounting off and declare an explicit `projected` volume with a `serviceAccountToken` source, choosing the audience and expiry. That is strictly better than the automount: the token is scoped to the intended recipient, and its lifetime is chosen rather than inherited. ## Enforcing it Because this is a Pod-spec field, it is a natural target for admission policy. Gatekeeper, Kyverno, or a Validating Admission Policy can reject Pods that mount a token without an exemption label, or mutate the field to `false` by default. Doing it in admission is what makes the control stick across teams, since a per-manifest convention drifts. The audit shape is easy to check: list Pods whose `spec.automountServiceAccountToken` is not `false` and whose ServiceAccount also does not disable it. Pod Security Standards do not cover this field, so it needs its own policy; that is worth saying explicitly, since candidates often assume the `restricted` profile handles it. ## What to say Name the field, both locations, and the Pod-spec-wins precedence. Justify it by what the token buys an attacker after container compromise (API reachability, enumeration, accumulated permissions on a shared account), note that the identity itself is unaffected, and mention explicit projected volumes for workloads that need a token for a non-Kubernetes audience.

  • A workload does not call the Kubernetes API but does need a ServiceAccount token to authenticate to Vault. What is the right configuration?
    Keep `automountServiceAccountToken: false` and declare an explicit `projected` volume with a `serviceAccountToken` source, setting `audience: vault` and an `expirationSeconds` you choose. The result is a token Vault accepts and the Kubernetes API server rejects, with a lifetime you control, which is strictly tighter than the automatic mount.
  • Does disabling automounting change what the Pod is allowed to do in RBAC?
    No. The Pod still runs as its ServiceAccount and any RoleBindings naming that subject still apply; the identity is unchanged. All that changes is that no credential is placed in the container's filesystem, so nothing in the Pod can present that identity to the API server. It also does not affect imagePullSecrets attached to the ServiceAccount.

saying these in an interview costs you the question

  • Believing the ServiceAccount setting overrides the Pod spec (it is the other way round)
  • Thinking disabling the mount removes the Pod's RBAC permissions or its identity
  • Assuming the restricted Pod Security Standard already blocks token automounting
  • Leaving automounting on because 'the ServiceAccount has no permissions', ignoring reconnaissance value and later permission drift

context