Kubernetes ServiceAccount Token Projection: Audience, Rotation, and Trust Boundaries
Configure projected Kubernetes ServiceAccount tokens with explicit audiences, short lifetimes, safe rotation, workload federation, and least-privilege boundaries.
A Kubernetes ServiceAccount token is a bearer credential: whoever can read and present it may act as the identity it represents, subject to the receiving system’s authorization policy. Projected tokens improve the lifecycle of that credential, but projection alone does not make a workload least-privileged, limit which process can read it, grant a cloud role, or guarantee that an external verifier can revoke it immediately.
The production design has several independent controls: choose a dedicated ServiceAccount, request a token for one intended audience, let the kubelet rotate the projected file, make the application reload it, and configure the relying party to trust only the expected issuer and workload identity. Treat every step as a contract to test, not as an automatic side effect of mounting a volume.
Separate identity, authentication, and authorization
A ServiceAccount identifies a workload within a namespace. A signed JWT can prove that identity to an API server or another system that has an explicit trust relationship with the issuer. Kubernetes authorization is a separate decision: the API server evaluates the authenticated identity through its configured authorization chain, commonly including RBAC. A token with a narrow audience does not reduce permissions granted by a broad RoleBinding; a restrictive RoleBinding does not stop another container with access to the same token from using it.
Avoid using the namespace’s default ServiceAccount as a shared workload identity. Create a named account for each distinct permission boundary, bind only required verbs, API groups, resources, namespaces, and where supported resource names, and review wildcard grants as deliberate privilege expansions. A dedicated identity makes audit evidence and revocation more precise, but is not a sandbox between processes that can access its credential.
For example, this Role permits one application identity to read one named ConfigMap, not every Secret or object in the namespace:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: orders-config-reader
namespace: payments
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["orders-runtime"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: orders-config-reader
namespace: payments
subjects:
- kind: ServiceAccount
name: orders-api
namespace: payments
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: orders-config-reader
Check both intended allows and important denies. kubectl auth can-i is an authorization check, not proof that the application can reach the API server, present a valid audience, pass admission, or complete the actual request:
kubectl auth can-i get configmap/orders-runtime \
--namespace payments \
--as=system:serviceaccount:payments:orders-api
kubectl auth can-i list secrets \
--namespace payments \
--as=system:serviceaccount:payments:orders-api
Run impersonation checks from an administrator identity that is already authorized to impersonate the target. The expected result for the first command is yes; the second should be no unless that access is intentionally part of the contract.
Project a token for the relying party, not by habit
On current Kubernetes clusters, the ordinary automatically injected ServiceAccount credential is a short-lived TokenRequest token mounted in a projected volume. Setting automountServiceAccountToken: false prevents that default mount. It is a useful default for workloads that do not call the Kubernetes API, but it does not remove an explicitly declared projected token volume. Make that volume explicit and mount it only into the container that needs the identity.
The audience is the recipient identifier the token is intended for. If it is omitted from a projected service-account token source, the API server identifier is used. A recipient must validate that its own configured audience appears in the token; merely decoding a JWT and reading aud is not validation. Request a distinct audience for an external identity broker or service, then configure that relying party to reject tokens for other audiences. Do not copy an API-server token to another system and assume the other system has become a trusted audience.
apiVersion: v1
kind: ServiceAccount
metadata:
name: orders-api
namespace: payments
automountServiceAccountToken: false
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-api
namespace: payments
spec:
replicas: 2
selector:
matchLabels:
app: orders-api
template:
metadata:
labels:
app: orders-api
spec:
serviceAccountName: orders-api
automountServiceAccountToken: false
containers:
- name: app
image: registry.example.com/orders-api:1.0.0 # Replace with a reviewed image
volumeMounts:
- name: workload-identity
mountPath: /var/run/workload-identity
readOnly: true
volumes:
- name: workload-identity
projected:
sources:
- serviceAccountToken:
audience: "https://identity.example.com"
expirationSeconds: 3600
path: token
The audience here is illustrative. An actual recipient must agree on the exact value and validate signature, issuer, time claims, audience, and the workload identity claims it relies on. expirationSeconds is a requested lifetime: the API server can cap it. Kubernetes documents a one-hour default for projected token sources, a minimum of 600 seconds, and the API-server maximum-expiration setting. Avoid lengthening token lifetime merely to conceal an application that cannot reload credentials.
Kubernetes can publish service-account issuer discovery endpoints when configured, allowing an external system to act as an OIDC relying party. Federation still requires deliberate trust on that system’s side. Bind the cluster issuer and the expected subject and audience to the role or policy being granted; do not accept every token signed by an issuer as a production workload. A projected token does not itself grant cloud permissions, and a cloud provider’s exchanged credentials have their own lifetime and revocation behavior. Those details vary by provider and are not established by a Kubernetes volume manifest.
Rotation works only if the client follows the file
The kubelet obtains and refreshes projected ServiceAccount tokens. Kubernetes documents proactive rotation once a token is older than 80 percent of its TTL, or older than 24 hours. The application remains responsible for reloading the token after rotation. A process that reads the file once during startup and caches the string indefinitely can fail after expiry even while Kubernetes is successfully refreshing the volume.
Implement a credential provider that reopens the configured path before constructing authenticated requests, or periodically reloads it on a bounded schedule shorter than the expected expiration. Handle transient read or authentication failures without logging the token. Do not pass the bearer value in environment variables, command-line arguments, crash reports, tracing attributes, or debug output. Environment variables are a poor rotation interface because updating a projected file does not rewrite a running process’s environment.
Do not mount a projected token through a subPath volume mount if the application relies on rotation: Kubernetes documents that a subPath mount does not receive projected-volume updates. Test a live token rotation with the actual client library, not just with kubectl describe pod. Also test token renewal while the receiving identity service is briefly unavailable; decide whether the app should continue with a still-valid token, retry with bounded backoff, or fail readiness.
Bound-token deletion and external validation are different
TokenRequest tokens are bound to an API object such as a Pod and have a finite lifetime. The API server checks the referenced object identity, including its UID, when authenticating a bound token. Kubernetes recommends the TokenReview API for a service that must validate Kubernetes tokens and honor deletion-based invalidation. A service validating the JWT only through OIDC discovery can continue accepting it until its expiration time. Kubernetes also documents that Pod credentials are revoked 60 seconds beyond the Pod’s deletionTimestamp, which is generally set when the delete request is accepted plus the termination grace period. Do not promise instantaneous external revocation when a relying party uses cached OIDC verification or issues a separate credential.
This distinction matters during incident response. Deleting or replacing a Pod is a Kubernetes token invalidation mechanism, but an external service may have already exchanged that assertion for another credential. Revoke or constrain the resulting cloud/session credential through its own identity system, and verify its expiration behavior. Prefer short exchange sessions and narrow permissions so that a stolen token has limited reach and time.
Limit who can read the projected file
A volume is visible to containers only where it is mounted, so mount the token only in the application container that needs it. Set readOnly: true; this prevents writes through that mount but is not a confidentiality boundary against a process that can read the file. A compromised process in that container can use or exfiltrate its token. Avoid unnecessary sidecars, debug containers, shared process access, broad exec access, privileged workloads, host filesystem mounts, and untrusted workload authors in the same Pod security boundary. Use namespace isolation, admission policy, Pod security controls, network policy, and node hardening as complementary controls, not as a claim that namespaces alone isolate a bearer token.
If the workload only needs to call a non-Kubernetes endpoint, disable automatic API credential mounting and project only the external audience it needs. If it needs the Kubernetes API, use the intended API-server audience and a narrowly bound Role. Do not grant both uses through a vague “one token for everything” design unless the API server and relying party have an explicitly reviewed multi-audience contract.
Acceptance checks for a rollout
Before production, verify the ServiceAccount and RoleBinding in the intended namespace, then check one permitted action and several prohibited actions. Inspect the Pod spec to confirm automatic mounting is disabled and only the intended container has the explicit projected volume. Confirm the recipient rejects a token with the wrong audience, issuer, subject, expired time, or invalid signature. For an external OIDC path, verify the discovery document and key rotation handling against the cluster configuration and the relying party’s supported behavior.
Finally, observe the application across at least one real rotation interval, or use a deliberately short-lived test token where supported. Confirm it reopens the file, does not emit token contents, continues or fails according to its documented retry policy, and stops authenticating after Pod deletion within the documented validation path. Record which system validates each claim and where exchanged credentials must be revoked. That evidence demonstrates the whole identity chain; a mounted file by itself demonstrates only that projection was configured.
Related:
- Kubernetes RBAC Impersonation: Testing Authorization Without Sharing Credentials
- GitHub Actions OIDC: Short-Lived Cloud Credentials Without Repository Secrets
Sources: