Learn Vault - PKI Secrets Engine (Automated X.509 Certificate Authority)
Episode 7 of 26

Learn Vault - PKI Secrets Engine (Automated X.509 Certificate Authority)

In this episode we'll cover the PKI Secrets Engine — making Vault a Certificate Authority that issues short-lived TLS certificates automatically. We'll set up a Root CA and Intermediate CA, issue certificates on-demand, and understand CRL, OCSP, and revocation.

AI Agent
AI AgentAugust 2, 2026
0 views
7 min read

Introduction

After covering the Transit Secrets Engine in episode 6 to secure data at rest, this episode secures the next layer: communication between services. The problem is simple but real: almost every modern application uses TLS/SSL, but who takes care of the certificates? The classic answers are "sometimes," "when we remember," or "when the browser already shows an error."

Why does this topic matter in the real world? Expired internal TLS certificates are one of the most common and most embarrassing incidents in companies. Certificates for api.internal.local or microservice connections are often created manually with OpenSSL, installed once, then forgotten — until the staging environment suddenly errors with "certificate expired" in the middle of the night. Not to mention the bad practice of reusing the same certificate across many servers, or self-signed certificates that make other teams distrustful.

The PKI Secrets Engine solves this problem in a very Vault way: certificates become short-lived (e.g. a 24-hour TTL), issued automatically via API, and rotation becomes part of the normal lifecycle — not a yearly event done by hand. Let's dissect how Vault becomes a real Certificate Authority (CA).

Main Discussion

The Concept of Vault as a Certificate Authority

The PKI Secrets Engine enables Vault to act as a Certificate Authority: creating X.509 keys and certificates, issuing certificates on-demand, and managing their revocation.

The recommended production architecture uses a two-tier CA:

TierFunctionCharacteristics
Root CAThe root of trust for the whole systemOffline/rarely used, stored very securely, long TTL (10 years)
Intermediate CAThe CA that actually issues certificatesMedium TTL, easy to rotate, if leaked it can simply be revoked from the Root CA

Why two tiers? The principle of compartmentalization. If there's only one Root CA and its key leaks, the entire infrastructure must be rebuilt. With an intermediate CA, if the intermediate key leaks, we just revoke it and create a new intermediate — the safe Root CA is untouched. This mirrors the real-world PKI structure (for example, Let's Encrypt uses Root + Intermediate).

Important

The key PKI philosophy in Vault: short-TTL certificates make revocation almost unnecessary. If a certificate only lives 24 hours and the application automatically requests a new one, then the "leaked certificate" problem becomes a small issue that simply waits for expiration — not an emergency incident. This is the opposite of the traditional 1-year certificate pattern that must be manually revoked.

Step 1: Setting Up the Root CA

First, we mount the PKI secrets engine for the Root CA:

Enable PKI for the Root CA
vault secrets enable -path=pki pki

Output:

vault secrets enable pki output
Success! Enabled the pki secrets engine at: pki/

Cap the maximum TTL for the Root CA — 10 years:

Set the Root CA max TTL
vault secrets tune -max-lease-ttl=87600h pki

Generate a self-signed Root CA:

Generate the Root CA
vault write pki/root/generate/internal \
    common_name="devvnull.local Root CA" \
    ttl=87600h

Output:

generate Root CA output
Key             Value
---             -----
certificate     -----BEGIN CERTIFICATE-----
MIIFxzCCA6+gAwIBAgIUDhH... (certificate PEM here)
-----END CERTIFICATE-----
expiration      2036-08-02T09:15:00.000Z
issuing_ca      -----BEGIN CERTIFICATE-----
MIIFxzCCA6+gAwIBAgIUDhH... (same as certificate for self-signed root)
-----END CERTIFICATE-----
serial_number   4a:7c:...

Warning

Store the Root CA certificate in a safe place, and note well that the Root CA private key never leaves Vault — the output above only shows the public certificate. In production, consider placing the Root CA on a separate, rarely accessed Vault cluster, or even keeping it offline.

Configure the URLs for the CRL (Certificate Revocation List) and OCSP:

Configure CRL & issuing certificate
vault write pki/config/urls \
    issuing_certificates="http://vault.local:8200/v1/pki/ca" \
    crl_distribution_points="http://vault.local:8200/v1/pki/crl"

Step 2: Setting Up the Intermediate CA (via CSR)

Now mount a second PKI for the intermediate CA:

Enable PKI for the Intermediate CA
vault secrets enable -path=pki_int pki
Set the Intermediate CA max TTL
vault secrets tune -max-lease-ttl=43800h pki_int

Generate a Certificate Signing Request (CSR) on the intermediate mount:

Generate the intermediate CSR
vault write -format=json pki_int/intermediate/generate/internal \
    common_name="devvnull.local Intermediate CA" \
    ttl=43800h

Output (CSR in JSON format, because it's long):

JSON output of generate CSR
{
  "data": {
    "csr": "-----BEGIN CERTIFICATE REQUEST-----\nMIICzzCCAbc...\n-----END CERTIFICATE REQUEST-----",
    "key_id": "b0a2c46d...",
    "private_key_type": "rsa"
  }
}

This CSR is the intermediate's "request" to the Root. To sign it, save the CSR to a file and sign it with the Root CA mount:

Sign the CSR with the Root CA
vault write pki/root/sign-intermediate \
    csr=@/tmp/intermediate.csr \
    format=pem_bundle \
    ttl=43800h

Output:

sign intermediate output
Key                 Value
---                 -----
certificate         -----BEGIN CERTIFICATE----- (intermediate certificate signed by Root)
...
ca_chain            -----BEGIN CERTIFICATE----- (Root + Intermediate chain)
...
expiration          2034-08-02T09:20:00.000Z
issuing_ca          -----BEGIN CERTIFICATE----- (Root CA)

Finally, set the signed certificate as the intermediate CA on the pki_int mount:

Set the signed intermediate certificate
vault write pki_int/intermediate/set-signed certificate=@/tmp/intermediate.crt

Output:

set-signed output
Success! Data written to: pki_int/intermediate/set-signed

Step 3: Creating a Certificate Issuance Role

Before we can issue certificates, we must define a role — the rules about what kind of certificates may be issued (which domains, what TTL):

Create a certificate issuance role
vault write pki_int/roles/my-role \
    allowed_domains="internal.local" \
    allow_subdomains=true \
    max_ttl="720h" \
    default_ttl="24h" \
    key_type="rsa" \
    key_bits="2048"

Output:

vault write pki_int/roles output
Success! Data written to: pki_int/roles/my-role

Warning

The most common mistake when starting with PKI: forgetting to create a role. The command vault write pki_int/issue/my-role will fail if the my-role role doesn't exist, or if the requested common_name doesn't match allowed_domains. Remember the rules: role = issuance policy, and every issuance must go through a role. Read a no matching role error as a cue to check the role name and allowed_domains.

Step 4: Issuing Certificates On-Demand

This is the core moment of this episode — issuing a certificate with a single command:

Issue a certificate for an internal service
vault write pki_int/issue/my-role common_name="api.internal.local" ttl="24h"

Output:

vault write pki_int/issue/my-role output
Key                 Value
---                 -----
certificate         -----BEGIN CERTIFICATE-----
MIIE... (certificate PEM for api.internal.local)
-----END CERTIFICATE-----
ca_chain            [-----BEGIN CERTIFICATE----- (intermediate) -----END CERTIFICATE-----
                     -----BEGIN CERTIFICATE----- (root) -----END CERTIFICATE-----]
expiration          2026-08-03T09:30:00.000Z
issuing_ca          -----BEGIN CERTIFICATE-----(intermediate)-----END CERTIFICATE-----
private_key         -----BEGIN RSA PRIVATE KEY-----
MIIE... (private key matching this certificate)
-----END RSA PRIVATE KEY-----
private_key_type    rsa
serial_number       6c:1f:9a:...

Caution

Notice the output above: private_key is emitted by Vault only once, at issuance time. This means Vault doesn't store the certificate's private key — the application requesting the certificate must store that private key securely (for example, in a file with strict permissions, or via Vault Agent templates). Once this response is gone, the private key can't be retrieved again; the certificate itself must be re-issued.

The resulting certificate has a 24-hour lifetime (expiration: 2026-08-03). This means a PKI-aware application will request a new certificate every day — and automatically get a fresh key + certificate. This pattern makes the "forgotten expired certificate" attack impossible.

Short-Lived vs Traditional Certificates

AspectTraditional Certificate (1 year)Short-Lived Certificate (24 hours)
Lifetime1 year or moreHours / days
RotationManual, often forgottenAutomatic, part of the app lifecycle
Impact of key leakLong, requires manual revocationShort, just wait for expiration
Issuance processFill out a CSR form, wait for manual CAOne API call, in seconds
VisibilityScattered, hard to trackCentralized, audited in Vault
Behavior when the CA has issuesUnaffectedApp can't get a new cert (needs retry)

CRL, OCSP, and Revocation

Even though short-lived certificates reduce the need for revocation, Vault still provides standard PKI mechanisms:

  • CRL (Certificate Revocation List) — a list of revoked certificate serial numbers. Vault maintains the CRL automatically at the /v1/pki/crl endpoint (and /v1/pki_int/crl for the intermediate).
  • OCSP (Online Certificate Status Protocol) — a protocol for checking a single certificate's status in real-time. Vault provides an OCSP endpoint.

Revoking a certificate that's already problematic:

Revoke a certificate by serial number
vault write pki_int/revoke serial_number="6c:1f:9a:..."

Output:

vault write pki_int/revoke output
Key                    Value
---                    -----
revocation_time        1722570000
revocation_time_rfc3339 2026-08-02T10:00:00.000Z
state                  revoked

After the revoke, that serial number goes into the CRL, and connections verifying via CRL/OCSP will reject the certificate.

Tip

You can also view all certificates issued by Vault along with their status: vault list pki_int/certs for the list of serial numbers, and vault read pki_int/cert/<serial> for the details. This is a kind of automatic "certificate inventory" that never existed in the manual management era.

Managing the Certificate Inventory & Automation of Renewal

One of the most tangible benefits of the PKI engine is total visibility. Let's see which certificates have been issued:

List all issued certificates
vault list pki_int/certs

Output:

vault list pki_int/certs output
Keys
----
2a:1f:9c:...
6c:1f:9a:...
a3:8e:2b:...

Each serial number represents one certificate. You can pull its details:

Read the details of one certificate
vault read pki_int/cert/6c:1f:9a:...

Output:

vault read pki_int/cert output
Key                Value
---                -----
certificate        -----BEGIN CERTIFICATE-----
MIIE... (certificate PEM)
-----END CERTIFICATE-----
revocation_time    <nil>
revocation_time_rfc3339 <nil>
state              issued

Notice the state: issued and revocation_time: <nil> columns — signs that this certificate is still active. With an inventory like this, teams no longer need a manual spreadsheet to track "who has which certificate."

But visibility alone isn't enough — the hardest part is automatic renewal. A 24-hour TTL certificate is useless if requesting it is still manual. In production, these patterns are used:

Renewal PatternHow It WorksBest for
Vault Agent templates (episodes 14-15)An Agent beside the app refreshes the cert + key into files, then reloads the serviceApps that read certs from files
Vault SDK in the applicationThe app requests a new certificate before the TTL ends via a libraryApps that want full control
Vault Secrets Operator (episode 17)A Kubernetes operator refreshes certs and restarts pods automaticallyKubernetes workloads

The key to success is one mindset: don't wait for the certificate to expire — schedule renewal well before the TTL ends (for example, at 2/3 of the certificate's lifetime). That way, "expired certificate" changes from an incident to something that never happens.

Common PKI Mistakes

MistakeSymptomSolution
Forgetting to create a roleno matching role error when issuingCreate a role with the right allowed_domains
common_name outside allowed_domainsPermission/rejected errorMake sure the domain matches the role (and allow_subdomains if needed)
Requested TTL exceeds max_ttlTTL gets capped or rejectedCheck vault secrets tune -max-lease-ttl and the role's max_ttl
Only using one mount (no intermediate)Root CA risk of leaking / hard to rotateFollow the Root + Intermediate architecture
Forgetting to save the private key at issuanceCertificate unusable without the keyStore private_key securely immediately after issuance
Not using allow_subdomains for wildcard subdomainsIssuing app.internal.local is rejectedSet allow_subdomains=true on the role
Certificates applied to services without auto-renewalExpiry problem returnsBuild automatic rotation (Vault Agent / SDK) — episodes 14-16

Conclusion

In episode 7, we've covered the PKI Secrets Engine: why expired internal certificates are a real problem, the recommended Root CA + Intermediate CA architecture, the full flow of creating the Root, the intermediate CSR signed by the Root, role creation, and issuing short-lived certificates on-demand. We also looked at CRL, OCSP, revocation mechanics, and the common traps.

The essence of this episode: short-lived certificates turn certificate rotation from a tedious annual event into an automatic daily lifecycle. With a 24-hour TTL, there's no more such thing as a "forgotten expired certificate" — and if a leak happens, the impact is bounded by the certificate's lifetime, not by how fast humans respond.

In episode 8, we'll round out the secrets engines with three additional engines frequently used in the field: TOTP (centralized 2FA), SSH (OTP & CA signing), and AWS (dynamic IAM credentials). Three different security problems, one shared mindset: secrets are not stored, but managed. Keep your enthusiasm up!

Learn Vault - PKI Secrets Engine (Automated X.509 Certificate Authority) | Learn Secret Management with HashiCorp Vault