create secrets
Annotating a kubernetes.io/service-account-token Secret with a target ServiceAccount makes the token controller mint a long-lived, non-expiring JWT for it, with no create on serviceaccounts/token and no pod required.
The token is populated after the create returns, so reading it takes a second grant.
T0 with following verbs:
Authenticate as a privileged ServiceAccount in the namespace
scoped to same namespace as a cluster-admin-equivalent ServiceAccount
create does not imply read, and the token controller writes .data.token asynchronously after the create response.
Retrieval needs get, list, or watch on secrets, or a pod that mounts the Secret.
Contextual upgrade to T0:
T0 where a cluster-admin-equivalent ServiceAccount exists in the same namespace, such as a kube-system controller SA, and the minted token can be read back.
- API Group
- (core)
- Scope
- namespaced
- Audit Level
- RequestResponse
Escalation Paths
get a known service-account-token Secret name in kube-system.
Before 1.24, these were auto-created for every ServiceAccount under a predictable name, so they could simply be guessed
Auto-creation is off by default from 1.24, so on a current cluster the Secret has to already exist, and operator-managed clusters commonly keep one
Where secrets-create is also held, the precondition can be manufactured rather than found.
Annotate a new Secret for a privileged ServiceAccount, and the token controller fills in the token
Reuse the controller ServiceAccount token for cluster-admin-equivalent access.
Tokens from Secrets carry no expiry claim, so the access does not lapse
Additional rights needed:
· scoped to kube-system
· needs the Secret name, and a privileged token Secret must already exist
· scoped to kube-system
· enumerates instead, so no name is needed
· scoped to kube-system
· also catches a token Secret created later
· scoped to kube-system
· create manufactures the precondition rather than finding it
Note:
1.24 stopped auto-creating token Secrets, so guessing a predictable name is dead, but the underlying mechanism is not.
Manually created service-account-token Secrets are still populated with non-expiring tokens, and secrets-create in kube-system manufactures the precondition.
Create a kubernetes.io/service-account-token Secret annotated with the target ServiceAccount name.
The token controller populates .data.token with a signed JWT for that ServiceAccount which does not expire.
Population happens after the create response returns, so the token is never in the create result.
Retrieval therefore needs get, list, or watch on secrets, or a pod that mounts the Secret.
create alone mints a credential the caller cannot read.
Authenticate as the target ServiceAccount with the retrieved token.
Additional rights needed:
· create mints the token, get reads it back by name
· list returns the data without needing the name
· watch catches the token as the controller fills it in
· a pod that mounts the Secret reads it instead
Note:
The token controller still populates kubernetes.io/service-account-token Secrets created by hand, and long-lived token Secrets remain supported.
Restrict create on secrets and audit for hand-made token Secrets in kube-system.
