Overview
Vault provides:- KV v2 Engine: Application secrets (DB passwords, API keys, JWT RSA keys)
- Transit Engine: DEK encryption without exposing master key
- AppRole Auth: Machine-to-machine authentication
- Audit Logging: Complete access audit trail
Architecture
Local Development
docker compose -f docker-compose.dev.yml up handles Vault automatically. No manual setup needed.
The dev compose includes:
vault— Dev mode (in-memory, no TLS, token:dev-token, http://localhost:8200)vault-init— Runsscripts/vault-dev-init.shto seed engines, policies, AppRole, keys
/vault/config/. Services mount this volume read-only and use an entrypoint wrapper:
tests/e2e/docker-compose.e2e.yml) use the same script on port 8201.
Production Setup
Prerequisites
- Docker and Docker Compose v2+
- OpenSSL (TLS cert generation)
- jq (migration script)
1. Generate TLS Certificates
infrastructure/docker/config/vault/tls/:
vault-ca.crt— CA certificate (distribute to clients)vault-ca.key— CA private keyvault.crt— Vault server certificate (includes IP in SAN)vault.key— Vault server private key
2. Prepare Data Volumes
3. Deploy Vault
VAULT_BIND_ADDR=0.0.0.0 in .env for remote access.
Note: Thecommandmust beserver(no-config=flag). The Docker entrypoint adds-config=/vault/configautomatically. Explicit-configcauses “address already in use”.
4. Configure Firewall
5. Initialize Vault
6. Run Initialization Script
- KV v2 at
secret/, Transit engine withhoodcloud-masterkey - AppRole roles:
hoodcloud-control-plane,hoodcloud-auth-server,hoodcloud-payment-service,hoodcloud-admin - Policies for each role
- Audit logging at
/vault/logs/audit.log - Admin AppRole credentials (printed at the end — save these)
7. Revoke Root Token
8. Generate JWT RS256 Keys
Generate the RSA keypair for JWT signing:9. Generate X25519 Sealed Box Keypair
Generate the NaCl sealed box keypair for user-provided secret encryption. These are mandatory — api-server and orchestrator fail fast at startup without them.
Note: These must be a mathematically related X25519 keypair, not random bytes. For local dev, vault-dev-init.sh seeds them automatically.
10. Populate Secrets
Important:Incident notification fields are optional. If none are configured, incidents are tracked in the database but no external notifications are sent. Or use the interactive migration script:terraform_env_varsandprovider_env_varsmust be JSON objects, not JSON strings. The application deserializes them asmap[string]string.
11. Deploy AppRole Credentials
Note: Usechmod 644— containers run as non-root. The volume is mounted:ro.
12. Configure Control Plane Services
Copy the Vault CA certificate:.env:
- Vault env vars in
docker-compose.yml - Volume mount:
/opt/hoodcloud/secrets:/secrets:ro
Important: Useup -d, notrestart.restartdoes not reload.envchanges.
13. Save Credentials and Clean Up
Store securely (password manager):| Credential | Storage |
|---|---|
| 5 unseal keys | Distribute to separate trusted people |
| Admin AppRole role_id + secret_id | Password manager |
Vault CA certificate (vault-ca.crt) | Safe to keep in repo (gitignored keys are not committed) |
Secret Structure
Note: The payment service’snats_ctrl_account_pub(CTRL account public key) is not stored in Vault. It comes from theNATS_CTRL_ACCOUNT_PUBenvironment variable or YAML config. Only the signing seed is a secret and must be in Vault.
Configuration
All Vault-related environment variables (control plane and payment service) are documented in Environment Variables.Note: The payment service usesVAULT_ADDRESS(notVAULT_ADDR). See Environment Variables - Payment Service Vault for details.
Features
Circuit Breaker
| State | Behavior |
|---|---|
| Closed | Normal operation, requests go to Vault |
| Open | Vault unreachable, return cached values |
| Half-Open | Test request to check recovery |
- Failure threshold: 3 consecutive failures
- Recovery timeout: 30s (exponential backoff up to 5 min)
Secret Caching
- Default TTL: 5 minutes (
VAULT_CACHE_TTL) - Expired cache used as fallback when circuit is open
- Cache cleared on service restart
Token Renewal
- Renewal attempted at 75% of TTL
- On failure, re-authenticates with AppRole credentials
Credential Expiry Monitoring
Operations
Unseal After Restart
Vault seals on every restart. Unseal with 3 of 5 keys:Authenticate as Admin
After root token is revoked:Rotate Secrets
Rotate AppRole Secret ID
View Audit Logs
Verify Secret Storage
Troubleshooting
Service Cannot Authenticate
- Check Vault status:
vault status(sealed?) - Verify role exists:
vault read auth/approle/role/hoodcloud-control-plane - Regenerate secret_id:
Secret Not Found
Circuit Breaker Open
TLS Connection Errors
VAULT_TLS_CA_FILE=/secrets/vault-ca.crt in .env, or for CLI:
Security Model
AppRole Credential Security (Phase 0)
secret_id_num_uses=0— Reusable for re-authentication after Vault restartssecret_id_ttl=0— No expirysecret_id_bound_cidrs— Usable only from bound server IPtoken_bound_cidrs— Tokens valid only from bound server IP
Phase 1 Upgrade: SecretID Rotation
Add TTL to SecretIDs (secret_id_ttl=7d) and implement rotation via CI/Ansible. See scripts/vault-init.sh for AppRole configuration. Options:
- Vault Agent sidecar for automated token management
- Response wrapping (
secret_id_num_uses=1) for strongest security