Skip to main content
RunBook Academy

KubernetesLX · ServiceAccountsServiceAccounts

SA tokens in pod spec — explicit projection and audiences

Advanced⏱ ~13 minkubectl

What you'll learn

  • Configure a custom audience for the SA token in a Pod
  • Set a custom expiry shorter than the default 1 hour
  • Mount multiple tokens in one Pod for different audiences
  • Identify the production use cases (Vault, AWS STS, cloud workload identity)

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 projected token volume mounts a token with the API server’s audience and a 1-hour expiry. A workload that needs to authenticate to a different consumer (Vault, AWS STS, a custom API) requires a token with a different audience. This lesson covers the explicit projection pattern: custom audiences, custom expiries, multiple tokens in one Pod, and the production use cases.

The default projection

The SA admission controller adds a default projection:

volumes:
- name: kube-api-access
  projected:
    sources:
    - serviceAccountToken:
        path: token
        expirationSeconds: 3600
    - configMap:
        name: kube-root-ca.crt
        items:
        - key: ca.crt
          path: ca.crt
    - downwardAPI:
        items:
        - path: namespace
          fieldRef:
            fieldPath: metadata.namespace

The token’s audience is the API server’s (https://kubernetes.default.svc); the expiry is 1 hour; the path is /var/run/secrets/kubernetes.io/serviceaccount/token.

A workload that uses this token to authenticate to any other consumer (Vault, AWS, custom API) is rejected — the audience does not match.

Custom audience projection

A workload that needs to authenticate to a non-API consumer adds an explicit projection:

spec:
  serviceAccountName: api-sa
  automountServiceAccountToken: false  # disable the default
  volumes:
  - name: kube-api-access
    projected:
      sources:
      - serviceAccountToken:
          path: token
          audience: https://kubernetes.default.svc
          expirationSeconds: 3600
  - name: vault-token
    projected:
      sources:
      - serviceAccountToken:
          path: vault-token
          audience: vault://prod.example.com
          expirationSeconds: 600
  containers:
  - name: api
    volumeMounts:
    - name: kube-api-access
      mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      readOnly: true
    - name: vault-token
      mountPath: /etc/secrets/vault
      readOnly: true

This Pod has two tokens:

  • token at /var/run/secrets/kubernetes.io/serviceaccount/ for the API server (audience https://kubernetes.default.svc).
  • vault-token at /etc/secrets/vault/vault-token for Vault (audience vault://prod.example.com).

The API server rejects the Vault token; Vault rejects the API server token.

flowchart LR
    A[Pod] --> B[API server token]
    A --> C[Vault token]
    B --> D[aud: kubernetes]
    C --> E[aud: vault]
    D -->|matched| F[API server accepts]
    E -->|matched| G[Vault accepts]

Custom expiry

The default expiry is 1 hour (3600 seconds). A workload that needs a shorter rotation window (for tighter revocation) sets expirationSeconds explicitly:

- serviceAccountToken:
    path: short-lived-token
    audience: vault://prod.example.com
    expirationSeconds: 300  # 5 minutes

A 5-minute expiry means the token rotates every 4 minutes (at 80% of the lifetime). A revocation in the signing key invalidates the token within 5 minutes.

The bound-token cap is 1 hour 6 minutes; unbound tokens can have a longer duration (up to 24 hours, configurable per API server).

Multiple tokens in one Pod

A workload that authenticates to multiple consumers mounts multiple tokens:

volumes:
- name: kube-api-access
  projected:
    sources:
    - serviceAccountToken:
        path: token
        audience: https://kubernetes.default.svc
        expirationSeconds: 3600
- name: vault-token
  projected:
    sources:
    - serviceAccountToken:
        path: vault-token
        audience: vault://prod.example.com
        expirationSeconds: 600
- name: aws-token
  projected:
    sources:
    - serviceAccountToken:
        path: aws-token
        audience: sts.amazonaws.com
        expirationSeconds: 900

The workload reads each token from its mount path and presents it to the corresponding consumer. The audiences are explicit; no consumer accepts a token with the wrong audience.

Production use cases

  1. Vault. A workload reads secrets from Vault using a Kubernetes-authenticated token. The audience is vault://prod.example.com.
- serviceAccountToken:
    path: vault-token
    audience: vault://prod.example.com
    expirationSeconds: 600
  1. AWS STS (IRSA). A workload assumes an IAM role using a Kubernetes-authenticated token. The audience is sts.amazonaws.com.
- serviceAccountToken:
    path: aws-token
    audience: sts.amazonaws.com
    expirationSeconds: 900
  1. GCP Workload Identity. A workload uses a Kubernetes-authenticated token to impersonate a GCP service account. The audience is https://iam.googleapis.com/.
- serviceAccountToken:
    path: gcp-token
    audience: https://iam.googleapis.com/
    expirationSeconds: 900
  1. Custom APIs. A workload authenticates to an internal API using a Kubernetes-authenticated token. The audience is the API’s URL.
- serviceAccountToken:
    path: custom-token
    audience: https://api.internal.example.com
    expirationSeconds: 600

Common failure modes

  1. Wrong audience. The token has aud=https://kubernetes.default.svc but the workload presents it to Vault. Vault rejects. The fix is an explicit projection with the right audience.
  2. Default mount not disabled. The Pod has both the default mount (with API server audience) and the custom projection. The workload uses the default mount for Vault; Vault rejects. The fix is automountServiceAccountToken: false.
  3. Multiple tokens confused. A workload has three tokens and uses the Vault token for AWS. Both reject. The fix is to verify the mount paths and the audiences.
  4. Long expiry. A token with a 24-hour expiry is not revoked by a signing-key rotation within the window. The fix is the minimum duration the workload needs.

Production failure modes

  1. Tokens are projected without audiences. The default mount has the API server’s audience; a custom projection without an audience is rejected by every consumer. The fix is to always set audience.
  2. Tokens are mounted but never used. A token at /etc/secrets/vault/vault-token that the workload never reads is a credential that can be exfiltrated anyway. The fix is to mount only the tokens the workload needs.
  3. Tokens are not rotated. A workload with a 24-hour token that does not re-read sees a stale token after the kubelet’s rotation. The fix is shorter expiries and re-read before each call.

Cross-course references

  • The Linux course covers JWT and JWKS — the primitives that the kubelet uses.
  • The Observability course covers the audit log entries for projected token requests.

Quiz

Knowledge check · 4 questions

  1. Q1. What is the right pattern for a Pod that needs to authenticate to both the API server and Vault?

  2. Q2. AWS IRSA (IAM Roles for Service Accounts) requires a ServiceAccount token with audience `sts.amazonaws.com`, which can be projected into the Pod via a custom `serviceAccountToken` volume.

  3. Q3. Your workload uses Vault to read database credentials. The Pod has a custom projection with `audience: vault://prod.example.com`. The workload presents the token to Vault; Vault returns `403 audience mismatch`. Why, and how do you fix it?

    The Pod has both the default mount (API server audience) and the custom projection (Vault audience). The workload reads the default-mount token by mistake and presents it to Vault. The token's audience is the API server's, not Vault's. Vault rejects.

  4. Q4. Name three audiences commonly used in projected ServiceAccount tokens and the consumer each one targets.

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

Production discipline

Custom audience projection is the right primitive for workloads that authenticate to non-API consumers. Every consumer that accepts a Kubernetes-issued JWT has a specific audience; the projection must be configured for that audience. The default mount is for the API server; non-API consumers require explicit projections. A cluster whose workloads use custom audiences with explicit projections has a credential programme that is auditable; a cluster whose workloads use the default mount for non-API consumers has a credential programme that does not work.