Secrets, PKI & CertificatesXII · Secret Management PlatformsSecretManagers
Tokens, authentication methods and identity
What you'll learn
- Describe a session token as a handle carrying policy names, a time to live and an accessor
- Compute the effective lifetime of a token from the mount, system and method limits
- Predict what a revocation cascades to, and what an orphan token breaks about that
- Choose an authentication method from what the platform already issues to the workload
Prerequisites
Practice
Verified against OpenSSL 3.5.x teaching target; 3.0+ minimum · OpenSSH 10.x teaching target; 8.2+ minimum for certificate workflows · OpenBao 2.6.x · Smallstep step-ca 0.30.x · Certbot / Pebble Certbot current release; Pebble 2.10.x ACME test server · Kubernetes (cross-course target) 1.36.x · PostgreSQL 17.x · 2026-08-26
Every request after login carries a token, and almost every serious operational mistake in this part of the stack is a misunderstanding of what that token is. It is not a key. It is not a password. It is a handle that the manager resolves, at request time, into a set of policy names and a remaining lifetime. Everything useful about token hygiene follows from taking that definition literally.
What the manager stores against a token
Logging in through an authentication method returns a small object, and every field in it is operationally significant.
"auth": {
"client_token": "s.<REDACTED>",
"accessor": "n5GSr33ga2aoj00X03Z3y4Qh",
"policies": ["app-read", "default"],
"token_policies": ["app-read", "default"],
"metadata": {"role_name": "app-role"},
"orphan": true,
"lease_duration": 1200,
"renewable": true
}
The client_token is the bearer value; anyone holding it is
the identity, which is why it never belongs in a log line, a
process argument or an environment variable that a support
bundle will collect. The leading s. marks it as a service
token, the type that is tracked in storage and can be renewed
and revoked.
The accessor is a separate identifier for the same session
that is not usable to authenticate. Its value is that it
appears in audit records alongside the token, so an incident
responder can attribute every request made by one session,
write the identifier into a ticket, and hand it to somebody
else, without ever handling the credential itself.
policies names what the session may do, default being a
built-in policy attached to all tokens that cannot be removed.
lease_duration of 1200 is the token’s initial time to live in
seconds, here twenty minutes. renewable says the holder may
ask for more time. orphan says this session has no parent,
which changes what a revocation elsewhere will do to it.
Three numbers bound the lifetime, and the smallest wins
Operators frequently set one time to live and are surprised by the number they get back. The effective lifetime is the minimum of three limits, recomputed at every renewal.
# The authentication method's own suggestion, per role.
bao write auth/approle/role/app-role \
token_policies=app-read token_ttl=20m token_max_ttl=1h
- The system maximum. A server-wide
max_lease_ttl, which defaults to 768 hours, that is thirty-two days. No credential in the cluster outlives it. - The mount maximum. Each mount may carry its own lower ceiling, which overrides the system value for anything issued from that mount.
- The method suggestion. The authentication role may suggest a value. It can only suggest something less restrictive than the mount or system limit as an advisory; it can never raise the ceiling above them.
Laid out as an arithmetic problem, with a mount ceiling of twenty-four hours, the answer falls out of the smallest number present rather than the one you last edited:
system max_lease_ttl 768h (32 days, the default)
mount maximum 24h
role token_max_ttl 1h
role token_ttl 20m
-------------------------------------------------------
issued lease_duration 20m
ceiling honoured at renewal 1h (the smallest maximum)
Renewal is where the second surprise lives. The increment requested at renewal is measured from the current time, not added to the end of the current window, and the requested increment is completely advisory: the backend in charge may ignore it entirely. A client that renews with a two-hour increment against a role whose maximum is one hour receives whatever the ceiling permits, and a client may also renew with a smaller increment to shorten its own session deliberately.
Revocation cascades down the token tree
Tokens form a tree. A token created by another token is its child, and revoking a parent revokes all of its child tokens and all of their leases as well. That cascade is the mechanism that makes containment tractable: revoke the session an application used and every dynamic credential it obtained goes with it.
flowchart TD
P["Parent token\nissued at login"] --> C1["Child token"]
P --> C2["Child token"]
C1 --> L1["Lease: database credential"]
C2 --> L2["Lease: database credential"]
O["Orphan token\nno parent"] --> L3["Lease: database credential"]
P -.->|"revoke parent"| X["Both children and\nboth leases revoked"]
O -.->|"unaffected by any\nparent revocation"| L3
The diagram shows both behaviours side by side. Revoking the
parent takes the two children and the leases they created with
it, in one operation. The orphan token sits outside that tree
entirely: it has no parent, so it starts its own tree and no
revocation higher up can reach it. Note that the AppRole login
shown earlier returned "orphan": true. That is normal and
sensible for a machine login, because a workload’s session
should not evaporate because an unrelated operator session was
cleaned up, but it does mean the only way to end that session
is to revoke it directly or let it expire.
Where the first credential comes from
An authentication method converts something the client already
has into a session. Nine methods are built into the v2.6.2
binary: approle, cert, jwt, kerberos, kubernetes,
ldap, oidc, radius and userpass. There is no aws,
azure or gcp method in the binary at all; all cloud vendor
plugins have been moved external, so a lab step that runs
bao auth enable aws against a stock build fails. Kerberos,
LDAP and RADIUS are deprecated and slated for removal from the
main binary in v2.7.0, which makes them poor foundations for
new work.
For servers, AppRole is the documented recommendation, while userpass is positioned for developer machines. AppRole splits the credential in two: a RoleID that selects the role and is treated as a unique identifier rather than a secret, and a SecretID that is intended to always be secret. The pull mode is the recommended one, in which the manager generates the SecretID and the client fetches it, because that enables response wrapping to keep the SecretID confidential from intermediate systems during delivery. Push mode, where an external system knows both halves and injects them, is retained for legacy compatibility.
For in-cluster workloads the Kubernetes method is the direct answer. The pod presents its service account token, and the manager does not take it at face value: it calls the Kubernetes TokenReview API to validate that the JWT is still valid, then matches the result against bound service account names, namespaces and audience. Revocation correctness depends on the API server running with service account lookup enabled, which has defaulted on since Kubernetes 1.7; without it, tokens deleted in Kubernetes are not properly revoked here.
The costume: a long-lived token is a static secret
Everything above exists to make one point payable. A token whose lifetime is long enough that nobody thinks about renewal has all the properties of the credential file it was supposed to replace. It sits in an environment variable. It is copied into a second environment for testing. It appears in a support bundle. It outlives the person who created it, and it cannot be attributed to anything more specific than whoever pasted it.
The manager’s contribution was never the storage. It was the short lifetime, the per-request policy evaluation and the attributable record. Issue a token with a thirty-day life to a workload that could log in every twenty minutes and you have paid the full operational cost of a secret manager for none of its benefit. Worse, you have moved the credential to a place where fewer people are watching it, because everyone now believes secrets are handled.
The honest test is a question with a number in it: if this token leaked at a random moment, how long would the attacker have? If the answer is measured in weeks, the token is a static secret in a costume, and the fix is not better storage for it. The fix is a login the workload can repeat unattended, so the lifetime can be short enough that the answer becomes minutes.
Production discipline
- Make the workload able to re-authenticate unattended. Short lifetimes are only safe when a fresh login costs nothing. If re-login requires a human, the lifetime will creep upwards until it is meaningless.
- Set the mount ceiling, not just the role suggestion. A role value can be raised by whoever edits the role. A mount maximum bounds everything issued from it and is a separate change with separate review.
- Treat renewal failure as a login trigger. A client that handles a shortened or refused renewal by logging in again survives ceiling changes and manager restarts without an outage.
- Never log the token, and never pass it as a command argument. Process arguments are world-readable on a Linux host. Record the accessor instead when you need to attribute activity later.
- Prefer the method that consumes platform-issued identity. In Kubernetes that is the service account token validated through TokenReview, or JWT authentication against the cluster issuer. Falling back to AppRole is reasonable; falling back to a pasted token is not.
Cross-course references
- Kubernetes for Production Sysadmins - Part LVII (Authentication) covers the service account token and the TokenReview path that the Kubernetes method depends on.
- Linux for Production Sysadmins - Part IV (Users, Groups and Identity) covers the service account model whose long-lived credentials this lesson is arguing against.
- Git, CI/CD & GitOps for Infrastructure Engineers - Part XLII (CI Secrets) covers the pipeline variables where long-lived tokens accumulate and the masking that does not reliably hide them.
Quiz
Knowledge check · 4 questions
Q1. A client renews a token with a requested increment of two hours. What determines the time it actually receives?
Q2. Revoking a parent token also revokes any orphan token that was created during the same session.
Q3. What is a token accessor, and why is it useful during an incident?
Q4. Judge whether this deployment has genuinely adopted a secret manager, and say what you would change first.
A platform team migrated twelve services to a secret manager in June. Each service reads its database credential at startup using a token that was created once by an engineer and pasted into the deployment manifest. The tokens were issued with a thirty-day lifetime and a cron job renews them nightly. On 2026-08-26 a manifest repository is found to have been world-readable for three weeks, and one of the tokens appears in a build log from 2026-08-05.
Passing score: 75%. Answers are checked in this browser.