HashiCorp Vault — Certificate Rotation
Short-lived certificates with automatic rotation eliminate the need for CRL/OCSP and reduce the blast radius of compromised certificates.
Short-Lived Certificate Strategy
Traditional approach:
Issue cert → valid 1 year → CRL/OCSP for revocation
⚠️ Complex revocation infrastructure
⚠️ Revocation checking is unreliable
Vault approach:
Issue cert → valid 24 hours → auto-renew before expiry
✅ No revocation needed (cert expires naturally)
✅ Compromised cert is useless within hours
Recommended TTLs:
| Environment | Certificate TTL | Renewal at |
|---|---|---|
| Production services | 24-72 hours | 67% of TTL |
| CI/CD pipelines | 1-4 hours | Before each deploy |
| Development | 7-30 days | Manual or automated |
Rotation with Vault Agent
Vault Agent can automatically renew certificates using its template feature:
# vault-agent-config.hcl
auto_auth {
method "approle" {
config = {
role_id_file_path = "/etc/vault/role-id"
secret_id_file_path = "/etc/vault/secret-id"
}
}
}
template {
source = "/etc/vault/templates/cert.tpl"
destination = "/etc/nginx/ssl/cert.pem"
command = "systemctl reload nginx"
}
template {
source = "/etc/vault/templates/key.tpl"
destination = "/etc/nginx/ssl/key.pem"
command = "systemctl reload nginx"
}
# /etc/vault/templates/cert.tpl
{{ with secret "pki_int/issue/web-server" "common_name=api.example.com" "ttl=24h" }}
{{ .Data.certificate }}
{{ .Data.issuing_ca }}
{{ end }}
# /etc/vault/templates/key.tpl
{{ with secret "pki_int/issue/web-server" "common_name=api.example.com" "ttl=24h" }}
{{ .Data.private_key }}
{{ end }}
Vault Agent will:
- Issue a certificate on startup
- Re-issue before the certificate expires (at ~67% of TTL)
- Reload Nginx when the certificate is updated
Rotation with Cron
For simpler setups:
#!/bin/bash
# renew-cert.sh — Run via cron every 12 hours
vault write -format=json pki_int/issue/web-server \
common_name="api.example.com" \
ttl=24h > /tmp/cert.json
jq -r '.data.certificate' /tmp/cert.json > /etc/nginx/ssl/cert.pem
jq -r '.data.private_key' /tmp/cert.json > /etc/nginx/ssl/key.pem
jq -r '.data.issuing_ca' /tmp/cert.json >> /etc/nginx/ssl/cert.pem
systemctl reload nginx
rm /tmp/cert.json
# Crontab entry — renew every 12 hours
0 */12 * * * /usr/local/bin/renew-cert.sh
CA Certificate Rotation
When the intermediate CA is approaching expiry, rotate it:
# 1. Generate new intermediate CSR
vault write -format=json pki_int/intermediate/generate/internal \
common_name="My Org Intermediate CA v2" \
issuer_name="intermediate-2025" | jq -r '.data.csr' > /tmp/pki_int_v2.csr
# 2. Sign with root
vault write -format=json pki/root/sign-intermediate \
issuer_ref="root-2024" \
csr=@/tmp/pki_int_v2.csr \
format=pem_bundle \
ttl=26280h | jq -r '.data.certificate' > /tmp/intermediate_v2.pem
# 3. Import new intermediate
vault write pki_int/intermediate/set-signed \
certificate=@/tmp/intermediate_v2.pem
# 4. Update default issuer
vault write pki_int/config/issuers \
default="intermediate-2025"
During transition, both old and new certificates are valid.
Quick Reference
| Strategy | How | Best For |
|---|---|---|
| Vault Agent template | Auto-issues and reloads | Services with sidecar |
| Cron script | Periodic re-issue | Simple deployments |
| Application-managed | App calls Vault API | Custom rotation logic |
| cert-manager (K8s) | K8s-native cert management | Kubernetes workloads |
Next: mTLS