Secrets Management & Infrastructure Security
HashiCorp Vault: Dynamic Secrets Management
4 min read
HashiCorp Vault is the industry standard for secrets management. It provides dynamic secrets, encryption as a service, and fine-grained access control.
Why Vault?
| Feature | Benefit |
|---|---|
| Dynamic secrets | Generate short-lived credentials on-demand |
| Encryption as a service | Encrypt data without managing keys |
| Audit logging | Track every secret access |
| Lease management | Automatic secret rotation |
| Multiple auth methods | OIDC, Kubernetes, AWS, GitHub |
Core Concepts
┌─────────────────────────────────────────────────────┐
│ Vault Server │
├─────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Auth Methods │ │ Secret │ │
│ │ • Kubernetes │ │ Engines │ │
│ │ • OIDC │ │ • KV │ │
│ │ • AWS IAM │ │ • Database │ │
│ │ • GitHub │ │ • AWS │ │
│ └──────────────┘ │ • PKI │ │
│ │ └──────────────┘ │
│ ▼ │ │
│ ┌──────────────┐ │ │
│ │ Policies │◀───────┘ │
│ │ (ACL Rules) │ │
│ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────┘
Getting Started
Development Mode
# Start Vault in dev mode (NOT for production)
vault server -dev
# In another terminal
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='root'
# Verify
vault status
KV Secrets Engine
# Enable KV v2 secrets engine
vault secrets enable -path=secret kv-v2
# Store a secret
vault kv put secret/myapp/config \
api_key="sk-1234567890" \
db_password="secure_password"
# Retrieve a secret
vault kv get secret/myapp/config
# Get specific field
vault kv get -field=api_key secret/myapp/config
Dynamic Database Credentials
Instead of static passwords, Vault generates temporary credentials:
# Enable database secrets engine
vault secrets enable database
# Configure PostgreSQL connection
vault write database/config/mydb \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@db.example.com:5432/myapp" \
allowed_roles="readonly" \
username="vault_admin" \
password="admin_password"
# Create a role for read-only access
vault write database/roles/readonly \
db_name=mydb \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
# Get dynamic credentials (new creds each time!)
vault read database/creds/readonly
Output:
Key Value
--- -----
lease_id database/creds/readonly/abc123
lease_duration 1h
username v-token-readonly-xyz789
password A1B2C3D4E5F6G7H8
Policies for Access Control
# policy.hcl - Developer policy
path "secret/data/myapp/*" {
capabilities = ["read"]
}
path "database/creds/readonly" {
capabilities = ["read"]
}
# Deny access to production secrets
path "secret/data/production/*" {
capabilities = ["deny"]
}
# Create policy
vault policy write developer policy.hcl
# Assign policy to auth method
vault write auth/kubernetes/role/myapp \
bound_service_account_names=myapp-sa \
bound_service_account_namespaces=default \
policies=developer \
ttl=1h
CI/CD Integration
GitHub Actions with Vault
# .github/workflows/deploy.yml
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC
steps:
- uses: actions/checkout@v4
- name: Import Secrets from Vault
uses: hashicorp/vault-action@v2
with:
url: https://vault.example.com
method: jwt
role: github-actions
secrets: |
secret/data/myapp/config api_key | API_KEY ;
database/creds/readonly username | DB_USER ;
database/creds/readonly password | DB_PASS
- name: Deploy
run: |
# Secrets available as env vars
./deploy.sh
env:
API_KEY: ${{ env.API_KEY }}
DB_USER: ${{ env.DB_USER }}
DB_PASS: ${{ env.DB_PASS }}
Kubernetes Integration
# Pod with Vault Agent sidecar
apiVersion: v1
kind: Pod
metadata:
name: myapp
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/config"
spec:
serviceAccountName: myapp-sa
containers:
- name: app
image: myapp:latest
# Secrets available at /vault/secrets/config
Best Practices
| Practice | Why |
|---|---|
| Use dynamic secrets | Limits blast radius of compromise |
| Short TTLs | 1h for dev, 15m for production |
| Audit everything | Enable audit logging to SIEM |
| Principle of least privilege | Minimal permissions per service |
| Separate Vault instances | Dev/staging vs production |
Next, we'll explore GitHub Secrets and OIDC authentication for simpler use cases. :::