Vault¶
DRP's Integration with HashiCorp Vault¶
Digital Rebar Provision (DRP) integrates with HashiCorp Vault to extend its secret management capabilities. This integration enables users to securely retrieve secrets from Vault, enhancing DRP's native secret management options.
Plugin Configuration¶
These steps are necessary to use the Vault plugin. Configure the Vault plugin with the following parameters:
Upgrading from prior versions¶
Prior versions of this plugin set AutoStart: true, which caused DRP to auto-create an unconfigured vault plugin instance at install time. That instance was immediately broken (no vault/address) and silently captured vault:// URI dispatch.
After upgrading:
- If you have a broken
vaultinstance with empty params, destroy it:drpcli plugins destroy vault. - If you have a configured
vaultinstance, no action needed. -
For new installs, create the instance explicitly:
drpcli plugins create '{"Name":"vault","Provider":"vault","Params":{...}}'. -
vault/address: The address of the Vault server. Ensure it follows the formathttp(s)://<vault-server-address>:<port>. vault/auth-method: How the plugin authenticates to Vault. One oftoken(default),approle, orjwt. See the Authentication section below.vault/token: The authentication token used to connect to Vault. Required whenvault/auth-methodistoken. Learn more about Vault authentication tokens.vault/approle-role-id: The AppRole role ID. Required whenvault/auth-methodisapprole.vault/approle-secret-id: The AppRole secret ID (secure param). Required whenvault/auth-methodisapprole.vault/approle-mount: The mount path for the AppRole auth backend in Vault. Defaults toapprole(Vault's stock mount path). Only relevant whenvault/auth-methodisapprole.vault/jwt-role: The Vault role to authenticate as via JWT. Required whenvault/auth-methodisjwt.vault/jwt: The JWT to present at Vault login (secure param). Required whenvault/auth-methodisjwt.vault/jwt-mount: The mount path for the JWT auth backend in Vault. Defaults tojwt. Only relevant whenvault/auth-methodisjwt.vault/token-short-lived: Indicates that the Vault token is short-lived and should be refreshed on every call. This is useful when the token is backed by a different secret store or is rotated on the plugin often. The default value isfalse. Only honored whenvault/auth-methodistoken; see Authentication for the interaction with non-token methods.vault/kv-version: The version of Vault KV secrets engine while running in versioned mode. If none is provided it will default tov2.vault/kv-mount: The mount path for the KV secrets engine. Defaults tosecretfor KV v2 andkvfor KV v1.vault/insecure-tls: (Optional) Set totrueto disable TLS verification. This is useful when using self-signed certificates. The default value isfalse.vault/ca-cert: (Optional) The string form of a PEM format CA certificate file.
Optionally, you can configure a cache timeout, which allows DRP to temporarily store secrets in memory. This minimizes the number of requests made to the Vault server and improves performance. The duration is specified in seconds, with a default of 300 seconds.
vault/cache-timeout: The duration (in seconds) to cache secrets in memory.
Authentication¶
The plugin supports three authentication methods, dispatched by the vault/auth-method param:
Token (vault/auth-method: token, default)¶
The plugin uses the value of vault/token as the Vault client token. Suitable for development and for deployments where a long-lived token (root or otherwise) is acceptable.
AppRole (vault/auth-method: approle)¶
The plugin authenticates via Vault's AppRole auth backend, exchanging a role-id + secret-id pair for a Vault token. Suitable for production deployments where per-application credentials and least-privilege policies are required.
- On
Config(), the plugin callsauth/<vault/approle-mount>/loginwith the configured role-id and secret-id; the issued token becomes the in-memory token used for all subsequent operations. vault/tokenis NOT required whenvault/auth-methodisapprole; the plugin manages the token lifecycle itself.- The issued token's renewability and TTL are discovered via
LookupSelfand fed into the same auto-renewal path used for static tokens (see Token Auto-Renewal). - If the auto-renew watcher terminates (e.g., on
max_ttl, token revocation, or after grace-period exhaustion), the plugin transparently re-logs in with the cached role-id + secret-id to mint a fresh token. The re-login is bounded to 6 attempts (one immediate, then 5 retries spaced 5s, 30s, 2m, 5m, 10m apart; total budget ~17 minutes); if all attempts fail, the final error is surfaced viaPluginErrors. -
The AppRole's policy must include read access to the KV paths the plugin will fetch. A least-privilege example for secrets stored under a
drp/prefix:Terraformpath "secret/data/drp/*" { capabilities = ["read"] } path "secret/metadata/drp/*" { capabilities = ["read"] }The plugin does NOT require read access to the kv-mount root (
secret/datawithout a subpath). The Config()-time validation step usesauth/token/lookup-selfinstead, which Vault's built-in default policy grants to every non-root token. This lets operators scope their AppRole policies to exactly the secrets the plugin needs.
JWT (vault/auth-method: jwt)¶
The plugin authenticates via Vault's JWT auth backend, exchanging a JWT for a Vault token. Suitable for deployments where an identity provider Vault already trusts issues short-lived JWTs that DRP can present (OIDC IDPs like Auth0/Keycloak, Kubernetes API servers that the customer's Vault is federated with, etc.).
The plugin is a JWT consumer, not a JWT minter. Whatever identity source you use is responsible for producing the JWT and updating vault/jwt when rotation is needed; the plugin presents whatever is currently in vault/jwt at each login.
Lifecycle¶
[your rotation source] writes JWT -> vault/jwt
|
loginJWT reads at each call
|
v
Plugin login -> Vault
|
v
Vault token
(cached + auto-renewed
via LifetimeWatcher)
|
v
Used for actual secret fetches
until renewal can no longer
keep it alive, then loop
A login happens once at Config() and again whenever the auto-renewed Vault token can no longer be kept alive (typically at token_max_ttl). The JWT is consumed at those login moments only; secret fetches in between use the cached Vault token. So the operator's JWT rotation cadence matches the JWT's TTL (set by your identity source), not the Vault token's TTL.
- On every login call, the plugin reads the current
vault/jwtvalue from this plugin instance's params via the DRP API, then callsauth/<vault/jwt-mount>/loginwith{"role": <vault/jwt-role>, "jwt": <current vault/jwt>}. This re-read happens on every login attempt, so an external rotator's update tovault/jwtis observed at the next natural re-login without needing a plugin reconfig. vault/tokenis NOT required whenvault/auth-methodisjwt; the plugin manages the Vault token's lifecycle itself.vault/jwtmust be set at Config() time even though it is read fresh at each login; the Config()-time check makes a misconfigured plugin fail fast rather than at the first secret fetch.- Like AppRole, the issued Vault token is auto-renewed; when the watcher terminates the plugin re-logs in with the same bounded retry budget (6 attempts: one immediate, then 5 retries spaced 5s, 30s, 2m, 5m, 10m apart; total budget ~17 minutes). Each retry re-reads
vault/jwt, so if rotation completes mid-failure the plugin recovers automatically. - Vault's "Kubernetes" auth method shares the
role + jwtlogin payload with the generic JWT method; setvault/jwt-mountto your Kubernetes auth mount (typicallykubernetes) to use it. This is the natural fit when your JWT source is a Kubernetes cluster.
Where does the JWT come from?¶
The plugin does not care; it just consumes vault/jwt. Realistic sources:
| Source | How vault/jwt gets populated |
|---|---|
| Corporate OIDC IDP that Vault is federated with | The IDP's tooling pushes a fresh JWT into vault/jwt on its rotation cadence (via drpcli plugins set or the DRP API) |
| A Kubernetes cluster Vault trusts | A scheduled job (CronJob inside the cluster, or a DRP task on a worker with cluster credentials) calls oc create token <sa> --audience=vault and pushes the result into vault/jwt |
| A long-lived static JWT signed by something Vault trusts | Operator pastes it once; rotated infrequently (annually or on policy change) |
Bootstrap is whichever of the above your environment already has. The plugin's contract starts after a JWT exists somewhere; producing the first one is the operator's problem, not the plugin's.
Role configuration¶
Least-privilege role configuration example for a Kubernetes service account named drp-vault in the drp namespace, presenting tokens with audience vault:
vault write auth/jwt/role/drp-vault \
role_type=jwt \
bound_audiences=vault \
bound_subject=system:serviceaccount:drp:drp-vault \
user_claim=sub \
token_policies=drp-vault \
token_ttl=1h \
token_max_ttl=4h
The drp-vault policy itself only needs the capabilities the plugin actually exercises against your KV paths (read for fetches, plus write/create if DRP is the source of truth for any secret it stores, such as a cluster's kubeconfig during install).
Compatibility with vault/token-short-lived¶
vault/token-short-lived: true is incompatible with any non-token auth method (approle, jwt). Short-lived asks the plugin to re-read vault/token from DRP params on every fetch (the customer's external rotator owns the token lifecycle), while AppRole and JWT ask the plugin to manage the token lifecycle itself via login + renewal + re-login. If both are configured, the plugin logs a warning and surfaces a PluginErrors entry describing the conflict, then runs as the configured non-token method only (short-lived is silently disabled in memory).
Token Auto-Renewal¶
The plugin uses the Vault Go SDK's LifetimeWatcher to keep renewable tokens alive in the background. On Config(), the plugin calls Vault's LookupSelf on the configured token to determine its renewability and TTL:
- Renewable tokens with a positive TTL are renewed automatically at Vault-driven cadence (roughly half the TTL with jitter) up to their max_ttl.
- Non-renewable tokens (e.g., the root token) skip auto-renewal with an info-level log entry.
- If renewal terminates because the token can no longer be kept alive, the plugin writes a descriptive entry to its
PluginErrorsfield so the failure is visible to operators viadrpcli plugins show. The classification is determined by a post-mortemLookupSelfand falls into one of:renewal call failed: ...— the renewal call returned an error directly (requires a non-defaultRenewBehavior).token unreadable post-renewal: ...— the token is gone or rejected; typical aftermath of revocation.max_ttl reached or token expired— Vault still knows the token but its TTL is zero or negative.grace period exhausted with <ttl> TTL remaining— the watcher gave up while the token was still notionally valid, almost always because transient renewal errors ate the grace window.
Auto-renewal is compatible with vault/token-short-lived. The two mechanisms are orthogonal: auto-renewal extends the in-memory token through its max_ttl; short-lived re-reads the vault/token param on every fetch so an external rotator can swap in a fresh token across max_ttl boundaries. Both flags may be set simultaneously.
Operational Configuration¶
Beyond configuring the plugin, you need to set up a lookupUri to specify the location of the secret in Vault:
decrypt/lookup-uri:-
Specifies where the secret is stored
- Format:
<plugin-name>://<key-to-lookup>?path=<path-to-secret> -
In this case, we are using the
vaultplugin. Let's assume we have a secret at pathhellowith keyfoo: -
Example URI will be:
vault://foo?path=hello
- Format:
-
The URI can also have parameter expansions.
Path Formatting Based on Vault KV Secrets Engine Version¶
-
For KV Version 1:
- Use the full path, e.g., for
kv/my-secret, the path iskv/my-secret.- Details about KV version 1: https://developer.hashicorp.com/vault/docs/secrets/kv/kv-v1
- Use the full path, e.g., for
-
For KV Version 2:
- Exclude
/secretfrom the path. For a secret stored at/secret/foo/creds, the path should be/foo/creds.- Details about KV version 2: https://developer.hashicorp.com/vault/docs/secrets/kv/kv-v2
- Exclude
Unsure which version your Vault secrets engine is using? Run
vault secrets list -detailedto find out. Look for the Options column. - If it showsversion: "2", it's KV version 2. - If the Options column is empty or does not mention a version, it's likely KV version 1.Here is an example output:
Usage¶
Assuming that a secret named
fooexists at thehellopath in Vault. Check Vault Docs. Also assuming that a machine namedvault-testexists in DRP.
Set the secret:
drpcli machines set Name:vault-test param ipmi/password to '{ "LookupUri": "vault://foo?path=hello" }'
Retrieve the secret: