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. OpenBao validates that token against the configured JWT auth method and returns a scoped operator token for maintenance work.
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 returns a token with the operator maintenance policy, not blanket admin privileges. | The controller gets the capabilities it needs for health checks, step-down, and autopilot without widening normal access. |
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 the unreleased main docs. Use the version menu for the newest published release, or check the release notes for what is already out.
Was this page helpful?
Use Needs work to open a structured GitHub issue for this page. The Yes button only acknowledges the signal locally.