OpenBao — Secrets Management & Encryption

Published

July 7, 2026

Modified

July 8, 2026

Keywords

Security, SSH

Overview

OpenBao is an open-source secrets management and encryption-as-a-service platform. It originated as a community fork of HashiCorp Vault after its license changed from MPL 2.0 to the BUSL, making commercial use without a paid agreement restrictive.

OpenBao provides a unified interface for accessing secrets — passwords, OAuth tokens, API keys, certificates, and more — with tight access control. It encrypts sensitive data at rest and supports dynamic secrets, data leasing, renewal, and revocation.

Why Use It

Without a centralized system, secrets live scattered across config files, environment variables, and developer machines. OpenBao consolidates them into one encrypted store with audit logging and fine-grained access control.

The core benefit is eliminating long-lived distributed secrets:

  • Dynamic credentials — databases, Kubernetes, and other services get short-lived credentials generated on demand that auto-expire, removing manual rotation
  • SSH certificate signing — users generate local key pairs signed by a central CA, producing time-limited certificates instead of distributing static SSH keys
  • Encryption-as-a-service — applications encrypt and decrypt data without managing keys themselves, useful for compliance without building PKI infrastructure
  • Limited blast radius — short-lived, renewable tokens with parent-child relationships mean revoking a parent invalidates all children, containing compromises

Project Health

OpenBao is a genuinely well-governed open-source project, not a vanity fork1.

It operates under MPL 2.0 — an OSI-approved license with no hosting restrictions — and is governed by the Linux Foundation (moved from LF Edge to the OpenSSF sandbox in May 2025)2. A Technical Steering Committee with members from ControlPlane, SAP, Adfinis, Wallix, IOTech, and GitLab oversees development through working groups and a three-tier contributor structure with supply-chain security safeguards3.

The community has ~380 contributors, 6,200+ GitHub stars, and 26 releases since its November 2023 inception4. Commercial supporters including ControlPlane, SAP (via its EU-funded ApeiroRA initiative)5, Adfinis, and recently Nvidia6 back it with the same model that made Kubernetes successful: companies that profit from supporting the project contribute maintainers back to it.

Gaps relative to Vault Enterprise remain: no disaster recovery or performance replication, no Sentinel policies (CEL-based policies are planned), and no managed SaaS offering. Community channels replace vendor support SLAs. The ecosystem is younger with fewer production war stories, but the trajectory is clear.

Getting Started

Authenticate

Connect to an existing server, and authenticate using the root token or a configured auth method:

export BAO_ADDR='https://your-server:8200'
export BAO_CACERT='/path/to/ca.crt'
Variable Purpose
BAO_ADDR URL of the OpenBao server API endpoint; default is http://127.0.0.1:8200
BAO_CACERT Path to the CA certificate that signed the server’s TLS certificate; required for identity verification

The following command performs OIDC (OpenID Connect) authentication and stores the resulting token in BAO_TOKEN for all subsequent CLI calls. The $(...) subshell captures the token from bao login7.

export BAO_TOKEN=$(bao login -method=oidc -path=oidc -token-only)
Flag Purpose
-method=oidc Use the OIDC authentication method
-path=oidc Mount path of the OIDC auth backend on the server
-token-only Suppress verbose output; print only the raw token

Status

Verify the installation:

bao status
Key                     Value
---                     -----
#…
Initialized             true
Sealed                  false
#…
HA Mode                 active
#…
Field Expected Meaning
Initialized true Server is ready; false means storage is not prepared
Sealed false Server can serve requests; true means all commands will fail
HA Mode active This node is serving; standby means point BAO_ADDR elsewhere

If the server is sealed or uninitialized, an administrator must initialize and unseal it first.

Namespaces

Namespaces provide secure multi-tenancy within a single OpenBao instance. Each namespace is an isolated environment with its own secrets engines, authentication methods, policies, and identities. If your token has delegated permissions, you can create your own namespace and act as its administrator8.

Create a new namespace:

bao namespace create v.penso

To operate inside it, set the namespace context so all subsequent commands target that tenant:

export BAO_NAMESPACE=v.penso
# or pass it per-command
bao -namespace=v.penso status

Inside your namespace, you have full control to mount secrets engines, define policies, and configure AppRole authentication without affecting other tenants.

Access Model

Every operation in OpenBao passes through three layers. An authentication method first verifies identity — such as Kubernetes, AppRole, or OIDC — and consults a role to decide which policies to attach and how long the resulting token lives. The issued token carries those policies and must be presented with every subsequent request. Finally, policies enforce path-based access control, determining whether the request is allowed on the target path.

Roles

A role is a named configuration template that defines what an operation is allowed to produce. Roles exist in two contexts, and they are unrelated to each other9.

  • Auth roles — map an authenticated identity to policies and token constraints. When a pod or machine logs in via Kubernetes, AppRole, or another auth method, OpenBao looks up the role to decide which policies to attach and how long the resulting token lives
  • Secrets engine roles — define rules for secrets generation. The SSH engine uses roles to determine which users can be signed, which extensions are granted, and how long certificates remain valid

In both cases, the role acts as a gatekeeper: it constrains what can be issued before anything is created. Without a matching role, authentication succeeds but no token is returned, or a signing request is rejected outright.

Policies

OpenBao uses path-based access control lists (ACLs) written in HCL or JSON. Policies are deny-by-default, meaning an empty policy grants no permissions. Every action maps to a path and a capability.

Capability HTTP Verb Action
create POST/PUT Create new data at a path
read GET Read data at a path
update POST/PUT Modify existing data
patch PATCH Partial updates
delete DELETE Remove data
list LIST List keys under a path
sudo Access root-protected endpoints
deny Explicitly block access; always takes precedence

Two built-in policies exist10:

  • default — attached to every token by default; grants minimal system access
  • root — unrestricted access; should never be used in production

To verify what your current token can do on a specific path without triggering a 403 error:

bao sys capabilities -path=secrets/database/

Tokens

Tokens are the primary authentication mechanism in OpenBao. Every request carries a token, which is validated against attached policies to determine what actions are allowed11. Two parameters control token lifetime:

Parameter Effect
TTL Time-to-live from creation. When it reaches zero, the token expires and requests fail
Max TTL Upper bound. A renewable token can be extended via renewal, but never beyond this ceiling. Set both to 0 for a non-expiring token

A token is either renewable or not. Renewable tokens must be actively renewed by the holder before expiry, typically using bao token renew. If renewal is missed, the token and all its children expire silently.

Beyond the standard parent-child model, three types exist for specific use cases:

Type Behavior
Orphan Has no parent token. Revoking another token never affects it. Useful when independent lifecycle is needed
Periodic Never truly expires as long as it is renewed before each TTL window. Commonly used for long-lived service accounts
Batch Short-lived, non-renewable, cached server-side. Returned by auth methods like Kubernetes or AWS. Cannot create child tokens

Creation

Create a token with token create12:

bao token create -ttl=24h
Key                  Value
---                  -----
token                s.t01ozKccP69Q1PxmLReQ3ept.MIr2Kh
token_accessor       5jQ6WzRdP9auOVjB03uehg6s.MIr2Kh
token_duration       24h
token_renewable      true
#…
Flag Purpose
-ttl Time-to-live; accepts durations like 15m, 24h, 720h

Fields in the command output

Field Meaning
token The credential to present on every request via BAO_TOKEN
token_accessor Handle for auditing and management; can look up or revoke, cannot impersonate
token_duration Remaining time-to-live before expiry
token_renewable Whether the token can be renewed before it expires

Lookup

Inspect a specific token to see its policies, remaining lifetime, and other attributes13:

bao token lookup s.t01ozKccP69Q1PxmLReQ3ept.MIr2Kh
Key                 Value
---                 -----
#…
creation_ttl        24h
#…
num_uses            0
#…

From a user’s perspective, five fields matter most:

Field Meaning
creation_ttl Remaining time-to-live; decrements toward zero. When it reaches zero, the token expires and requests fail
num_uses Remaining use count before invalidation; 0 means unlimited uses

Renewal

Extend a renewable token’s life before it expires14:

bao token renew $token

Renewal resets the remaining TTL but never exceeds the token’s max TTL. If the current remaining TTL plus the original TTL would surpass the max TTL, the result is capped at the max. Periodic tokens reset to their full TTL on each renewal.

Revocation

Revoke a token to immediately invalidate it and all of its children15:

bao token revoke $token

Use the accessor instead of the token value when you want to revoke without possessing the credential itself:

bao token revoke-accessor $accessor

Revoking a parent token cascades to all descendants. Orphan tokens have no parent, so they are only affected by direct revocation.

Authentication

Before OpenBao issues a token, it must verify who you are. Auth methods define how that verification happens16. Some methods delegate identity to an external system you already trust. Others keep everything self-contained within OpenBao.

Three categories cover the options:

  • Platform-native — Infrastructure attests identity directly. No secrets to distribute.
    • Kubernetes …pods present a service account token validated against the K8s API
    • AWS / GCP / Azure …workloads use cloud IAM credentials from instance metadata
  • External identity provider — OpenBao delegates to a system that already manages users.
    • LDAP …binds against an LDAP server such as Active Directory or OpenLDAP
    • RADIUS …challenges a RADIUS server for network infrastructure authentication
    • Kerberos …validates a TGT against a Kerberos KDC for enterprise SSO
    • JWT / OIDC …verifies a signed JWT from a trusted issuer, useful for CI/CD pipelines
  • Self-contained — OpenBao handles identity itself.
    • Certificate (TLS) …client certificate in mutual TLS, for environments with existing PKI
    • Userpass …username and password managed within OpenBao, simplest for developer workstations
    • AppRolerole_id plus secret_id exchanged at login, for bare metal and VMs

Platform-native methods are preferred where available since they eliminate secret distribution entirely. External providers reuse identity you already manage. Self-contained methods work anywhere but require you to provision and protect credentials yourself.

Kubernetes

Kubernetes authentication eliminates secret distribution. A pod presents its service account token; OpenBao validates it against the Kubernetes API server and, if the token matches a configured role, issues a short-lived token17.

The service account token takes the form of a JWT18 (JSON Web Token): three Base64-encoded sections — header, payload, and signature — joined by dots. The header declares the signing algorithm. The payload carries claims, key-value pairs that identify the holder. The signature proves the issuer and guards against tampering.

OpenBao does not verify the JWT itself. Instead, it forwards the token to the Kubernetes TokenReview API19, which checks the signature and returns the pod’s identity — service account name, namespace, and groups. OpenBao then matches that identity against its configured roles. The exchange follows four steps:

  1. The pod sends its JWT to OpenBao
  2. OpenBao calls the TokenReview API using its own service account token
  3. The API server validates the JWT and returns the pod’s identity
  4. OpenBao matches the identity against a role and issues a token

OpenBao never trusts the pod. It defers to Kubernetes. Delete the service account or let the token expire, and authentication fails immediately.

Configure

Mount the auth method:

bao auth enable kubernetes

Point OpenBao at your Kubernetes cluster so it can verify tokens20:

bao write auth/kubernetes/config \
  token_reviewer_jwt="$SERVICE_ACCOUNT_JWT" \
  kubernetes_host="https://$KUBERNETES_API_HOST:443" \
  kubernetes_ca_cert="@/path/to/ca.crt"
Parameter Purpose
token_reviewer_jwt Service account token OpenBao uses to call the K8s TokenReview API
kubernetes_host HTTPS address of the Kubernetes API server
kubernetes_ca_cert CA certificate that signed the API server’s TLS certificate

The service account token belongs to a dedicated OpenBao service account with the token-reviewer ClusterRole bound to it. OpenBao needs this only to validate pod tokens, not to read secrets or manage resources.

Create Role

Define a role that maps Kubernetes identities to OpenBao policies21:

bao write auth/kubernetes/role/web-server \
  bound_service_account_names=web-server \
  bound_service_account_namespaces=production \
  token_ttl=15m \
  token_max_ttl=30m \
  policies=read-secrets
Parameter Effect
bound_service_account_names Service account name the pod must use; * allows any
bound_service_account_namespaces Namespace the pod must run in; * allows any
token_ttl Lifetime of the token returned after a successful login
token_max_ttl Upper bound; the token cannot be renewed beyond this
policies Policies attached to the resulting token

Tighten the binding by constraining both name and namespace. A pod in staging with the same service account name will be rejected if the role specifies production.

Login

Inside the pod, authenticate using the projected service account token22:

export BAO_TOKEN=$(\
  bao write -format=json -field=token \
    auth/kubernetes/login \
    role=web-server \
    jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token \
)
Argument Purpose
role=web-server Role name defined on the OpenBao side
jwt=@... Path to the projected service account token inside the pod

The token file at /var/run/secrets/kubernetes.io/serviceaccount/token is automatically mounted by Kubernetes when using projected service account tokens with a short TTL (e.g., 1 hour). OpenBao validates it against the API server. If the service account name and namespace match the role’s bounds, a token is returned.

No secrets are distributed to the pod. The platform proves who it is.

AppRole

AppRole splits identity into two halves and inserts a login step. One half is semi-public, the other is a constrained secret. Mount the auth method first23:

bao auth enable approle
Success! Enabled approle auth method at: approle/

Create

Create a role that defines what the resulting token can do:

bao write auth/approle/role/web-server \
  secret_id_ttl=1h \
  token_ttl=15m \
  token_max_ttl=30m \
  policies=read-secrets
Success! Data written to: auth/approle/role/web-server
Parameter Effect
secret_id_ttl How long a secret_id remains valid before login is refused
token_ttl Lifetime of the token returned after a successful login
token_max_ttl Upper bound; the token cannot be renewed beyond this
policies Policies attached to the resulting token

List

List all configured AppRoles under the mount point24:

bao list auth/approle/role/
Keys
----
web-server

The command returns only role names, never credentials or token values. Your token must have the list capability on auth/approle/role/* — having read alone is not enough.

Read a specific role to inspect its constraints:

bao read auth/approle/role/web-server
Key                        Value
---                        -----
bind_secret_id             true
#…
policies                   [read-secrets~]
#…
secret_id_num_uses         0
secret_id_ttl              1h
#…
token_max_ttl              30m
#…
token_period               0s
#…
token_ttl                  15m
#…
Field Meaning
bind_secret_id secret_id is required for login; disable for advanced workflows
policies Policies attached at creation time (the ~ suffix indicates identity-derived policies)
secret_id_num_uses How many times a secret_id can be used before expiring; 0 means unlimited
secret_id_ttl Lifetime of a generated secret_id before it expires
token_max_ttl Upper bound for the resulting token; renewal cannot extend beyond this
token_period If non-zero, the token is periodic and resets to this value on each renewal
token_ttl Initial lifetime of the token returned after login

Role & Secret

Get the semi-public identifier — safe to embed in images or repositories:

bao read auth/approle/role/web-server/role-id
Key        Value
---        -----
role_id    3ef4698e-6875-5d2e-9f80-0c9d65f46a81

Get the secret credential

bao write -f auth/approle/role/web-server/secret-id
Key                   Value
---                   -----
secret_id             e39be171-63ba-cfd0-c444-043554a27b97
secret_id_accessor    02e07d94-ac16-0ba7-92b8-26d900920446
secret_id_num_uses    0
secret_id_ttl         1h

The secret_id is the actual credential and should be delivered separately — by your orchestrator, CI pipeline, or cloud-init — ideally response-wrapped so only the intended machine can unwrap it exactly once.

At boot, the machine exchanges both halves for a short-lived token:

export BAO_TOKEN=$(\
        bao write -format=json -field=token \
            auth/approle/login role_id=$role_id secret_id=$secret_id \
)

Secrets Engines

A secrets engine is a pluggable module that defines how OpenBao manages data. Every action in OpenBao happens inside an engine, and each engine is mounted at its own path25.

Common engines26:

Engine Function
Cubbyhole Per-token private secret storage; only the token that wrote it can read it
Databases Dynamically generates short-lived credentials for supported databases
Identity Internal store for entities, groups, and identity-based access control
Key/Value (KV) Stores arbitrary static data such as passwords and API keys
Kubernetes Generates Kubernetes service account tokens on demand
LDAP Generates dynamic LDAP credentials
PKI Acts as a certificate authority to issue and sign X.509 certificates
RabbitMQ Generates dynamic RabbitMQ credentials
SSH Signs or issues SSH certificates for dynamic host and user authentication
System Internal endpoints for control, policy, and debugging
TOTP Generates one-time passwords for multi-factor authentication
Transit Provides encryption-as-a-service without storing your underlying data

KV Store

A KV (Key-Value) store is a secrets engine for storing arbitrary static data such as passwords, API keys, or configuration values27. The current standard is KV v2, which tracks changes over time. Enable a KV v2 store at a custom path28:

bao secrets enable -path=secrets -version=2 kv
Success! Enabled the kv secrets engine at: secrets/
Component Purpose
-path=secrets The mount point where the engine will be accessible
-version=2 Explicitly requests KV version 2
kv The type of secrets engine being enabled

Every write creates a new version, allowing you to inspect history or roll back to a previous state without losing data. Deletes are soft by default, meaning removed secrets can be recovered.

Write

In KV v2, every write creates a new version. You can inspect the version history or roll back to a previous version without losing data. Write a secret with kv put, paths follow the pattern <mount-path>/<key>:

bao kv put secrets/database username=admin password=s3cret
==== Secret Path ====
secrets/data/database

======= Metadata =======
Key                Value
---                -----
created_time       2026-07-07T06:43:44.146010062Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1
Component Purpose
secrets/database The mount point and key for the secret
username=admin… Key-value pair to store; multiple pairs can be added space-separated

Two things stand out in the output:

  • Secret path …KV v2 stores actual data under a /data/ subpath (e.g., secrets/data/database). Policies must explicitly grant access to secrets/data/*, or reads and writes will fail with a 403.
  • Metadata …OpenBao tracks the lifecycle of every secret. The version increments on each write for rollbacks. deletion_time shows when a soft delete occurred (n/a means active). If destroyed is true, the version is permanently erased and cannot be recovered.

List

List secrets in a path29:

bao kv list secrets/
----
database

The command returns only key names, never secret values or version information. It lists immediate children of the given path, so nested keys require an explicit prefix to traverse deeper. Your token must have the list capability in its policy — having read or create alone is not enough.

Read

Retrieve the current version of a secret with30:

bao kv get secrets/database
#…
====== Data ======
Key         Value
---         -----
password    s3cret
username    admin
Component Purpose
secrets/database The mount point and key to retrieve

The output is divided into two sections. Data contains the actual key-value pairs you stored. A second Metadata section (omitted above for brevity) provides the version number, creation time, and deletion status.

# Read a specific previous version
bao kv get -version=1 secrets/database

# Output as JSON for programmatic parsing
bao kv get -format=json secrets/database

# Extract a single field directly
bao kv get -field=password secrets/database

Delete

In KV v2, deleting a secret does not immediately erase it from storage. Instead, it performs a soft delete, which marks the latest version as deleted while preserving the history for potential recovery31.

# Soft delete the latest version (sets deletion_time, recoverable)
bao kv delete secrets/database

# Restore a specific deleted version back to active status
bao kv undelete -version=1 secrets/database

# Permanently erase versions 1 through N (sets destroyed to true)
bao kv destroy -versions=1-3 secrets/database

# Hard delete all versions at once (leaves no trace)
bao kv delete -delete-version=0 secrets/database

SSH

The SSH secrets engine signs SSH public keys or generates one-time passwords for authenticating to remote machines32. Unlike static credentials, signed certificates expire automatically and can be revoked centrally.

Mount the SSH secrets engine at a custom path33:

bao secrets enable -path=ssh-signer ssh
Success! Enabled the ssh secrets engine at: ssh-signer/

Separate mount paths are recommended for client and host key signing to isolate their CAs and roles.

Configure CA

Generate a signing key pair that OpenBao will use to sign certificates34:

bao write ssh-signer/config/ca generate_signing_key=true
Key             Value
---             -----
#…
public_key      ssh-rsa AAAAB3NzaC1yc2E...

The public key must be trusted by target hosts so they accept signed certificates. Distribute it to each server’s SSH configuration:

curl -o /etc/ssh/trusted-user-ca-keys.pem \
        $BAO_ADDR/v1/v.penso/ssh-signer/public_key
Argument Purpose
$BAO_ADDR OpenBao server address and port from your environment
/v1/ API version prefix
v.penso/ Namespace segment; required because raw HTTP calls don’t inherit BAO_NAMESPACE
ssh-signer/ Mount path where the SSH secrets engine was enabled
public_key Endpoint that returns the signing CA’s public key without authentication

Then add the following to /etc/ssh/sshd_config on each host:

TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem

Restart the SSH service after updating the configuration.

Create Role

Define a role that controls which users and extensions are allowed on signed certificates35:

bao write ssh-signer/roles/default -<<EOF
{
  "allowed_users": "*",
  "allow_user_certificates": true,
  "default_extensions": {
    "permit-pty": "",
    "permit-port-forwarding": ""
  },
  "key_type": "ca",
  "ttl": "30m"
}
EOF
Success! Data written to: ssh-signer/roles/default
Field Purpose
allowed_users Usernames permitted in signed certificates; * allows any user
ttl Certificate lifetime; shorter values reduce exposure if a key is compromised

The default_extensions object controls capabilities granted to signed certificates:

Extension Effect
permit-pty Interactive shell access
permit-port-forwarding SSH tunneling and port forwarding
permit-agent-forwarding SSH agent forwarding
permit-X11-forwarding X11 display forwarding
permit-user-rc Execution of ~/.ssh/rc on the server

Sign Key

Generate a SSH key pair36:

ssh-keygen -t rsa -f /tmp/ssh-demo -N "" -C "demo@example.com"
Generating public/private rsa key pair.
Your identification has been saved in /tmp/ssh-demo
Your public key has been saved in /tmp/ssh-demo.pub
#…
Flag Purpose
-t rsa Key algorithm; rsa is widely supported by older SSH servers
-f /tmp/ssh-demo Output file path for the private key; public key gets a .pub suffix automatically
-N "" Set an empty passphrase so SSH prompts are skipped during the demo
-C "…" Free-form comment attached to the public key for identification

Request OpenBao to sign the public key37:

# Redirect output to disk
bao write -field=signed_key ssh-signer/sign/default \
  public_key=@/tmp/ssh-demo.pub > /tmp/ssh-demo-cert.pub
Argument Purpose
-field=signed_key Output only the certificate, discarding metadata like the serial number
ssh-signer/sign/default Path to the signing endpoint; default is the role name
public_key=@... The @ prefix tells OpenBao to read the key from a file instead of a raw string

Verify an SSH certificate:

ssh-keygen -Lf /tmp/ssh-demo-cert.pub
/tmp/ssh-demo-cert.pub:
        #…
        Valid: from 2026-07-07T10:27:48 to 2026-07-07T10:58:18
        #…
        Extensions:
                permit-port-forwarding
                permit-pty
Flag Purpose
-L Display detailed information about a public key or certificate
-f Specify the input file path

Relevant fields in the command output:

Field What to Check
Valid Certificate time window; outside this range the host rejects it
Extensions Granted capabilities; missing permit-pty means no interactive shell, missing permit-port-forwarding blocks tunnels

Connect

SSH into the target host using both the signed certificate and the corresponding private key38:

ssh -i /tmp/ssh-demo-cert.pub -i /tmp/ssh-demo username@host

The host validates the certificate signature against its trusted CA. If the principals match and the certificate has not expired, access is granted without password or static key exchange.

Appendix

Tokens vs AppRoles

When machines need to authenticate to OpenBao, two common approaches exist: a static token or the AppRole authentication method. Both result in the machine holding a token that reads from a KV store, but they differ fundamentally in how that token is obtained and what its lifecycle looks like.

Dimension Static Token AppRole
Credential model Single artifact; possession equals access Two halves (role_id + secret_id) exchanged for a token at login
Rotation Manual; requires revoking and redistributing everywhere Automatic; rotate secret_ids through orchestrator without touching machines
Leakage risk Stolen token works immediately from anywhere secret_id can be constrained by TTL, use count, and CIDR; role_id alone is harmless
Token lifetime Often drifts toward long-lived or non-expiring Short-lived by design (e.g., 15m) with periodic re-login
Audit trail Thin; no per-boot event, shared identity across deployments Every boot produces a login event; per-machine granularity
Revocation All-or-nothing; shared tokens affect all holders Per-machine via secret_id accessor, or per-role to revoke everyone
Operational cost Minimal setup, high maintenance over time Higher initial setup, lower ongoing maintenance

Rule of thumb: static token for throwaway or dev environments, AppRole when you have an orchestration layer capable of delivering constrained secret_ids, platform-native auth whenever the infrastructure provides it.

Static Token

An operator creates a token with the appropriate policies and distributes it to the machine via config file, environment variable, or systemd credential. The token is the credential. Whoever holds it has access — there is no login step, no second factor, nothing to constrain. It is the simplest approach and works fine for a lab or a single pet server. Treat it as a static API key.

Consider following drawbacks

  • Rotation is manual and disruptive — Revoke the old token, create a new one, redistribute it everywhere. In practice these tokens drift toward long-lived or non-expiring.
  • Leaks are immediately exploitable — A stolen token works from anywhere unless you explicitly set token_bound_cidrs at creation. This flag restricts the token to requests originating from specific IP ranges (e.g., -token-bound-cidr=10.0.0.0/24). If the token is used outside that range, OpenBao rejects it. Few people bother setting it because it requires stable client IPs and breaks if the machine’s address changes.
  • Audit trail is thin — You see requests from one token accessor, but every deployment of the same machine image reuses the identical identity with no per-boot login event.
  • Renewal burden falls on the machine — The application must renew the token itself, or you accept expiry-and-outage, or you use a non-expiring token, which is worse.

AppRole

AppRole does not eliminate secret zero, it relocates it. Something trusted must still deliver the secret_id. If you take the lazy path — role_id and secret_id sitting in the same config file, secret_id_num_uses=0, generous TTLs — you have rebuilt a static token with more moving parts and gained nothing. The indirection only pays off if you actually enforce constraints and use a separate delivery path.

Why the indirection matters:

  • Constrainable credentials — Set secret_id_num_uses=1 and secret_id_bound_cidrs on the role. A stolen secret_id may already be consumed, expired, or unusable from the attacker’s network. A static token has none of these safety nets.
  • Short-lived tokens by design — A 15m token TTL with periodic re-login means a leaked token ages out fast. Compromise windows shrink from “until someone notices” to minutes.
  • Rotation without redistribution — Continuously rotate secret_ids through your orchestrator. The role_id never changes, so machine images do not need rebuilding.
  • No single point of compromise — No single artifact at rest grants access. An attacker needs both the embedded role_id and the delivery channel for secret_id. With a static token, one file is the whole kingdom.
  • Per-machine audit and revocation — Every boot produces a login event. Revoke one machine’s secret_id accessor without affecting others. With shared static tokens, revocation is all-or-nothing.

When the environment offers native identity, skip AppRole entirely. Cloud providers and Kubernetes expose their own auth methods (aws, gcp, kubernetes, cert, jwt) where the platform attests identity directly and you distribute no secrets at all.

Footnotes

  1. OpenBao GitHub Repository
    https://github.com/openbao/openbao↩︎

  2. OpenBao Joins the OpenSSF
    https://openbao.org/blog/openbao-joins-the-openssf/↩︎

  3. OpenBao Governance and Community Roles
    https://github.com/openbao/openbao/blob/main/GOVERNANCE.md↩︎

  4. OpenBao GitHub Repository
    https://github.com/openbao/openbao↩︎

  5. OpenBao GitHub Organization
    https://github.com/openbao↩︎

  6. Nvidia Adopts OpenBao (TechTarget, Jun 2026)
    https://www.techtarget.com/searchitoperations/news/366644831/Nvidia-adopts-OpenBao-open-source-fork-of-HashiCorps-Vault↩︎

  7. bao login Command Reference
    https://openbao.org/docs/commands/login↩︎

  8. OpenBao Namespaces Documentation
    https://openbao.org/docs/concepts/namespaces/↩︎

  9. OpenBao Roles Documentation
    https://openbao.org/docs/concepts/roles/↩︎

  10. OpenBao Policies Documentation
    https://openbao.org/docs/concepts/policies/↩︎

  11. OpenBao Tokens Documentation
    https://openbao.org/docs/concepts/tokens/↩︎

  12. bao token create Command Reference
    https://openbao.org/docs/commands/token/create↩︎

  13. bao token lookup Command Reference
    https://openbao.org/docs/commands/token/lookup↩︎

  14. bao token renew Command Reference
    https://openbao.org/docs/commands/token/renew↩︎

  15. bao token revoke Command Reference
    https://openbao.org/docs/commands/token/revoke↩︎

  16. OpenBao Auth Methods Documentation
    https://openbao.org/docs/auth/↩︎

  17. OpenBao Kubernetes Auth Documentation
    https://openbao.org/docs/auth/kubernetes/↩︎

  18. JWT Specification (RFC 7519)
    https://datatracker.ietf.org/doc/html/rfc7519↩︎

  19. Kubernetes TokenReview API Reference
    https://kubernetes.io/docs/reference/access-authn-authz/authentication/#tokenreview↩︎

  20. bao write Command Reference
    https://openbao.org/docs/commands/write/↩︎

  21. OpenBao Kubernetes Auth Roles
    https://openbao.org/docs/auth/kubernetes/#roles↩︎

  22. OpenBao Kubernetes Auth Login
    https://openbao.org/docs/auth/kubernetes/#login↩︎

  23. OpenBao AppRole Documentation
    https://openbao.org/docs/auth/approle/↩︎

  24. bao list Command Reference
    https://openbao.org/docs/commands/list↩︎

  25. OpenBao Secrets Engines Documentation
    https://openbao.org/docs/secrets/↩︎

  26. Full list of secrets engines
    https://openbao.org/docs/secrets/↩︎

  27. OpenBao KV Secrets Engine Documentation
    https://openbao.org/docs/secrets/kv/↩︎

  28. bao secrets enable Command Reference
    https://openbao.org/docs/commands/secrets/enable/↩︎

  29. bao kv list Command Reference
    https://openbao.org/docs/commands/kv/list↩︎

  30. bao kv get Command Reference
    https://openbao.org/docs/commands/kv/get↩︎

  31. bao kv delete Command Reference
    https://openbao.org/docs/commands/kv/delete↩︎

  32. OpenBao SSH Secrets Engine Documentation
    https://openbao.org/docs/secrets/ssh/↩︎

  33. bao secrets enable Command Reference
    https://openbao.org/docs/commands/secrets/enable/↩︎

  34. bao write Command Reference
    https://openbao.org/docs/commands/write/↩︎

  35. Signed SSH Certificates Guide
    https://openbao.org/docs/secrets/ssh/signed-ssh-certificates/↩︎

  36. ssh-keygen Manual
    https://man.openbsd.org/ssh-keygen↩︎

  37. bao ssh Sign Command Reference
    https://openbao.org/docs/commands/ssh/sign↩︎

  38. OpenSSH Certificate Authentication
    https://man.openbsd.org/ssh↩︎