Skip to content

OIDC with HashiCorp Vault

1. Enable and configure the JWT auth method

Section titled “1. Enable and configure the JWT auth method”
Terminal window
vault auth enable jwt
vault write auth/jwt/config \
oidc_discovery_url="https://slipway.example.com" \
bound_issuer="https://slipway.example.com"

Vault fetches the signing keys from your Slipway issuer’s discovery document, so Vault must reach /.well-known/openid-configuration.

2. Create a policy and a role bound to claims

Section titled “2. Create a policy and a role bound to claims”
Terminal window
vault policy write api-prod - <<'POLICY'
path "kv/data/api/prod/*" { capabilities = ["read"] }
POLICY
vault write auth/jwt/role/slipway-api-prod - <<'ROLE'
{
"role_type": "jwt",
"user_claim": "sub",
"bound_audiences": ["vault"],
"bound_claims_type": "string",
"bound_claims": {
"org": "acme",
"project": "api",
"environment": "production"
},
"token_policies": ["api-prod"],
"token_ttl": "10m"
}
ROLE

bound_claims is what makes this safe: a token from another project, or from a job that is not bound to the production environment, is refused.

permissions: { id_token: write }
jobs:
- name: deploy
environment: { name: production, service: api }
steps:
- run: |
export VAULT_ADDR=https://vault.example.com:8200
export VAULT_TOKEN=$(vault write -field=token auth/jwt/login \
role=slipway-api-prod jwt="$SLIPWAY_ID_TOKEN")
echo "::mask::$VAULT_TOKEN"
DB_PASSWORD=$(vault kv get -field=password kv/api/prod/db)
echo "::mask::$DB_PASSWORD"
./migrate.sh

Create a Slipway secret of kind external_ref, and the platform authenticates to Vault with the job’s identity for you:

Terminal window
slipway secret set DB_PASSWORD --env production --external-ref vault://kv/data/api/prod/db#password
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}

See the Vault integration. Vault transit can also act as the org’s key-encryption provider (BYOK), see Secrets.