Skip to content
Service accounts

Service accounts

Users authenticate with a kubeconfig. Workloads authenticate with a ServiceAccount — a namespaced identity that a Pod runs as, and that RBAC rules are written against.

Every namespace has one called default, and every Pod that does not name another one runs as it. Most Pods never talk to the API server at all, and yet they all used to receive a token to do so. Understanding that is the point of this page.

Reference

Terminology

  • ServiceAccount — An identity for a process. Namespaced, created like any other object, and referenced from a Pod with spec.serviceAccountName.
  • Bound token — A token issued for one ServiceAccount, with an audience and an expiry, projected into the Pod and rotated automatically. This is the modern mechanism; the old approach of a token stored in a Secret no longer happens by default.
  • Projected volume — The mount at /var/run/secrets/kubernetes.io/serviceaccount/ containing the token, the namespace, the CA certificate and the audience.
  • automountServiceAccountToken — Set to false to stop the token being mounted at all. Can be set on the ServiceAccount or on the Pod.
  • imagePullSecrets — Registry credentials attached to a ServiceAccount apply to every Pod that uses it, which is often neater than repeating them per Pod.

Where the Token Is

Inside any Pod that has one:

/var/run/secrets/kubernetes.io/serviceaccount/
├── token        # the bearer token, rotated periodically
├── ca.crt       # the API server's CA, to verify it
└── namespace    # the Pod's namespace, as a file

The kubectl and client libraries find these automatically, which is why a Pod that uses an in-cluster client “just works” with no configuration — and why a leaked Pod can sometimes talk to the API server as its workload.

Exercises

  1. Create a ServiceAccount and run a Pod as it.

    kubectl create serviceaccount app
    kubectl run client --image=docker.io/library/busybox --restart=Never \
      --overrides='{"spec":{"serviceAccountName":"app"}}' -- sleep 3600
  2. Use the token from inside the Pod.

    kubectl exec client -- sh -c '
      TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
      wget -qO- --header="Authorization: Bearer $TOKEN" \
        https://kubernetes.default.svc/api/v1/namespaces/default/pods
    '

    Expect a 403 Forbidden. The identity is real and the token is valid; it simply has no permissions. Authentication is not authorisation — the next page is the authorisation half.

  3. Mint a short-lived token from the command line. Useful for debugging, and worth knowing because these tokens have an expiry:

    kubectl create token app --duration=10m
    kubectl create token app --duration=10m | cut -d. -f2 | base64 -d 2>/dev/null | head -c 200

    The decoded payload carries aud, exp and the ServiceAccount’s name. Note that the maximum duration is capped by the cluster — some managed clusters allow no more than 24 hours — so a token baked into a kubeconfig is not permanent.

  4. Turn the token off. Set automountServiceAccountToken: false on a Pod and confirm the directory is empty or absent. If the workload never calls the API server, this is free security: there is no token to steal.

Gotchas Worth Knowing

  • Every Pod used to get a token whether it needed one or not. Turning it off is one line and removes a credential from your attack surface.
  • Tokens expire now. Anything that copies a token to a file and expects it to work next week will stop working, and the error is a plain 401.
  • default has no permissions in a clean cluster — but some platforms bind additional roles to it, so check rather than assume: kubectl auth can-i --list --as=system:serviceaccount:default:default.
  • A ServiceAccount is namespaced and cannot be shared across namespaces. The full subject name is system:serviceaccount:<namespace>:<name>, which is exactly what you will write in the RBAC objects on the next page.
  • imagePullSecrets on the ServiceAccount is the standard way to give every workload in a namespace access to a private registry.

What to Take Away

  • A ServiceAccount is the identity of a process; default is what a Pod gets if it says nothing.
  • The token is projected into the Pod, bound to that ServiceAccount, and expires.
  • Mount it only if the workload needs it.
  • Authentication is not authorisation — the token proves who you are, RBAC decides what you may do.