Home › CKS › Topics › Practice tasks › ServiceAccount Token Security
CKS · Minimize Microservice Vulnerabilities

Disable ServiceAccount Token Automounting

First, modify the existing ServiceAccount stats-monitor-sa in the namespace monitoring to turn off automounting of API credentials.

Check live lab access → All CKS tasks

Live lab access

This task description is public. Live practice requires full access unless this is the selected free weekly task. AI Lab Coach is available in single-task labs, including the free weekly task. See this week's free task.

Create an account to continue →

Read this week's free task →

On a phone? Read the task and create your account here, then sign in on a desktop or laptop for the terminal lab. Nothing starts until you choose Start.

The task

Context

A security audit has identified a Deployment improperly handling service account tokens, which could lead to security vulnerabilities.

Task

First, modify the existing ServiceAccount stats-monitor-sa in the namespace monitoring to turn off automounting of API credentials.

Next, modify the existing Deployment stats-monitor in the namespace monitoring to inject a ServiceAccount token mounted at /var/run/secrets/kubernetes.io/serviceaccount/token.

Use a Projected Volume named token to inject the ServiceAccount token and ensure that it is mounted read-only.

Original Prepium guide · Written for Kubernetes v1.35 and checked against the official documentation on 6 October 2026. Use a disposable practice cluster.

Stop automatic token mounts, then hand out a scoped, short-lived token on purpose

By default every Pod gets a ServiceAccount token, a CA bundle and its namespace name in a projected volume at /var/run/secrets/kubernetes.io/serviceaccount. If the application never talks to the API, that token is only something an attacker can steal. CKS hardening therefore has two halves: turn the automatic mount off, and where a token is genuinely needed, project one explicitly with a narrow audience, a short lifetime and a read-only mount.

ServiceAccount.automountServiceAccountToken ─┐
Pod.spec.automountServiceAccountToken ───────┴─→ Pod value wins if both are set
                                                  │ true (or unset)
                                                  ▼
                       admission adds kube-api-access-<suffix> (token, ca.crt, namespace)

explicit projected volume ─→ serviceAccountToken {path, audience, expirationSeconds}
                           ─→ kubelet fetches and rotates it ─→ your mountPath (readOnly)

1. Look at the default first

kubectl create namespace prepium-tutorial
kubectl -n prepium-tutorial create serviceaccount metrics-shipper
kubectl -n prepium-tutorial run before --image=busybox:1.36 \
  --overrides='{"apiVersion":"v1","spec":{"serviceAccountName":"metrics-shipper"}}' -- sleep 3600
kubectl -n prepium-tutorial describe pod before

Under Mounts you see the service account path coming from a volume named kube-api-access- plus a random suffix, marked (ro). Under Volumes it is listed as a projected volume with a token expiration of 3607 seconds, the kube-root-ca.crt ConfigMap and the downward API.

2. Turn automounting off on the ServiceAccount

kubectl -n prepium-tutorial patch serviceaccount metrics-shipper \
  -p '{"automountServiceAccountToken": false}'
kubectl -n prepium-tutorial get pod before -o jsonpath='{.spec.volumes[*].name}'

The existing Pod still lists its kube-api-access volume. The ServiceAccount admission controller only acts when a Pod is created, so changes reach new Pods only; for a Deployment that means a rollout. Remember precedence: if a Pod spec sets automountServiceAccountToken itself, that value beats the ServiceAccount's. A Pod with true still gets the token even though the account says false, so check both places during an audit.

3. Project a token you actually control

Save this as cks-automount-pod.yaml and apply it.

apiVersion: v1
kind: Pod
metadata:
  name: shipper
  namespace: prepium-tutorial
spec:
  serviceAccountName: metrics-shipper   # the account has automount turned off
  containers:
    - name: shipper
      image: busybox:1.36
      command: ["sh", "-c", "sleep 3600"]
      volumeMounts:
        - name: gateway-cred
          mountPath: /var/run/secrets/gateway
          readOnly: true
  volumes:
    - name: gateway-cred
      projected:
        sources:
          - serviceAccountToken:
              path: gateway-token          # file name inside the mount
              audience: metrics-gateway    # who should accept this token
              expirationSeconds: 1200      # 20 minutes; the minimum is 600

Each field narrows the risk. audience marks who the token is for; a recipient is expected to reject tokens that do not name it (when omitted it defaults to the API server's identifier). expirationSeconds defaults to one hour and cannot go below 600. The kubelet requests the token through the TokenRequest API and rotates it once it is older than 80% of its lifetime or older than 24 hours, so the application must re-read the file rather than cache it. Avoid subPath for this mount, because subPath mounts of projected sources do not receive updates.

4. Verify from inside

kubectl apply -f cks-automount-pod.yaml
kubectl -n prepium-tutorial get pod shipper -o jsonpath='{.spec.volumes[*].name}'
kubectl -n prepium-tutorial exec shipper -- ls /var/run/secrets/gateway
kubectl -n prepium-tutorial exec shipper -- ls /var/run/secrets/kubernetes.io/serviceaccount
kubectl -n prepium-tutorial exec shipper -- grep /var/run/secrets/gateway /proc/mounts

The volume list shows only gateway-cred, with no kube-api-access entry. The second command lists gateway-token; the third fails because nothing was mounted at the default path. In /proc/mounts the option list for the gateway mount includes ro. The token is a JWT, so a standard JWT decoder run on your workstation shows its aud and exp claims if you want to confirm the audience and lifetime.

Common mistakes

Example checkEvidence
Automount disabledautomountServiceAccountToken: false on the ServiceAccount, and no Pod-level override to true
No default token volumeNew Pod has no kube-api-access-* volume
Explicit token projectedProjected volume with a serviceAccountToken source and the intended path
Mount is read-onlyreadOnly: true in the spec and ro in /proc/mounts

Cleanup: kubectl delete namespace prepium-tutorial. References: Configure Service Accounts for Pods, Projected Volumes, Managing Service Accounts. Next, try switching a workload's ServiceAccount or immutable containers.

Exam
CKS
Domain
Minimize Microservice Vulnerabilities
Grading
Programmatic · partial credit

What this tests

Apply Pod Security Standards, securityContext, and mTLS, and protect secrets at rest. On the CKS exam, Minimize Microservice Vulnerabilities tasks are graded purely on what you build in the cluster - not multiple choice - so the only way to get faster is to do them on a real cluster against a clock.

Practice it for real

The public description lets you study the task before signing up. Full access includes this live lab; the selected Task of the Week is free with an account. You solve the task in a real terminal, hit validate, and a programmatic checker scores exactly what you got right and wrong (with partial credit). You can open the solution while practising, then retry and use the failed checks to improve your score.

Related CKS tasks