Short-lived TLS certificates from Vault PKI
Run an internal CA in Vault and issue certs that live for hours, not years — rotation becomes a non-event.
A three-year TLS certificate in a Kubernetes Secret is a time bomb with a calendar invite. When it expires, dashboards go red and nobody remembers who generated the CSR. Vault's PKI secrets engine flips the model: Vault is your private CA, apps request certificates with a TTL measured in hours or days, and renewal is an API call — not a ticket. Leases bind lifetime; when the lease ends, the cert is dead whether or not anyone rotated the file on disk.
You will enable PKI, generate an intermediate CA, define a role that caps TTLs and allowed domains, then issue a certificate and read the lease. For HA Vault, auth methods, and dynamic secrets beyond PKI, continue in Vault from dev to production.
The leaf private key is generated in Vault (or by the client, depending on API). The lease binds lifetime — when it expires, treat the cert as dead.
Enable PKI and build an intermediate
Production setups keep a root CA offline and only the intermediate in Vault. For a lab, a Vault-generated root is fine — just do not pretend that lab root is your company trust anchor. Tune max-lease-ttl on the mount so no role can accidentally mint multi-year leaves. Document the CA hierarchy in runbooks: which mount is authoritative, who can sign intermediates, and where CRL/OCSP endpoints live.
vault secrets enable pkivault secrets tune -max-lease-ttl=87600h pki# lab only — generate a root; prefer importing an intermediate in prodvault write -field=certificate pki/root/generate/internal \common_name="Vault Lab Root" ttl=87600h > lab-root.pemvault write pki/config/urls \issuing_certificates="https://vault.example.com:8200/v1/pki/ca" \crl_distribution_points="https://vault.example.com:8200/v1/pki/crl"
A role that refuses wild requests
Roles are the policy. Cap max_ttl, list allowed_domains, and turn on enforce_hostnames. A role that allows * with a 90-day TTL recreates the old long-lived cert problem inside Vault. Separate roles per service class — shop-svc for app certs, ingress-edge for public hostnames — so a compromised AppRole cannot mint arbitrary domains.
vault write pki/roles/shop-svc \allowed_domains="shop.svc.cluster.local,shop.example.com" \allow_subdomains=true \enforce_hostnames=true \max_ttl="72h" \ttl="24h"
vault write pki/issue/shop-svc common_name=checkout.shop.svc.cluster.local ttl=24hKey Valuecertificate -----BEGIN CERTIFICATE-----private_key -----BEGIN RSA PRIVATE KEY-----ca_chain [lab root / intermediate]lease_duration 24hstore privately; Vault will not show the private key againHand the cert to the workload safely
In Kubernetes, prefer the Vault Agent injector or External Secrets patterns that refresh before TTL. Writing a leaf key into a long-lived Secret without renewal automation just moves the expiry bomb. The injector sidecar renews at ~80% of lease lifetime and reloads the app — or writes to a projected volume the process watches.
Publish the issuing CA certificate to clients that need to trust internal services — ingress controllers, service meshes, and curl in CI. Vault exposes it at pki/ca/pem. Rotate the intermediate on a schedule and cross-sign during overlap so renewals never require a flag day.
Where this goes next
PKI is one engine. Wire Kubernetes auth so pods never see a long-lived Vault token, and pair short-lived TLS with NetworkPolicy so a compromised cert cannot be exfiltrated to an unexpected destination. The Vault from dev to production course covers HA, auth methods, dynamic secrets, and PKI done properly.
Go deeper in a courseVault from dev to productionHA, auth methods, dynamic secrets, and PKI done properly.View course