Skip to main content
RunBook Academy

KubernetesLX · ServiceAccountsServiceAccounts

Default bindings and automountServiceAccountToken

Advanced⏱ ~13 minkubectl

What you'll learn

  • Explain what the `default` ServiceAccount is and why it is the most dangerous SA in production
  • Configure `automountServiceAccountToken: false` to prevent token mounting on Pods that do not need it
  • Identify the production patterns for minimum-surface default SAs
  • Recognise the failure modes of over-broad defaults and unmounted tokens

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

The default ServiceAccount in every namespace is the most exposed SA in production. Every Pod that does not specify serviceAccountName uses it; its token is auto-mounted into every such Pod. A default SA with broad RBAC, or with automountServiceAccountToken: true and no use case for the API, is a Critical finding. This lesson covers the defaults, the auto-mount flag, and the production patterns.

The default SA

Every namespace has a default SA, auto-created by the namespace controller when the namespace is created:

kubectl get sa -A | grep default
# NAMESPACE    NAME      SECRETS   AGE
# default      default   0         30d
# kube-public  default   0         30d
# prod         default   0         30d

The SA’s metadata is minimal:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: prod

No RBAC bindings, no image pull secrets, no labels. A Pod without spec.serviceAccountName uses this SA.

automountServiceAccountToken

The automountServiceAccountToken flag controls whether the kubelet mounts the projected token volume:

# On the SA (applies to every Pod using the SA)
apiVersion: v1
kind: ServiceAccount
metadata:
  name: api-sa
  namespace: prod
automountServiceAccountToken: false  # no token mounted
# On the Pod (overrides the SA's flag for this Pod)
apiVersion: v1
kind: Pod
metadata:
  name: api
spec:
  serviceAccountName: api-sa
  automountServiceAccountToken: false

When the flag is false:

  • The kubelet does not call TokenRequest for the SA.
  • The projected token volume is not added to the Pod spec.
  • /var/run/secrets/kubernetes.io/serviceaccount/token does not exist.
  • The workload cannot authenticate to the API server using the SA’s token (because there is none).

A workload that does not call the API server should have this flag set to false. The flag is the single highest-leverage SA hardening control.

flowchart LR
    A[Pod spec.automountServiceAccountToken] --> B{false?}
    B -->|yes| C[No token mounted]
    B -->|no| D[SA's flag]
    D --> E{false?}
    E -->|yes| C
    E -->|no| F[Token mounted]

Production patterns

Three patterns:

  1. Default SA with no RBAC and no automount. The default SA in every namespace is bound to nothing, and automountServiceAccountToken is set to false.
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: prod
automountServiceAccountToken: false
  1. Every workload specifies its own SA. No Pod relies on default. The workload’s SA has the minimum RBAC it needs.
apiVersion: v1
kind: Pod
metadata:
  name: api
spec:
  serviceAccountName: api-sa
  containers:
  - name: api
    image: myapp:v1.0
  1. Disable token automount on workloads that don’t call the API. A logging sidecar, a metrics exporter, a pure network service — none of these need the API.
spec:
  serviceAccountName: log-shipper-sa
  automountServiceAccountToken: false

Verifying the defaults

# Check the default SA in every namespace
for ns in $(kubectl get ns -o name | cut -d/ -f2); do
  echo "=== $ns ==="
  kubectl get sa default -n "$ns" -o jsonpath='{.automountServiceAccountToken}'
  echo
done

# Check what the default SA can do
for ns in $(kubectl get ns -o name | cut -d/ -f2); do
  echo "=== $ns ==="
  kubectl auth can-i --list -n "$ns" \
    --as=system:serviceaccount:$ns:default
done

A default SA with any non-empty --list output is a finding.

Disabling the default SA

A team that wants to forbid the default SA entirely can:

  1. Set automountServiceAccountToken: false on the default SA in every namespace.
  2. Use a ValidatingAdmissionPolicy to require spec.serviceAccountName on every Pod.
  3. Bind the default SA to nothing.
# ValidatingAdmissionPolicy
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: require-sa
spec:
  failurePolicy: Fail
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: [""]
    apiVersions: ["v1"]
    resources: ["pods"]
  validations:
  - expression: "object.spec.serviceAccountName != '' && object.spec.serviceAccountName != 'default'"
    message: "Pods must specify a serviceAccountName other than 'default'"

The policy rejects any Pod that does not specify a SA or that uses default. Every Pod has an explicit SA.

Production failure modes

  1. Default SA bound to edit or view. Every Pod in the namespace has read or write access to every object. The fix is to remove the binding.
  2. Default SA with automountServiceAccountToken: true. Every Pod has a token mounted even when not needed. The fix is to set it to false.
  3. Workload needs the API but uses default. A workload was deployed without a SA and uses default. If default has no RBAC, the workload fails. The fix is to specify the workload’s own SA with the minimum RBAC.
  4. ValidatingAdmissionPolicy too strict. A policy that rejects every Pod without a SA breaks workloads that legitimately use default. The fix is to scope the policy to non-kube-system namespaces.

Cross-course references

  • The Observability course covers the audit log entries for default SA usage.
  • The Linux course covers the file permissions for the token mount path.

Quiz

Knowledge check · 4 questions

  1. Q1. What is the operational effect of setting `automountServiceAccountToken: false` on a ServiceAccount?

  2. Q2. If the `default` ServiceAccount in a namespace has an RBAC binding to a Role, every Pod in the namespace that does not specify a `serviceAccountName` inherits the binding.

  3. Q3. Your workload in `prod` calls the API server to read a ConfigMap. You set `automountServiceAccountToken: false` on the SA. The workload reports `Unauthorized: no token`. What went wrong, and how do you fix it?

    The SA `api-sa` has `automountServiceAccountToken: false`. The Pod uses `api-sa`. The workload tries to read a ConfigMap from the API server. The token file at `/var/run/secrets/kubernetes.io/serviceaccount/` is empty. The workload has no token.

  4. Q4. Name three production patterns for hardening the `default` ServiceAccount in a namespace.

Passing score: 75%. Answers are checked in this browser.

Production discipline

The default ServiceAccount is the highest-leverage target for SA hardening. Bind it to nothing, set automountServiceAccountToken: false, and require every Pod to specify a SA. A cluster whose default SA is inert and whose workloads use minimum-surface SAs has a workload identity programme that is defensible. A cluster whose default SA has broad RBAC and automountServiceAccountToken: true has a workload identity programme that is not — the audit log will surface the over-broad bindings, the ValidatingAdmissionPolicy will catch new violations, and the discipline is to enforce the policy at every admission.