The controller, OpenBao Pods, and lifecycle Jobs use different identities. Keep each Kubernetes ServiceAccount aligned with its projected token, OpenBao JWT role, and policy.
Map the identities
| Actor | Kubernetes identity | OpenBao authentication | Boundary |
|---|---|---|---|
| Provisioner | Provisioner ServiceAccount in the operator namespace | None | Kubernetes RBAC only |
| Controller | Controller ServiceAccount in the operator namespace | Projected JWT bound to openbao-operator | Routine lifecycle and maintenance |
| OpenBao Pods | Per-cluster ServiceAccount | Runtime and unseal integrations | Server workload identity |
| Backup Job | Generated backup ServiceAccount | Projected JWT or backup token Secret | Snapshot read and object storage |
| Restore Job | Generated restore ServiceAccount | Projected JWT or restore token Secret | Destructive snapshot restore |
| Upgrade Job | Generated upgrade ServiceAccount | Projected JWT | Rolling or BlueGreen orchestration |
Changing JWT transport does not merge these identities. Each actor retains its own ServiceAccount, role, audience, and policy.
Understand the default JWT path
The controller Deployment mounts a projected one-hour ServiceAccount token. Its audience and
OPENBAO_JWT_AUDIENCE both default to openbao-internal. OpenBao validates the JWT against the bound audience and
the controller ServiceAccount subject.
The default inline strategy sends the JWT in the request-specific OpenBao inline-auth headers. Use standard only
when a proxy or intermediary cannot carry those headers; it performs a JWT login and sends the resulting OpenBao token
as X-Vault-Token.
Use the standard JWT transport
kubectl -n openbao-operator-system set env \
deployment/openbao-operator-controller \
OPENBAO_JWT_AUTH_STRATEGY=standard
kubectl -n openbao-operator-system rollout status \
deployment/openbao-operator-controllerLeave the variable unset, or set it to inline, for the default path. The controller propagates the selected strategy
to JWT-backed backup, restore, and upgrade Jobs.
Bootstrap operator and human access
spec.selfInit.enabled: true makes OpenBao execute the initialization requests and revoke the root token.
spec.selfInit.oidc.enabled: true adds the operator JWT auth method, role, and policies to that bootstrap. It does not
create human access.
Self-init is one-shot. The operator uses the generated auth surface after initialization but does not continuously reconcile OpenBao policies. A human administrator must apply later policy changes required by an operator upgrade.
Configure a manual controller role
Use manual JWT configuration only for a controlled bootstrap or a custom install that cannot use self-init OIDC. Render the operator installation first, then substitute the actual namespace, ServiceAccount, and audience.
Bind 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 \
token_max_ttl=1h \
token_no_default_policy=trueThe controller policy needs the steady-state maintenance paths:
Define the controller policy
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"]
}Do not add backup, restore, or upgrade permissions to this policy. Those belong to job-specific roles.
Verify a custom installation
Check these values together:
- The rendered controller ServiceAccount name and operator namespace.
- The Deployment’s projected
openbao-tokenvolume and one-hour expiration. - The projected audience and
OPENBAO_JWT_AUDIENCE. - The JWT role’s
bound_audiencesandbound_subject. - The controller’s reachability to the configured OIDC discovery or JWKS endpoint.
- The separate ServiceAccounts and JWT roles generated for lifecycle Jobs.
Troubleshoot authentication
| Symptom | Likely cause | Check first |
|---|---|---|
Controller receives permission denied immediately | Audience or bound-subject mismatch | Rendered controller identity and JWT role |
| Self-init finishes but operator auth does not settle | Discovery or JWKS cannot be reached or validated | Issuer, discovery URL, CA, and network path |
| Backup or restore auth fails while the controller works | Job-specific identity or role is missing | The Job’s ServiceAccount and JWT role |
| A custom namespace or prefix breaks auth | OpenBao still trusts the default subject | All rendered identity references and bound_subject |
If JWT login succeeds but a specific request is denied, continue with operator authorization.