Kubetier
← Permission Reference
T1

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

  1. 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

  2. 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

  3. 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

  4. 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.

K8s docs ↗
  1. Create a kubernetes.io/service-account-token Secret annotated with the target ServiceAccount name.

  2. The token controller populates .data.token with a signed JWT for that ServiceAccount which does not expire.

  3. Population happens after the create response returns, so the token is never in the create result.

  4. 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.

  5. Authenticate as the target ServiceAccount with the retrieved token.

Additional rights needed:

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.

K8s docs ↗
K8s docs ↗