Vault Transit signing
TL;DR — Configure
signing.key_store: vault_transitand AuthPlane sends every JWT payload to Vault for signing. Private key material never lives on the AuthPlane host. Adds ~2 ms latency per signing op, adds Vault as a hard dependency. Best fit for compliance environments, HSM-backed Vault deployments, zero-trust setups, and multi-replica Kubernetes.
When to choose Vault Transit
AuthPlane supports three signing key stores. Pick this one only when you need it.
Why Vault Transit
- Keys never leave Vault — signing material lives inside Vault’s HSM-backed or software-backed Transit engine
- Audit trail — Vault logs every signing operation
- Managed rotation — Vault Transit handles key rotation on its side
- FIPS compliance — when Vault is backed by an HSM
Prerequisites
- HashiCorp Vault 1.12+
- Transit secrets engine enabled
- A Transit key configured for signing — ECDSA-P256 for ES256, RSA-2048 for RS256
Step 1 — Enable the Transit engine
vault secrets enable transit
Step 2 — Create a signing key
For ES256 (ECDSA P-256), AuthPlane’s default:
vault write transit/keys/authserver-signing type=ecdsa-p256
For RS256:
vault write transit/keys/authserver-signing type=rsa-2048
Step 3 — Create a Vault policy
authserver-signing-policy.hcl:
path "transit/sign/authserver-signing" {
capabilities = ["update"]
}
path "transit/verify/authserver-signing" {
capabilities = ["update"]
}
path "transit/keys/authserver-signing" {
capabilities = ["read"]
}
Apply:
vault policy write authserver-signing authserver-signing-policy.hcl
Step 4 — Choose an authentication method
Option A — Static token
Fine for dev; requires token rotation for production.
vault token create -policy=authserver-signing -period=768h
signing:
algorithm: ES256
key_store: vault_transit
vault_transit:
address: https://vault:8200
token: "hvs.your-vault-token"
mount: transit
key_name: authserver-signing
Option B — AppRole (recommended for production)
Enable AppRole, create a role tied to the signing policy:
vault auth enable approle
vault write auth/approle/role/authserver \
token_policies="authserver-signing" \
token_ttl=1h \
token_max_ttl=4h
Get the role ID and secret ID:
vault read auth/approle/role/authserver/role-id
vault write -f auth/approle/role/authserver/secret-id
Config:
signing:
algorithm: ES256
key_store: vault_transit
vault_transit:
address: https://vault:8200
mount: transit
key_name: authserver-signing
approle:
role_id: "your-role-id"
secret_id: "your-secret-id"
mount: approle
AuthPlane requests a fresh Vault token via AppRole on boot and re-authenticates before the token expires.
Environment variables
AUTHPLANE_SIGNING_KEY_STORE=vault_transit
AUTHPLANE_SIGNING_ALGORITHM=ES256
AUTHPLANE_VAULT_ADDR=https://vault:8200
# Option A: static token
AUTHPLANE_VAULT_TOKEN=hvs.your-token
# Option B: AppRole
AUTHPLANE_VAULT_APPROLE_ROLE_ID=your-role-id
AUTHPLANE_VAULT_APPROLE_SECRET_ID=your-secret-id
AUTHPLANE_VAULT_APPROLE_MOUNT=approle
AUTHPLANE_VAULT_TRANSIT_MOUNT=transit
AUTHPLANE_VAULT_TRANSIT_KEY_NAME=authserver-signing
AUTHPLANE_VAULT_TIMEOUT=10s
Kubernetes (Helm) setup
Same knobs via values.yaml — see Kubernetes (Helm) → Vault Transit. Recommended Vault-auth patterns in Kubernetes:
- AppRole — inject
roleId/secretIdviaexistingSecretor External Secrets Operator. - Vault Agent Sidecar — Vault Agent annotations via
podAnnotations; mount injected secrets viaextraVolumes. - Vault CSI Provider — mount secrets as volumes.
The Helm chart doesn’t ship a Vault subchart — Vault is infrastructure your organization already runs.
Docker Compose (dev/testing)
Dev-mode Vault for local iteration only. Never for production.
services:
vault:
image: hashicorp/vault:1.15
ports:
- "8200:8200"
environment:
VAULT_DEV_ROOT_TOKEN_ID: dev-root-token
cap_add:
- IPC_LOCK
authserver:
image: authplane/authserver:latest
environment:
AUTHPLANE_SIGNING_KEY_STORE: vault_transit
AUTHPLANE_VAULT_ADDR: http://vault:8200
AUTHPLANE_VAULT_TOKEN: dev-root-token
AUTHPLANE_VAULT_TRANSIT_KEY_NAME: authserver-signing
depends_on:
- vault
Then inside the vault container:
vault secrets enable transit
vault write transit/keys/authserver-signing type=ecdsa-p256
Note — dev mode Vault (
VAULT_DEV_ROOT_TOKEN_ID) is for testing only. In production, use a properly initialized and sealed Vault.
Validation rules
Boot fails if:
signing.vault_transit.addressis missing whenkey_store: vault_transit- Both
tokenandapprole.role_idare set (they’re mutually exclusive) approle.role_idis set butapprole.secret_idis missing
Key rotation with Vault Transit
Two levels:
- Vault-side rotation —
vault write -f transit/keys/authserver-signing/rotatebumps the key version inside Vault. AuthPlane’s next signing operation picks up the new version transparently. - AuthPlane-side rotation —
authserver admin key rotatestill works and triggers a new key version request against Vault. Prefer this route if you want AuthPlane’s audit trail alongside Vault’s.
The JWKS keeps both old and new keys visible until the old one expires so in-flight tokens continue to verify.
Troubleshooting
vault transit: permission denied
The token or AppRole doesn’t have the required policy. Verify:
vault token lookup
vault policy read authserver-signing
Common cause: policy typo, or policy applied to a different role than the one whose credentials AuthPlane has.
vault transit: key not found
The Transit key doesn’t exist under the configured mount. Create it:
vault write transit/keys/authserver-signing type=ecdsa-p256
Double-check the mount matches vault_transit.mount in your config.
vault transit: connection refused
AuthPlane can’t reach Vault. Check:
vault_transit.addressis correct- In Docker Compose, use the service name (
http://vault:8200) notlocalhost - In Kubernetes, use the ClusterIP or the FQDN (
vault.vault.svc:8200) - Vault is unsealed and initialized (
vault status)
AppRole authentication loops or reauthenticates constantly
Token TTL is too short and secret_id_ttl expired. Bump token_ttl and token_max_ttl on the role, or regenerate the secret_id.
Related
- Operate overview — mode picker (includes signing store choice matrix)
- Operate: Standalone — Vault Transit config in a systemd context
- Operate: Kubernetes — Helm values for Vault Transit
- Security: Key management — rotation policy, algorithm choice, keyfile vs postgres vs Vault
- Configuration reference — every signing knob