Automate Certificates with Vault PKI
What is the Vault PKI secret engine
Vault is an open-source tool by HashiCorp that provides secrets management, encryption as a service, and advanced data protection. It helps organizations secure, store, and control access to tokens, passwords, certificates, and encryption keys. Vault offers a unified interface to access different secret engines and provides granular access control to ensure only authorized entities can access sensitive data.
The Vault PKI (Public Key Infrastructure) secret engine allows Vault to act as a certificate authority (CA), enabling it to issue, manage, and revoke digital certificates. This engine simplifies the processes involved in PKI by providing a set of APIs to interact with and automate certificate lifecycle management.
Vault PKI secret engine can be integrated with an Automated Certificate Management Environment (ACME) client to automate the issuance and renewal of TLS certificates. ACME is a protocol for automating interactions between certificate authorities (CAs) and servers.
Using Traefik Gateway ACME certificate resolvers, you can use Vault PKI secret engine for automatic issuance, validation, and renewal or TLS certificates.
Setting Up Vault PKI for ACME
ACME certificate lifecycle management protocol is supported starting on Vault v1.14.
Using the Vault PKI secret engine we are going to setup two CAs on two different mount paths:
- Root CA: The highest level of trust in a PKI hierarchy.
- Intermediate CA: Operate under the Root CA and is responsible for issuing ACME certificates.
Having multiple CAs will allow you to tweak certificate lifespan and reduce exposure.
In the following steps, we assume that the Vault server is reachable on http://vault:8200
.
First, let's start with the Root CA:
# Enable the PKI secret engine on the `/pki` mount path.
vault secrets enable pki
# Tweak certificate lifespan.
vault secrets tune -max-lease-ttl=8760h pki
# Generates a new self-signed root CA certificate and save it under `./root_ca.crt`.
vault write -field=certificate pki/root/generate/internal \
common_name="example.com" \
issuer_name="root" \
ttl=87600h > root_ca.crt
# Configure the cluster path and AIA path (Required by the ACME feature).
vault write pki/config/cluster \
path=http://vault:8200/v1/pki \
aia_path=http://vault:8200/v1/pki
# Create the role that will be used for issuing certificates from the Root CA.
vault write pki/roles/servers \
allow_any_name=true \
no_store=false
# Configure the issuing certificate endpoints, CRL distribution points, and OCSP server endpoints that
# will be encoded into issued certificates
vault write pki/config/urls \
issuing_certificates={{cluster_aia_path}}/issuer/{{issuer_id}}/der \
crl_distribution_points={{cluster_aia_path}}/issuer/{{issuer_id}}/crl/der \
ocsp_servers={{cluster_path}}/ocsp \
enable_templating=true
Then, we can continue with the intermediate CA:
# Enable the PKI secret engine on the `/pki_int` mount path.
vault secrets enable -path=pki_int pki
# Tweak certificate lifespan, shorter than the root CA.
vault secrets tune -max-lease-ttl=43800h pki_int
# Generate an intermediate certificate and save the CSR under `./pki_intermediate.csr`.
vault write -format=json pki_int/intermediate/generate/internal \
common_name="example.com Vault Intermediate Authority" \
issuer_name="intermediate" \
| jq -r '.data.csr' > pki_intermediate.csr
# Sign the intermediate certificate with the root CA private key, and save the generated certificate
# under `./intermediate.cert.pem`.
vault write -format=json pki/root/sign-intermediate \
issuer_ref="root" \
csr=@pki_intermediate.csr \
format=pem_bundle ttl="43800h" \
| jq -r '.data.certificate' > intermediate.cert.pem
# Imported the signed intermediate certificate.
vault write pki_int/intermediate/set-signed certificate=@intermediate.cert.pem
# Configure the cluster path and AIA path (Required by the ACME feature).
vault write pki_int/config/cluster \
path=http://vault:8200/v1/pki_int \
aia_path=http://vault:8200/v1/pki_int
# Create the role that will be used for issuing certificates from the Intermediate CA.
vault write pki_int/roles/intermediate \
issuer_ref="$(vault read -field=default pki_int/config/issuers)" \
allow_any_name=true \
max_ttl="720h" \
no_store=false
# Configure the issuing certificate endpoints, CRL distribution points, and OCSP server endpoints that
# will be encoded into issued certificates.
vault write pki_int/config/urls \
issuing_certificates={{cluster_aia_path}}/issuer/{{issuer_id}}/der \
crl_distribution_points={{cluster_aia_path}}/issuer/{{issuer_id}}/crl/der \
ocsp_servers={{cluster_path}}/ocsp \
enable_templating=true
# Configure the pki_int engine so it handle ACME requests correctly.
vault secrets tune \
-passthrough-request-headers=If-Modified-Since \
-allowed-response-headers=Last-Modified \
-allowed-response-headers=Location \
-allowed-response-headers=Replay-Nonce \
-allowed-response-headers=Link \
pki_int
And finally, enable the ACME feature on the intermediate CA:
vault write pki_int/config/acme enabled=true
Setting Up Traefik Gateway
Once Vault server is configured, it can be used as a CA in an ACME certificate resolver. You can choose to use a non-distributed or a distributed ACME certificate resolver, as explained in the Let's Encrypt page.
To keep the example short, we are going to show the configuration for a non-distributed ACME certificate resolver.
A new certificate resolver needs to be defined with the caServer
option. This option defines which CA will be responsible for
issuing certificates. In our case, the pki_int
secret engine on the Vault server.
certificatesResolvers:
my-resolver:
acme:
email: "[email protected]"
storage: "/path/to/acme.json"
caServer: "http://vault:8200/v1/pki_int/acme/directory"
httpChallenge:
entryPoint: "web"
The certificate resolver can then be used on the Ingress objects that needs certificates
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: whoami
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls.certresolver: my-resolver
spec:
rules:
- host: my-domain.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: whoami
port:
number: 80
Related Content
- See more information about the certificates resolvers in Traefik Hub API Gateway.
- See more information about Vault PKI engine consideration.
- See more information about Vault PKI with external account binding (EAB).