CKSKubernetes 1.35

Lessons

Service accounts: stop mounting tokens nobody uses

updated 2026-10-22 by seed

Why this is a guaranteed task

Every Pod gets a service account token mounted at
/var/run/secrets/kubernetes.io/serviceaccount/token unless you say otherwise.
If the SA can do something interesting, anybody who pops the container owns that
permission — and the token never expires unless it is projected with an expiry.

Switch it off in both places

apiVersion: v1
kind: ServiceAccount
metadata: {name: api, namespace: prod}
automountServiceAccountToken: false      # all Pods using this SA
---
apiVersion: v1
kind: Pod
metadata: {name: client, namespace: prod}
spec:
  serviceAccountName: api
  automountServiceAccountToken: false    # this Pod, whatever the SA says

The Pod field wins over the SA field. Set both when the task says "must not be
mounted" — the marker may check either object.

serviceAccountName is immutable. If you have to change it on a running
Deployment, the Pod template change triggers a rollout; on a bare Pod you delete
and recreate.

Verify

kubectl exec -n prod client -- ls /var/run/secrets/kubernetes.io/serviceaccount/
# ls: cannot access ...: No such file or directory

kubectl get sa api -n prod -o jsonpath='{.automountServiceAccountToken}'
kubectl get pod client -n prod -o jsonpath='{.spec.automountServiceAccountToken}'

And from the Pod's identity:

kubectl auth can-i --list --as=system:serviceaccount:prod:api

Projected tokens: audience and expiry

The default token is a Secret that lives as long as the SA. A projected token
is issued per Pod, bound to a workload, expires, and can carry an audience:

spec:
  serviceAccountName: api
  volumes:
  - name: token
    projected:
      defaultMode: 0400
      sources:
      - serviceAccountToken:
          path: token
          expirationSeconds: 3600
          audience: vault
  containers:
  - name: app
    image: registry.example.com/app:1.0
    volumeMounts:
    - {name: token, mountPath: /var/run/secrets/tokens, readOnly: true}

kubectl create token api -n prod --duration=1h --audience=vault produces a
short-lived token from the CLI — useful when the task wants a token without
mounting anything.

The cluster-admin binding that must go

kubectl get clusterrolebindings -o wide | grep -i cluster-admin
kubectl get clusterrolebinding bad-binding -o yaml     # find the subject
kubectl delete clusterrolebinding bad-binding
kubectl auth can-i --list --as=system:serviceaccount:prod:api   # now minimal

Also check for system:serviceaccounts or system:authenticated as subjects —
those grant to every service account in the cluster.

Default SA in a namespace

apiVersion: v1
kind: ServiceAccount
metadata: {name: default, namespace: prod}
automountServiceAccountToken: false

Doing that once per namespace is the cheapest cluster-wide win, and it is a
common "harden this namespace" sub-task.