Skip to content

HashiCorp Vault

The vault lease backend reads dynamic secrets from a HashiCorp Vault secret engine. This works with any Vault dynamic secret backend — AWS, database, PKI, etc. You configure which Vault response fields map to which environment variables.

Configuration

toml
[leases.vault-db]
type = "vault"
secret_path = "database/creds/my-role"
duration = "1h"

[leases.vault-db.env_map]
username = "DB_USER"
password = "DB_PASSWORD"
FieldRequiredDescription
secret_pathYesVault API path for the dynamic secret
env_mapYesMap of Vault response field names to environment variables
addressNoVault server URL (falls back to FNOX_VAULT_ADDR, then VAULT_ADDR)
tokenNoVault auth token (falls back to FNOX_VAULT_TOKEN, then VAULT_TOKEN)
credential_commandNoShell command that prints a Vault token when no token is configured
namespaceNoVault namespace for Vault Enterprise / HCP Vault (falls back to FNOX_VAULT_NAMESPACE, then VAULT_NAMESPACE)
durationNoRequested lease TTL (e.g., "1h", "30m"; default: "15m")
methodNoHTTP method: "get" (default) or "post" (for pki/issue)

Prerequisites

The backend needs a Vault address and token. fnox resolves them in this order:

  1. address / token fields in config
  2. FNOX_VAULT_ADDR / FNOX_VAULT_TOKEN environment variables
  3. VAULT_ADDR / VAULT_TOKEN environment variables
  4. credential_command for the token

If the address or token is missing, fnox prints one of:

text
Vault address and token not found. Set VAULT_ADDR and VAULT_TOKEN.
Vault address not found. Set VAULT_ADDR.
Vault token not found. Set VAULT_TOKEN.

When credential_command is configured, fnox runs it through the platform shell and uses its trimmed stdout as the token. The command is rendered as a Tera template with address, secret_path, and namespace variables, and fnox sets VAULT_ADDR and VAULT_NAMESPACE in the command's environment from the lease config. Output is cached for five minutes within the current fnox process so repeated lease operations do not repeat the login, and the cache is cleared if Vault rejects the token. The command must finish within 30 seconds.

Credentials produced

Determined by the env_map configuration. The keys are field names from the Vault response, and the values are the environment variable names to inject.

Limits

  • Max duration: 24 hours
  • Revocation: Full support — calls PUT /v1/sys/leases/revoke on the Vault server

Examples

AWS dynamic secrets

toml
[leases.vault-aws]
type = "vault"
address = "https://vault.example.com:8200"
secret_path = "aws/creds/my-role"
duration = "1h"

[leases.vault-aws.env_map]
access_key = "AWS_ACCESS_KEY_ID"
secret_key = "AWS_SECRET_ACCESS_KEY"
security_token = "AWS_SESSION_TOKEN"

Database credentials

toml
[leases.vault-db]
type = "vault"
secret_path = "database/creds/readonly"
duration = "30m"

[leases.vault-db.env_map]
username = "DB_USER"
password = "DB_PASSWORD"
bash
fnox exec -- sh -c 'PGPASSWORD="$DB_PASSWORD" psql -h db.example.com -U "$DB_USER" mydb'

PKI certificates

This backend selects the method and maps response fields, but does not expose arbitrary request-body parameters. Use it only with a role that can issue with these defaults; use a custom command if your request needs fields such as common_name.

PKI and some other engines require POST requests. Set method = "post":

toml
[leases.vault-pki]
type = "vault"
secret_path = "pki/issue/my-role"
method = "post"
duration = "24h"

[leases.vault-pki.env_map]
certificate = "TLS_CERT"
private_key = "TLS_KEY"
issuing_ca = "TLS_CA"

With stored token

toml
[providers.op]
type = "1password"
vault = "Infrastructure"

[secrets]
VAULT_TOKEN = { provider = "op", value = "Vault/token", env = false }

[leases.vault-aws]
type = "vault"
address = "https://vault.example.com:8200"
secret_path = "aws/creds/my-role"

[leases.vault-aws.env_map]
access_key = "AWS_ACCESS_KEY_ID"
secret_key = "AWS_SECRET_ACCESS_KEY"
security_token = "AWS_SESSION_TOKEN"

With namespace (enterprise / HCP)

toml
[leases.vault-db]
type = "vault"
namespace = "admin/my-team"
secret_path = "database/creds/app-role"

[leases.vault-db.env_map]
username = "DB_USER"
password = "DB_PASSWORD"

With credential command

toml
[leases.vault-db]
type = "vault"
address = "https://vault.example.com"
namespace = "team-a"
credential_command = "vault login -method=oidc -token-only"
secret_path = "database/creds/readonly"
method = "post"

[leases.vault-db.env_map]
username = "DB_USER"
password = "DB_PASSWORD"

Notes

  • TTL is advisory. The duration field is sent to Vault as a TTL hint, but many engines (database, pki, rabbitmq) ignore it and use the role's configured default TTL instead. fnox warns if the actual lease_duration returned by Vault differs significantly from the requested value.
  • Static KV secrets never expire. KV v2 responses (data.data) are unwrapped automatically, and a lease_duration of 0 is treated as "no expiry", so the lease stays active until you revoke it.
  • GET vs POST. Most Vault dynamic secret engines use GET (e.g., aws/creds, database/creds). Some engines like pki/issue require POST — set method = "post" for those.

See also

MIT LicenseCopyright © 2026jdx.dev