HashiCorp Vault and PKI
Key Takeaways
- Vault's PKI secrets engine acts as a CA — issuing X.509 certificates via API with configurable roles, TTLs, and constraints
- Short-lived certificates (hours/days) issued on-demand eliminate the need for renewal infrastructure — request a new cert when the old one expires
- Vault Agent auto-renews certificates and templates them to files, triggering service reloads — fully automated certificate lifecycle
- Vault PKI is an Intermediate CA, not a Root — chain it under your existing Root CA for proper hierarchy
HashiCorp Vault’s PKI secrets engine transforms Vault into a programmable Certificate Authority. Applications request certificates via Vault’s API (or CLI), specifying the common name and SANs. Vault generates the key pair, signs the certificate according to role-based policies (allowed domains, key types, maximum TTL), and returns the certificate, private key, and CA chain. Combined with Vault Agent (which auto-renews and templates certificates to files), this creates a fully automated internal PKI that issues short-lived certificates without manual intervention.
Why it matters
- API-driven CA — no web portals, no email-based validation, no manual CSR submission. Applications request certificates programmatically and receive them in seconds.
- Short-lived certificates — Vault can issue certificates with TTLs of hours or days. Short lifetimes eliminate the need for revocation infrastructure (CRL/OCSP) — compromised certificates expire before they can be exploited.
- Role-based policy — Vault roles define what certificates can be issued: allowed domains, maximum TTL, key types, required SANs. Applications can only request certificates within their authorized scope.
- Dynamic issuance — each application instance gets its own unique certificate. No shared certificates, no wildcard keys deployed to multiple systems. Compromise of one instance doesn’t affect others.
- Integrated with identity — Vault authenticates requestors via Kubernetes service accounts, AWS IAM roles, LDAP, OIDC, or AppRole. Certificate issuance is tied to verified identity.
How it works
- Mount PKI engine — enable the PKI secrets engine at a path (e.g.,
pki/for root,pki_int/for intermediate) - Generate or import CA — generate a Root CA internally, or (recommended) generate a CSR and have your existing Root CA sign it (making Vault an Intermediate CA)
- Create roles — define certificate issuance policies: allowed domains, max TTL, key type, whether to allow subdomains, IP SANs, etc.
- Application authenticates — application logs into Vault using its identity (K8s auth, AWS auth, AppRole)
- Application requests certificate — writes to
pki_int/issue/{role-name}with desired CN and SANs - Vault issues certificate — generates key pair, signs certificate with the Intermediate CA key, returns: certificate, private key, CA chain, serial number
- Application uses certificate — configures TLS with the received cert and key
- Renewal — before TTL expires, application (or Vault Agent) requests a new certificate. Old one is discarded.
In real systems
Setting up Vault PKI (Intermediate CA):
# Enable PKI secrets engine
vault secrets enable -path=pki_int pki
vault secrets tune -max-lease-ttl=43800h pki_int # 5 years max
# Generate Intermediate CA CSR
vault write -format=json pki_int/intermediate/generate/internal \
common_name="Vault Intermediate CA" \
key_type="ec" key_bits=256 \
| jq -r '.data.csr' > vault_intermediate.csr
# Sign with your Root CA (external to Vault)
# ... (sign vault_intermediate.csr with your offline Root CA) ...
# Import signed intermediate certificate
vault write pki_int/intermediate/set-signed certificate=@signed_intermediate.pem
Creating a role (issuance policy):
vault write pki_int/roles/web-server \
allowed_domains="example.com,internal.example.com" \
allow_subdomains=true \
max_ttl="720h" \
key_type="ec" \
key_bits=256 \
require_cn=false \
allowed_uri_sans="spiffe://example.com/*"
Issuing a certificate:
vault write pki_int/issue/web-server \
common_name="api.example.com" \
alt_names="api-v2.example.com" \
ttl="24h"
# Returns:
# certificate: -----BEGIN CERTIFICATE-----...
# private_key: -----BEGIN EC PRIVATE KEY-----...
# ca_chain: [intermediate cert, root cert]
# serial_number: 3a:b2:c1:...
# expiration: 1719878400
Vault Agent (automatic renewal + templating):
# vault-agent-config.hcl
auto_auth {
method "kubernetes" {
config = {
role = "web-server"
}
}
}
template {
source = "/etc/vault/templates/cert.tpl"
destination = "/etc/ssl/certs/server.pem"
command = "systemctl reload nginx"
}
template {
source = "/etc/vault/templates/key.tpl"
destination = "/etc/ssl/private/server.key"
perms = "0600"
}
# cert.tpl:
# {{ with secret "pki_int/issue/web-server" "common_name=api.example.com" "ttl=24h" }}
# {{ .Data.certificate }}
# {{ .Data.ca_chain }}
# {{ end }}
cert-manager with Vault issuer (Kubernetes):
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: vault-issuer
spec:
vault:
server: https://vault.internal:8200
path: pki_int/sign/web-server
auth:
kubernetes:
role: cert-manager
mountPath: /v1/auth/kubernetes
serviceAccountRef:
name: cert-manager
Where it breaks
Vault as Root CA — teams generate the Root CA inside Vault for convenience. If Vault’s storage is compromised, the Root CA key is exposed — invalidating the entire PKI hierarchy. Vault should be an Intermediate CA, signed by an offline Root CA stored in an HSM. This way, Vault compromise means revoking the Intermediate and issuing a new one — not rebuilding the entire trust hierarchy.
TTL too short without Vault Agent — certificates are issued with 1-hour TTL for maximum security. But the application doesn’t use Vault Agent and has no renewal logic. After 1 hour, the certificate expires and the service goes down. Short TTLs require automated renewal (Vault Agent, application-level renewal, or cert-manager). Match the TTL to your renewal capability — don’t set 1-hour TTLs if your renewal mechanism runs every 6 hours.
Vault unavailability blocks certificate issuance — Vault is a single point of failure for certificate issuance. If Vault is down (maintenance, network partition, storage failure), no new certificates can be issued. Existing certificates continue working until they expire. For short-lived certificates (24 hours), a 24-hour Vault outage means services start failing. Mitigations: Vault HA cluster (3+ nodes), certificates with sufficient TTL to survive maintenance windows, and caching of recently-issued certificates.
Operational insight
Vault PKI’s sweet spot is internal certificates with short lifetimes (1-30 days) for service-to-service mTLS, internal APIs, and development environments. It’s not ideal for public-facing certificates (use ACME/Let’s Encrypt) or long-lived certificates (use a traditional CA with proper ceremony). The power of Vault PKI is making certificates disposable: issue them freely, use them briefly, discard them. This eliminates entire categories of certificate management problems (renewal tracking, revocation, key rotation) by making certificates ephemeral. If a certificate is compromised, it expires in hours anyway. If a service is decommissioned, its certificates expire without cleanup. The operational model shifts from “manage certificate lifecycle” to “ensure Vault is available to issue on demand.”
Related topics
Ready to Secure Your Enterprise?
Experience how our cryptographic solutions simplify, centralize, and automate identity management for your entire organization.