Operator authentication paths
Default JWT authentication, the audience contract behind it, and the checks to run when you customize controller identity wiring.
Diagram
Default operator auth path
Kubernetes issues a projected token for the controller. The operator sends that JWT with each OpenBao maintenance request using inline authentication.
Decision matrix
JWT default path
| Property | What you get | Why it matters |
|---|---|---|
| No stored root token | The controller authenticates with a projected ServiceAccount token instead of a long-lived Secret. | You avoid carrying a static high-privilege credential in Kubernetes just to let the operator do day 2 work. |
| Automatic rotation | Kubernetes rotates the projected token on its own schedule. | Controller auth ages out naturally instead of depending on manual token rotation discipline. |
| Audience binding | The token is scoped to OPENBAO_JWT_AUDIENCE, which defaults to openbao-internal. | A token issued for OpenBao auth should not be replayable against unrelated services. |
| Scoped maintenance policy | The JWT role grants the operator maintenance policy to each authenticated request, not blanket admin privileges. | The controller gets the capabilities it needs for health checks, step-down, and autopilot without widening normal access. |
For supported OpenBao versions, JWT-backed operator requests use OpenBao inline authentication by default.
The controller, backup jobs, restore jobs, and upgrade jobs still use the same projected ServiceAccount JWTs,
role names, audiences, and policies. The transport changes from a separate login request plus X-Vault-Token
to inline auth headers on the actual OpenBao request.
Reference table
JWT transport strategy
| Strategy | How it authenticates | When to use it |
|---|---|---|
inline | Sends the projected ServiceAccount JWT through OpenBao inline auth headers on each operator-owned request. | Default for supported OpenBao versions. Use this for normal operation. |
standard | Performs the legacy JWT login request and then sends the returned OpenBao token as X-Vault-Token. | Operational compatibility switch when an intermediary drops custom inline auth headers or enforces header-size limits. |
Configure
Use the standard JWT strategy
kubectl -n openbao-operator-system set env deployment/openbao-operator-controller-manager \
OPENBAO_JWT_AUTH_STRATEGY=standard
Leave the variable unset, or set it to inline, for the default inline-auth path. The operator propagates this setting to backup, restore, and upgrade executor jobs.
Inline authentication sends the projected ServiceAccount JWT in
X-Vault-Inline-Auth-Parameter-jwt. Treat this header like X-Vault-Token and
Authorization: do not log it in ingress, proxy, service mesh, debug, or audit
shipping pipelines. Logging request metadata such as method, path, status, and
duration is safe; logging full HTTP headers is not.
spec.selfInit.oidc.enabled: true bootstraps the controller auth path only.
It does not create a human login method by itself.
If you use self-init, create human access in the same bootstrap contract through selfInit.requests so the cluster has an operator path and a human path from the start.
spec.selfInit.oidc.enabled: true bootstraps the controller JWT auth method, policy, and role.
After bootstrap, the operator uses that auth surface but does not continue mutating OpenBao
policies on its own. When a later operator release needs an additional controller capability,
the human operator must update the openbao-operator policy explicitly.
Decision matrix
Bootstrap authentication surfaces
| Surface | Where it is defined | Why it exists |
|---|---|---|
| Operator lifecycle auth | spec.selfInit.oidc.enabled or an equivalent manually managed JWT role | Lets the operator perform backup, restore, upgrade, and maintenance work with a short-lived scoped identity. |
| Human login path | spec.selfInit.requests or another deliberate bootstrap path in the same cluster bring-up plan | Ensures someone can actually sign in after the root token is revoked. |
Self-init and manual bootstrap
Decision matrix
Choose the bootstrap path
| Path | Use it when | Operator behavior | Watch for |
|---|---|---|---|
| Self-init with OIDC | You want the supported production path and the cluster is allowed to bootstrap its own operator auth surface. | The operator configures JWT auth, discovery, the openbao-operator policy, and the bound role automatically. | Bootstrap a human login path in selfInit.requests at the same time so the cluster is usable after root-token revocation. |
| Manual JWT configuration | You are carrying a compatibility workflow, a custom install shape, or a controlled bootstrap sequence. | You create the JWT auth method, policy, and role binding yourself. | Rendered ServiceAccount name, namespace, and audience drift are the usual breakpoints. |
Reference table
Who owns policy changes after bootstrap
| Install shape | Who creates the initial policy | Who updates it later |
|---|---|---|
| Self-init with OIDC | The bootstrap contract created by the operator during self-init. | The human operator. Self-init does not keep mutating OpenBao policies after the cluster is initialized. |
| Manual JWT configuration | The cluster administrator. | The cluster administrator. |
Configure
Controller policy scope
path "sys/health" {
capabilities = ["read"]
}
path "sys/step-down" {
capabilities = ["sudo", "update"]
}
path "sys/storage/raft/configuration" {
capabilities = ["read"]
}
path "sys/storage/raft/remove-peer" {
capabilities = ["update"]
}
path "sys/storage/raft/autopilot/configuration" {
capabilities = ["read", "update"]
}
path "sys/storage/raft/autopilot/state" {
capabilities = ["read"]
}
Keep the controller policy focused on maintenance work. Backup, restore, and upgrade jobs authenticate through their own roles instead of inheriting the controller scope.
Configure
Manual JWT role example for a custom controller identity
bao write auth/jwt-operator/role/openbao-operator \
role_type="jwt" \
bound_audiences="openbao-internal" \
user_claim="sub" \
bound_subject="system:serviceaccount:platform-security:demo-openbao-operator-controller" \
token_policies="openbao-operator" \
token_ttl="1h"
Replace the namespace, ServiceAccount name, and audience with the values from your rendered install, not the example defaults.
What must stay aligned
Reference table
Custom-install checks
| Surface | Must match | What breaks when it drifts |
|---|---|---|
| Controller identity | Rendered controller ServiceAccount name and operator namespace | The OpenBao JWT role or admission-policy identity references the wrong controller subject. |
| Projected token mount | The controller Deployment still mounts the openbao-token projected volume | The controller loses its default path to authenticate to OpenBao at all. |
| JWT audience | OPENBAO_JWT_AUDIENCE, the projected token audience, and the OpenBao role bound_audiences | A valid ServiceAccount token is rejected because it was issued for a different audience contract. |
| Discovery access | The controller can reach /.well-known/openid-configuration and the JWKS endpoint | OpenBao cannot validate the projected token even though the controller identity itself is correct. |
Reference table
Typical auth failures
| Symptom | Most likely cause | Check first |
|---|---|---|
permission denied when the controller calls OpenBao | JWT audience mismatch or an incorrect bound subject | Rendered identity and OPENBAO_JWT_AUDIENCE alignment |
| Self-init completes but controller auth never settles | OIDC discovery is unreachable or incomplete | Kubernetes API discovery reachability and cluster condition output |
| Backup or restore auth fails even though controller auth works | Executor jobs use separate roles and ServiceAccounts | Operator authorization and the job-specific auth path |
Continue the support path
Official OpenBao background
You are reading docs for version 0.4.x. Use the version menu to switch to next or another archived release.
Was this page helpful?
Use Needs work to open a structured GitHub issue for this page. The Yes button only acknowledges the signal locally.