KubernetesLVII · AuthenticationAuthentication
Bearer tokens — long-lived, file-based, deprecated
What you'll learn
- Explain how bearer tokens authenticate to the API server
- Identify the long-lived token file and the legacy ServiceAccount Secret
- Recognise the deprecation timeline (1.24 beta opt-in for projected tokens, 1.32 removal)
- Migrate from long-lived tokens to projected tokens or OIDC
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
Bearer tokens are the most general authentication
mechanism Kubernetes supports. The client sends a token
in the Authorization header; the API server validates
it via TokenReview and the matched authenticator returns
a UserInfo. The two legacy forms of bearer tokens —
the static --token-auth-file and the long-lived
ServiceAccount token Secret — are now deprecated. This
lesson covers how bearer tokens work, the legacy forms
that must be removed, and the migration path.
How bearer tokens authenticate
Every request to the API server may carry a token:
GET /api/v1/namespaces/prod/pods
Authorization: Bearer <token>
The API server’s authentication chain tries each
configured authenticator against the token. The first
match returns a UserInfo; the chain stops. If no
match, the request is rejected (or treated as
anonymous, depending on the configuration).
flowchart LR
R[Request] --> T[Authorization: Bearer header]
T --> A{Static token file?}
A -->|match| U1[UserInfo]
A -->|no match| B{ServiceAccount token?}
B -->|match| U2[UserInfo]
B -->|no match| C{OIDC?}
C -->|match| U3[UserInfo]
C -->|no match| D{Webhook?}
D -->|match| U4[UserInfo]
D -->|no match| AN[401 / anonymous]
Legacy form 1: --token-auth-file
The oldest form of bearer token is a static file of
token,user,uid,group lines passed to the API server:
# /etc/kubernetes/auth-tokens
abcdef1234567890,alice,1001,team-a-dev
fedcba0987654321,bob,1002,team-a-dev
The API server matches the Authorization: Bearer abcdef1234567890 header against this file and returns
{username: "alice", groups: ["team-a-dev"]} as the
UserInfo. The token is forever — it does not
expire, and there is no revocation except by editing
the file and reloading the API server.
# kubeconfig entry (legacy)
users:
- name: alice
user:
token: abcdef1234567890
This is structurally identical to the “long-lived API key” pattern that every security team has deprecated in the past decade. Kubernetes ships it for backwards compatibility; production clusters should not use it.
Legacy form 2: long-lived ServiceAccount token Secrets
The second legacy form is the long-lived ServiceAccount
token Secret. Each ServiceAccount has a corresponding
Secret of type kubernetes.io/service-account-token:
apiVersion: v1
kind: Secret
metadata:
name: ci-runner-token-xyz
annotations:
kubernetes.io/service-account.name: ci-runner
type: kubernetes.io/service-account-token
data:
token: <base64-encoded JWT>
ca.crt: <base64-encoded CA>
namespace: <base64-encoded namespace>
The token is a JWT signed by the service-account-key
on the API server. It is valid until the token controller
deletes the Secret, which is never by default. The
JWT carries no audience claim and no expiry. Any process
that can read the Secret can use the token until the
Secret is deleted.
The deprecation timeline:
| Version | Change |
|---|---|
| 1.6 (2017) | ServiceAccount admission controller auto-creates long-lived tokens |
| 1.21 (2021) | kubernetes.io/service-account-token Secrets marked deprecated |
| 1.22 (2021) | Auto-creation moved behind LegacyServiceAccountTokenNoAutoCreation (beta off by default) |
| 1.24 (2022) | Auto-creation off by default; --service-account-signing-key-file and audience flags introduced |
| 1.30 (2024) | Long-lived token Secrets removed from auto-creation entirely |
| 1.32 (2025) | Long-lived token Secrets unreachable via legacy API paths |
A cluster running 1.34 must use projected tokens (next lesson). Long-lived token Secrets are a hard error.
Why long-lived tokens are dangerous
The structural reasons:
- No expiry — leaked tokens are forever.
- No revocation — the only way to invalidate is to delete the Secret, which is manual.
- No audience scoping — the token works against any API server that trusts the signing key. A token issued for cluster A works on cluster B if the CA is shared.
- No per-request identity — the JWT has a subject but no IdP record. “alice” cannot be traced.
The replacement is projected tokens (Lesson 3) for workloads and OIDC (Lesson 4) for humans. Both are short-lived (1 hour by default), revocable (the signing key rotation invalidates outstanding tokens), and scoped (audience claim).
Migrating from long-lived tokens
For ServiceAccount tokens:
- Audit:
kubectl get serviceaccounts -A -o json | jq '.items[] | {name, namespace}' - Find long-lived Secrets:
kubectl get secrets -A -o json | jq '.items[] | select(.type=="kubernetes.io/service-account-token")' - Migrate workloads to projected tokens via
automountServiceAccountToken: true(default) and the workload’s RBAC. - Delete the long-lived Secrets:
kubectl delete secret <name> -n <namespace>
For --token-auth-file:
- Replace with OIDC for humans.
- Replace with projected tokens for CI/CD.
- Remove the flag from the API server flags.
Production failure modes
- Long-lived token Secrets survive the 1.32 cutover. A cluster upgraded past 1.32 may still have Secrets of the legacy type. Audit and delete them.
- CI/CD pipelines still use static tokens. The
pipeline runs
kubectl --token=$TOKEN. Migrate to OIDC or to projected tokens via the TokenRequest API (next lesson). --token-auth-fileis not removed after OIDC is added. Both authenticators are active; the static token file is a back door that survives a switch to OIDC. Remove the flag, do not just stop using it.- Tokens shared across clusters. A token issued by cluster A works on cluster B if the same CA signs both. Multi-cluster setups must use audience claims or per-cluster CAs.
Cross-course references
- The Linux course covers JWT signing and the key management that underpins all token authentication.
- The Observability course covers the audit log entries for token-based requests.
Quiz
Knowledge check · 4 questions
Q1. Which bearer token form is deprecated and inappropriate for human users in production?
Q2. Long-lived ServiceAccount token Secrets (type kubernetes.io/service-account-token) are still auto-created by default in Kubernetes 1.34.
Q3. Your CI pipeline authenticates to the cluster with a static token from `--token-auth-file`. The token has been in the file for 18 months and is used by 12 pipelines across 4 teams. The token was leaked in a public GitHub commit. What is the blast radius, and what is the migration path?
The token is `abcdef1234567890` with username `ci-pipeline` and group `ci-runners`. The token has full RBAC for cluster read and write to the `ci` and `staging` namespaces. The CI pipelines run from a corporate AWS account with IP allow-listing, but the token is also accepted from any IP.
Q4. Name the two legacy bearer token forms in Kubernetes and the modern replacement for each.
Passing score: 75%. Answers are checked in this browser.
Production discipline
Bearer tokens are the right authentication shape for
Kubernetes (token in a header, validated by a chain of
authenticators), but the right kinds of bearer tokens
are projected tokens for workloads and OIDC for humans.
A cluster that ships --token-auth-file or that has
long-lived ServiceAccount token Secrets has a
credential-management failure that should be fixed
before any other security work. The deprecation
timeline (1.30 auto-creation removed, 1.32 legacy type
unreachable) is the lever; the audit (kubectl get secrets -A --field-selector type=kubernetes.io/service-account-token) is the
verification. A cluster whose CI runs on static tokens
is a cluster that has not done the migration.