Configure operator authentication

Align Kubernetes identities, projected JWTs, OpenBao roles, and human access during bootstrap.

Updated 26 September 2026 · 5 min read

On this page

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

ActorKubernetes identityOpenBao authenticationBoundary
ProvisionerProvisioner ServiceAccount in the operator namespaceNoneKubernetes RBAC only
ControllerController ServiceAccount in the operator namespaceShared or target-specific JWT bound to openbao-operatorRoutine lifecycle and maintenance
OpenBao PodsPer-cluster ServiceAccountRuntime and unseal integrationsServer workload identity
Backup JobGenerated backup ServiceAccountProjected JWT or backup token SecretSnapshot read and object storage
Restore JobGenerated restore ServiceAccountProjected JWT or restore token SecretDestructive snapshot restore
Upgrade JobGenerated upgrade ServiceAccountProjected JWTRolling or BlueGreen orchestration

Changing JWT transport does not merge these identities. Each actor retains its own ServiceAccount, role, audience, and policy.

Select the controller credential

For new clusters that use self-init OIDC, set spec.controllerJWTMode: Target. This configuration fragment selects short-lived credentials for the controller; keep your human access requests in spec.selfInit.requests.

spec:
  controllerJWTMode: Target
  selfInit:
    enabled: true
    oidc:
      enabled: true

The controller requests a ten-minute Kubernetes ServiceAccount JWT with the sole audience urn:openbao:controller:<metadata.uid>. The token is bound to the controller Pod. The controller caches it in memory until one minute before the API server’s returned expiration. Each cluster UID has a separate credential. Self-init binds the openbao-operator role to this audience and the controller ServiceAccount subject. Raft maintenance and approved policy reconciliation use the selected credential. Executor Jobs retain their existing ServiceAccounts and installation-scoped audiences.

Shared, or an omitted field, preserves the projected installation-wide credential. There is no CRD default or mutating webhook that changes existing objects. An operator upgrade does not migrate OpenBao roles. Use the migration procedure before selecting Target on an initialized cluster. Enforced admission prevents removing Target or changing it to Shared. Authentication failures never trigger shared fallback.

The installation grants create on serviceaccounts/token only for the controller ServiceAccount in the operator namespace. Custom installations must also provide POD_NAME and POD_UID through the Downward API, alongside POD_NAMESPACE and OPERATOR_SERVICE_ACCOUNT_NAME.

Select the JWT transport

In Shared mode, the controller Deployment uses 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.

configure

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-controller

Leave 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. Later policy changes require administrator action. Optional approved policy reconciliation can restore built-in policy contents within an administrator-approved grant. Auth methods and JWT roles remain administrator-managed after initialization.

Configure a manual controller role

Use manual JWT configuration only for a controlled bootstrap or a custom install that cannot use self-init OIDC. The following example uses Shared mode. Render the operator installation first, then substitute the actual namespace, ServiceAccount, and audience. For Target mode on an initialized cluster, follow the migration procedure instead.

configure

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=true

The controller policy needs the steady-state maintenance paths:

configure

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:

  1. The rendered controller ServiceAccount name and operator namespace.
  2. The selected controllerJWTMode; omission means Shared.
  3. For Shared mode, the projected audience and OPENBAO_JWT_AUDIENCE; for Target, the cluster UID audience and token issuance RBAC.
  4. The JWT role’s bound_audiences and bound_subject.
  5. OpenBao’s reachability to the configured OIDC discovery or JWKS endpoint.
  6. The separate ServiceAccounts and JWT roles generated for lifecycle Jobs.

Troubleshoot authentication

SymptomLikely causeCheck first
Controller receives permission denied immediatelyAudience or bound-subject mismatchRendered controller identity and JWT role
Self-init finishes but operator auth does not settleDiscovery or JWKS cannot be reached or validatedIssuer, discovery URL, CA, and network path
Backup or restore auth fails while the controller worksJob-specific identity or role is missingThe Job’s ServiceAccount and JWT role
A custom namespace or prefix breaks authOpenBao still trusts the default subjectAll rendered identity references and bound_subject

If JWT login succeeds but a specific request is denied, continue with operator authorization.

Search the handbook

Try “install”, “threat model”, or “compatibility”.