HashiCorp Vault — Certificate Rotation

2 minute read

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:

  1. Issue a certificate on startup
  2. Re-issue before the certificate expires (at ~67% of TTL)
  3. 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