Vault’s PKI secrets engine can churn out cryptographically signed X.509 certificates on demand, acting as a full-fledged Certificate Authority (CA) without the usual operational overhead.
Let’s see it in action. Imagine you need a TLS certificate for a new service that will only run for an hour. Instead of going through a public CA or managing your own internal CA infrastructure, you can ask Vault to generate one for you, signed by a Vault-managed CA.
First, you need to enable the PKI secrets engine if it’s not already:
vault secrets enable pki
This command mounts the PKI engine at the /pki path. Next, you need to configure a root CA. Vault can generate one for you:
vault write pki/root/generate/internal \
common_name="my-vault-root-ca" \
ttl="87600h" # 10 years
This creates a self-signed root certificate and private key, stored securely within Vault. Now, you can create an intermediate CA that will be used to sign the actual service certificates. This is good practice for security and manageability.
vault write pki/intermediate/generate/internal \
common_name="my-vault-intermediate-ca" \
ttl="8760h" # 1 year
This generates an intermediate CA certificate and key, signed by the root CA. You’ll need to configure Vault to use this intermediate CA for issuing certificates.
vault write pki/config/intermediate \
ca_cert="$(/vault read -field=certificate pki/intermediate/cert)" \
ca_key="$(/vault read -field=private_key pki/intermediate/key)" \
max_lease_ttl="72h" # Max TTL for certificates issued by this intermediate
The ca_cert and ca_key are populated by reading the generated intermediate CA’s certificate and private key from Vault. The max_lease_ttl sets an upper bound on how long any certificate issued by this intermediate can be valid.
Now, you’re ready to define a role that specifies the properties of the certificates you want to issue. A role dictates things like the allowed common names, subject alternative names (SANs), and the default TTL.
vault write pki/roles/my-app-role \
key_type="rsa" \
max_ttl="1h" \
allowed_domains="myapp.example.com" \
allow_subdomains="true" \
user_id_field="none"
Here, we’ve defined a role named my-app-role. Certificates issued under this role will be valid for a maximum of 1 hour (max_ttl="1h"), will be for myapp.example.com and its subdomains (allowed_domains="myapp.example.com", allow_subdomains="true"), and will use RSA keys (key_type="rsa"). user_id_field="none" means we’re not tying certificate issuance to a specific authenticated user in Vault.
Finally, you can request a certificate for your application:
vault write pki/issue/my-app-role \
common_name="service.myapp.example.com" \
ttl="3600s" # 1 hour
This command asks Vault to issue a certificate using the my-app-role. You specify the common_name for the certificate, and an optional ttl which must be less than or equal to the max_ttl defined in the role. Vault will return the signed certificate, the private key, and the certificate chain.
This allows for dynamic, short-lived credentials that can be rotated automatically, significantly improving your security posture by reducing the window of opportunity for compromised keys.
The most surprising thing about Vault’s PKI is that it doesn’t just issue certificates; it manages the entire lifecycle of a Certificate Authority, from root generation to intermediate signing and revocation, all through its API.
The core problem this solves is the operational burden and security risks associated with traditional PKI management. Instead of deploying and maintaining complex CA software, managing hardware security modules (HSMs), and handling certificate renewal processes manually, you delegate all of that to Vault. Vault provides a single, auditable source of truth for your PKI operations, accessible via its API, CLI, or UI.
Internally, Vault uses its storage backend (like Consul, etcd, or a file backend) to store the CA private keys, root and intermediate certificates, and the configuration for each PKI role. When you request a certificate, Vault’s PKI engine retrieves the appropriate intermediate CA’s private key, uses it to sign a new certificate with the requested parameters (common name, SANs, TTL), and then returns the signed certificate along with the intermediate CA’s public certificate to form the certificate chain. The private key for the issued certificate is generated by Vault and returned to the client, but it’s never stored persistently by Vault for that specific certificate once issued.
When you configure an intermediate CA using vault write pki/config/intermediate, Vault takes the certificate and private key of that intermediate CA and stores them internally. It then uses the private key of this intermediate CA to sign any subsequent certificate requests that are processed by that intermediate. This means the root CA’s private key is only used to sign the intermediate CA’s certificate, and the intermediate CA’s private key is used for day-to-day certificate issuance. This layered approach is standard PKI best practice and limits the blast radius if an intermediate CA’s key is compromised.
The next hurdle you’ll encounter is managing certificate revocation, especially when dealing with potentially short-lived certificates that might still need to be revoked if a host is compromised before its TTL expires.