cat > pki.policy <<EOA
# Enable secrets engine
path "sys/mounts/*" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
# List enabled secrets engine
path "sys/mounts" {
capabilities = [ "read", "list" ]
}
# Work with pki secrets engine
path "pki*" {
capabilities = [ "create", "read", "update", "delete", "list", "sudo" ]
}
EOA
vault policy write pki pki.hcl
vault token create -policy=pki -field=token
For this demo we will allow users to operate with the intermediate, which is a best practice. If desired, the root could be used instead, but it would limit your flexibility of revoking the certificate.
echo $VAULT_ADDR
echo $VAULT_TOKEN
vault secrets enable pki
vault secrets tune -max-lease-ttl=87600h pki
# Generate the root certificate and save the certificate in CA_cert.crt.
# This generates a new self-signed CA certificate and private key.
# Vault will automatically revoke the generated root at the end of its lease period (TTL);
# the CA certificate will sign its own Certificate Revocation List (CRL).
# This assumes no root certificate exists. If it does, you will need to explicitly delete before recreating using this command.
vault write -field=certificate pki/root/generate/internal common_name="example.com" \
ttl=87600h > CA_cert.crt
# Print the validity dates
openssl x509 -in CA_cert.crt -noout -dates
# Configure the CA and CRL URLs:
vault write pki/config/urls \
issuing_certificates="$VAULT_ADDR/v1/pki/ca" \
crl_distribution_points="$VAULT_ADDR/v1/pki/crl"
# Generate intermediate
vault secrets enable -path=pki_int pki
vault secrets tune -max-lease-ttl=43800h pki_int
vault write -format=json pki_int/intermediate/generate/internal \
common_name="example.com Intermediate Authority" ttl="43800h" \
| jq -r '.data.csr' > pki_intermediate.csr
# Sign intermediate with root
vault write -format=json pki/root/sign-intermediate csr=@pki_intermediate.csr \
format=pem_bundle \
| jq -r '.data.certificate' > intermediate.cert.pem
# Store intermediate - check for alternative approach here https://www.vaultproject.io/api/secret/pki/index.html#generate-intermediate
vault write pki_int/intermediate/set-signed [email protected]
# Create a role
# A role is a logical name that maps to a policy used to generate those credentials.
# It allows configuration parameters to control certificate common names, alternate names,
# the key usages that they are valid for, and more.
vault write pki_int/roles/example-dot-com \
allowed_domains="example.com" \
allow_subdomains=true \
generate_lease=true \
max_ttl="720h"
# NOTE: Generate_lease is used to allow Consul template to renew this certificate. However if a large number of certificates is generated, this will have a performance impact. The default is false, not generating a Vault lease per certificate.
# Request certificate
vault write pki_int/issue/example-dot-com common_name="test.example.com" ttl="24h"
#-> The response contains the PEM-encoded private key, key type and certificate serial number.
vault write pki_int/revoke serial_number=<serial_number>
In one terminal window, download and run consul template as a process. With this configuration it will request a short lived certificate, and renew it before half the ttl is reached
wget https://releases.hashicorp.com/consul-template/0.19.5/consul-template_0.19.5_linux_amd64.zip
unzip consul-template_0.19.5_linux_amd64.zip
cat > cert.pem.tpl <<EOA
{{ with secret "pki_int/issue/example-dot-com" "common_name=test2.example.com" "ttl=10s" }}
{{ .Data.certificate }}{{ end }}
EOA
# NOTE: Consul-template will issue error messages if you are using the root token. Please create a token with a limited policy as described above. This behavior is because consul-template will try to renew its own VAULT_TOKEN, and the root token cannot be renewed.
consul-template \
-template "cert.pem.tpl:cert.pem:echo 'realoded cert!'"