Skip to main content
RunBook Academy

KubernetesLXXII · Controller ManagerController manager

ServiceAccount and Token controllers — identity for Pods

Advanced⏱ ~17 minkubectl

What you'll learn

  • Describe the ServiceAccount controller's role
  • Trace a Pod's token from spec to mount
  • Identify the bootstrap token controller
  • Reason about identity hygiene and rotation

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 ServiceAccount controller and Token controller together handle identity for every Pod in the cluster. Modern (1.24+) clusters use projected, time-limited tokens that are signed at Pod admission and bound to the Pod’s identity. This lesson walks the controllers, the token model, and the production discipline of identity.

The ServiceAccount controller

The ServiceAccount controller ensures that each ServiceAccount object exists:

flowchart LR
    NS[Namespace created] -->|default SA| SAC[SA controller]
    NS -->|any SA referenced| SAC
    SAC -->|creates SA object| API
    API -->|watched by| AC[Admission controller]
    AC -->|sees missing SA| AC2[Rejects Pod]

The controller:

  • On namespace creation, ensures a default SA exists in the namespace.
  • When a Pod references an SA (e.g., serviceAccountName: prod-web), the controller ensures the SA exists.

The legacy tokens

In pre-1.24 clusters, each ServiceAccount had a Secret with a token, used by Pods that mounted the Secret. The token never expired; it was long-lived.

In 1.24+, the legacy token is no longer auto-created. The SA controller still creates a SA object, but the Secret is not. The token controller is replaced by projected, time-limited tokens that are signed at Pod admission.

The projected tokens

The Token controller (modern) is actually part of the API server’s admission chain, not the controller manager strictly speaking. The flow:

sequenceDiagram
    autonumber
    participant U as User
    participant API as API server
    participant AS as admission webhook
    participant K as kubelet
    U->>API: create Pod
    API->>AS: TokenRequest (signs a token for the Pod)
    AS-->>API: token (1h TTL, audience, claim)
    API->>API: persist Pod
    API-->>U: 201 Created
    K->>API: watch Pod
    API-->>K: Pod spec with projected token mount
    K->>K: mount token at /var/run/secrets/kubernetes.io/serviceaccount/

The token is:

  • Signed with a current signing key.
  • Time-limited (default 1h, configurable).
  • Audience-specific.
  • Bound to the Pod’s identity.

The kubelet rotates the token before expiry.

The bootstrap token controller

The bootstrap token controller is part of the controller-manager. It governs kube-system-scoped tokens used by kubeadm join:

$ kubectl get secrets -n kube-system | grep bootstrap-token
bootstrap-token-abcdef   kubernetes.io/bootstrap-token   6      13d
bootstrap-token-...

The controller:

  • Creates the token’s Secret object on kubeadm token create.
  • Expires the token after a TTL (default 24h).
  • Ensures the bootstrap token is removed at expiry.

The controller is part of the controller-manager binary.

The SA token in the Pod

When a Pod mounts its SA token, the directory is:

/var/run/secrets/kubernetes.io/serviceaccount/
├── token        # the projected token (time-limited)
├── ca.crt       # cluster CA bundle
└── namespace    # the namespace as text

The Pod’s application reads token for outbound API calls; the kubelet rotates the file before expiry.

# View the projected token (in the Pod)
cat /var/run/secrets/kubernetes.io/serviceaccount/token

The token is opaque to the application; it is verified by the API server’s TokenReview endpoint (which checks the signing key, expiry, audience, and claim).

The TokenRequest API

The TokenRequest API is the mechanism by which the API server issues tokens for Pods:

POST /apis/authentication.k8s.io/v1/tokenreviews
{
  "kind": "TokenReview",
  "spec": {
    "token": "...",
    "audiences": ["https://kubernetes.default.svc.cluster.local"]
  }
}

The controller-manager’s RBAC ensures the controller is permitted to call TokenRequest for SAs in the cluster.

Token rotation

Modern Kubernetes (1.24+) rotates the projected token:

  • Projected token has a TTL (default 1h, configurable).
  • The kubelet refreshes the token before TTL.
  • Applications reading token get the latest value on read.

The application should re-read the token file periodically, not cache it.

The SA namespace annotation

A namespace can set a default SA via annotation:

kubectl annotate namespace prod \
  kubernetes.io/service-account.name=prod-web

Pods in prod without serviceAccountName use prod-web instead of default. The annotation is respected by the API server at admission.

Read-only / Safe
$ kubectl get sa -n prod -o yaml | grep 'name:' | head
...

The failure modes

FailureSymptomRecovery
SA controller downServiceAccounts not auto-createdRestart controller-manager
Projected token not rotatedPod application sees token rejectedRestart kubelet; check TokenRequest
Bootstrap token expiredNodes cannot joinRe-issue with kubeadm token create
Token mounted on default SADefault SA does not need a tokenDisable automounting

The SA admission controller

The API server’s ServiceAccount admission controller:

  • Ensures every Pod has a SA assigned (default if not specified).
  • Signs the projected token at Pod admission.
  • Adds the automountServiceAccountToken flag handling.

The admission chain is part of the API server, not the controller-manager.

Audit logging

The controller-manager’s actions on SA / tokens are in the controller’s logs. Projected token issuance is logged with the actor (the API server at admission).

For compliance, the audit log records:

  • SA creation.
  • Token issuance (sometimes; depends on audit policy).
  • Pod deletion (which does not invalidate the token but makes it useless).

The discipline of identity

  • Use projected tokens, not legacy. Migrate any workload from long-lived to projected tokens.
  • Disable default SA token automounting. Set automountServiceAccountToken: false on the default SA in production namespaces.
  • Audit SA / token usage. Track which SAs are referenced; rotate when the workload changes.
  • Tighten SA RBAC. SAs with overly broad RBAC are a privilege-escalation risk.

Quiz

Knowledge check · 4 questions

  1. Q1. What is the default TTL of a projected Service Account token in Kubernetes 1.34?

  2. Q2. In production, the default Service Account's automounted token should be left enabled so Pods without explicit SAs still have one.

  3. Q3. A Pod's application is failing with 401 Unauthorized after 2 hours. The token in /var/run/secrets/... appears stale. Diagnose.

    Pod deployed 3 hours ago. The application cached the token file. Now the API server rejects the cached token.

  4. Q4. Why does Kubernetes use projected, time-limited tokens instead of long-lived secrets for Pod identity?

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

Production discipline

  • Projected tokens over legacy. Modern clusters should not have legacy SA tokens.
  • Disable default SA automounting. Production namespaces should not auto-mount the default SA.
  • Use shortest TTL. Lower TTLs reduce leakage impact; default 1h is a balance.
  • Document token lifecycle. Runbook should cover token rotation, expiry, and recovery.
  • Audit SA usage. Track which SAs are referenced in workloads; rotate when changed.

Identity in Kubernetes is the foundation that RBAC and admission build on. Operating it well is operating the cluster’s secure posture.