· via dev.to (home feed)
Rotated Kubernetes service account tokens stay valid until expiry; bound Secrets revoke them
Rotating a Kubernetes service account token does not revoke the old one — it stays valid until expiry. A dev.to post shows how binding tokens to marker Secrets makes deletion act as revocation.

Rotating a Kubernetes service account token leaves the old credential fully usable. According to a post on dev.to by an engineer at Zenxsys, tokens issued through the TokenRequest API keep authenticating until their exp claim elapses — a window that can run from hours up to seven days. Issuing a replacement without killing the original is credential issuance, not revocation, and anyone who copied the previous token keeps access the whole time.
What naive rotation misses
Before changing anything, the author ran an existing rotation path against a single-node k3s 1.30.5 cluster. It worked, but the previous token was never invalidated, leaving a 24-hour to 7-day period in which a copied token still authenticates. There was also no preflight, so a missing RBAC verb only surfaced as a mid-rotation failure; no check that the new token actually authenticates before the old one is dropped; no grace period for consumers that pick up the replacement late; and no way back if the new token turned out to be wrong.
The primitive that makes revocation possible
The TokenRequest endpoint POST /api/v1/namespaces/{ns}/serviceaccounts/{sa}/token accepts a spec.boundObjectRef field. When a token is bound to an object, the API server checks at every authentication that the object still exists and still carries the same UID, so deleting the object stops the token from working.
Binding to a Pod is what projected volumes already do, but external holders such as CI systems have no Pod to anchor to. The post's answer is a marker Secret — an empty Opaque object whose only job is to exist and carry a UID. In the author's lab tests, the API server began refusing the bound token roughly ten seconds after the Secret was deleted, with runs ranging from 9.2 to 11.0 seconds. The delay comes from the API server's authentication cache, so revocation is fast but not instantaneous.
Two slots, verified swaps and rollback
Zenxsys built the pattern into zenxPAM, its just-in-time privileged access product, and the author discloses working on it. Each managed identity gets two marker slots, so the outgoing token can stay valid during a grace window while the replacement is already live. The flow creates the marker Secret, requests a token bound to it, and then proves the new credential with a SelfSubjectReview that must return the expected service account. A 401 fails the rotation; a 403 or 404 from a hardened or older cluster marks the check as skipped with a warning instead of silently passing.
Failures that occur after the marker Secret exists are compensated — the marker is deleted and vault state restored. The grace window is capped at the old token's remaining lifetime. Finalizing deletes the old marker and then polls the old token for up to 30 seconds; if it is still accepted after that, the result records a warning with evidence rather than assuming success. Rollback first verifies that the previous token still authenticates before restoring it, and changes nothing if the old token is already dead.
Least privilege and the confused-deputy risk
The rotator's own permissions stay narrow and namespace-scoped: create on serviceaccounts/token limited by name to the target service account, create on Secrets across the namespace (Kubernetes cannot restrict creation by name), and get and delete on Secrets restricted to the two slot names. One caveat the author acknowledges: the rotator can create arbitrary Secrets in that namespace, though it can only read or delete its own two markers.
The rotator itself is also flagged as a confused-deputy risk — a system that reads an API URL and an admin token from configuration, then calls that URL, is a convenient exfiltration gadget. Mitigations in zenxPAM include taking the cluster address and CA only from the cloud-account connector rather than the identity being rotated, requiring vault keys for admin tokens to start with K8S_, and refusing private or internal API hosts unless an operator allowlists them.
Stated limitations
Testing covered k3s 1.30.5 only — not EKS, AKS or GKE, which may cap token lifetimes or restrict SelfSubjectReview; the preflight reports the lifetime the API server actually grants. Revocation lags about ten seconds. And if secret binding is switched off, the old token simply stays valid until expiry, which the tool reports along with the end time.
Why it matters
Many teams treat token rotation as the response to a leaked credential, but for TokenRequest-issued tokens the old credential remains a live key until it expires on its own. The bound marker Secret pattern closes that gap by turning deletion into revocation, and it is implementable with nothing more exotic than kubectl. Operators should check what their rotation tooling actually does: whether it verifies the new token before switching, tolerates slow consumers, and confirms the old token is refused — ideally with an auditable record rather than a recollection.
- #kubernetes
- #security
- #service-accounts
- #access-management
- #devops