Issue, Deploy and Renew your Private Certificates with Vault and Consul-Template

Stéphane Este-Gracias
6 min readSep 28, 2022

Automate your Certificates Lifecycle with Vault — Part 2

Photo by Joshua Sortino on Unsplash

This article is a part of a series about how to
Automate your Certificates Lifecycle with Vault.

  1. Build an Internal PKI with Vault
  2. Issue, Deploy and Renew your Private Certificates with Vault and Consul-Template (this article)
  3. Rotate your CA seamlessly using a Vault PKI
  4. Securely store Public Certificates in Vault, generated by acme.sh
  5. Codify Vault Internal PKI using Terraform

Introduction

This article contains a walkthrough to issue, deploy and renew your private certificates using the Vault PKI built in the previous article of this series.

Pre-Requisites

Consul-Template

Consul-Template is a template rendering daemon that queries a Consul, Vault, or Nomad cluster and updates any number of specified templates on the filesystem.

Consul-Template and Vault

Consul-Template uses the Go Template format with additional functions. The dedicated functions for Vault are secret, secrets and pkiCert.

Before using pkiCert, read the Special Note carefully to understand its usage. The rendered file is used as a cache and must contain the certificate.

For instance, here are the following templates that issue the private key, the certificate, and the CA chain for the issued leaf certificate test.example.com. The last one queries the CA chain stored in pki_iss without issuing any leaf certificate.

{{- with secret "pki_iss/issue/example" "common_name=test.example.com" -}}
{{ .Data.private_key }}
{{- end -}}
{{- with secret "pki_iss/issue/example" "common_name=test.example.com" -}}
{{ .Data.certificate }}
{{- end -}}
{{- with secret "pki_iss/issue/example" "common_name=test.example.com" -}}
{{ .Data.ca_chain }}
{{- end -}}
{{- with secret "pki_iss/cert/ca_chain" -}}
{{ .Data.ca_chain }}
{{- end -}}

Issue and Deploy Certificate

First of all, the consul-template daemon should be configured with a dedicated configuration file:

  • to connect to the Vault server
  • to generate files from specified templates

Read the configuration documentation for further information

Here is a configuration file that uses the exemple role (built in the previous article) to generate cert.key, cert.crt and cert.ca files related to an issued certificate for test.example.com.

Disclaimer: On production
- Don’t use the root token
- Create auth methods, entities, groups and policies to grant or forbid access to PKI features

$ cat <<EOF > consul-template.hcl
vault {
address = "http://127.0.0.1:8200"
token = "root"
renew_token = false
}
template {
contents = <<EOH
{{- with secret "pki_iss/issue/example" "common_name=test.example.com" -}}
{{ .Data.private_key }}
{{- end }}
EOH
destination = "cert.key"
}
template {
contents = <<EOH
{{- with secret "pki_iss/issue/example" "common_name=test.example.com" -}}
{{ .Data.certificate }}
{{- end }}
EOH
destination = "cert.crt"
}
template {
contents = <<EOH
{{- with secret "pki_iss/issue/example" "common_name=test.example.com" -}}
{{- range .Data.ca_chain -}}
{{ . }}
{{ end -}}
{{- end -}}
EOH
destination = "cert.ca"
}
EOF

Start consul-template, but only once, not in a daemon mode to check the generated files. Next, check the rendered files.

$ consul-template -config=consul-template.hcl -log-level info -once
[INFO] consul-template v0.29.2 (06389a3d)
[INFO] (runner) creating new runner (dry: false, once: true)
[INFO] (runner) creating watcher
[INFO] (runner) starting
[INFO] (runner) rendered "(dynamic)" => "cert.key"
[INFO] (runner) rendered "(dynamic)" => "cert.crt"
[INFO] (runner) rendered "(dynamic)" => "cert.ca"
[INFO] (runner) once mode and all templates rendered
[INFO] (runner) stopping
$ cat cert.key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJm+Ep54nx2009V3EXIl5l4TmnEvsr4RiFxp7ryQFcS8oAoGCCqGSM49
AwEHoUQDQgAEVfh8VvQ/eKeKqvIYHvu59cpAr+G/aXU5xKhd//gn2wdBmaUCyZdj
aqtUo1muPKjAhROaNukXFu8DeJYJJoEJ2Q==
-----END EC PRIVATE KEY-----
$ cat cert.crt
-----BEGIN CERTIFICATE-----
MIICfjCCAiSgAwIBAgIUT0UMUxONeUwDTrK8wrpZCoeDJ9AwCgYIKoZIzj0EAwIw
QDEVMBMGA1UEChMMRXhhbXBsZSBMYWJzMScwJQYDVQQDEx5FeGFtcGxlIExhYnMg
SXNzdWluZyBDQSB2MS4xLjEwHhcNMjIwOTE5MTQ0MjU0WhcNMjIxMDIxMTQ0MzI0
WjAyMRUwEwYDVQQKEwxFeGFtcGxlIExhYnMxGTAXBgNVBAMTEHRlc3QuZXhhbXBs
ZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARV+HxW9D94p4qq8hge+7n1
ykCv4b9pdTnEqF3/+CfbB0GZpQLJl2Nqq1SjWa48qMCFE5o26RcW7wN4lgkmgQnZ
o4IBCDCCAQQwDgYDVR0PAQH/BAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr
BgEFBQcDAjAdBgNVHQ4EFgQUWV8iJwDXTuATSIYPrd1QYjr3fi0wHwYDVR0jBBgw
FoAU3j+4uC3xM3gfb2709xpS0G9Jp8UwPwYIKwYBBQUHAQEEMzAxMC8GCCsGAQUF
BzAChiNodHRwOi8vMTI3LjAuMC4xOjgyMDAvdjEvcGtpX2lzcy9jYTAbBgNVHREE
FDASghB0ZXN0LmV4YW1wbGUuY29tMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly8x
MjcuMC4wLjE6ODIwMC92MS9wa2lfaXNzL2NybDAKBggqhkjOPQQDAgNIADBFAiB0
asvZHDOB/XQGkEkd9OCGtXL8lbEDsacvxckhzWUmYQIhAIlVoA10TSZk8pbOc3p2
6o0MxJV7FRfSLSZHwngiWaAr
-----END CERTIFICATE-----
$ cat cert.ca
-----BEGIN CERTIFICATE-----
MIICYDCCAgagAwIBAgIUfzDvhr6caTp7VORPG2TMW1YxW/kwCgYIKoZIzj0EAwIw
PjEQMA4GA1UEChMHRXhhbXBsZTEqMCgGA1UEAxMhRXhhbXBsZSBMYWJzIEludGVy
bWVkaWF0ZSBDQSB2MS4xMB4XDTIyMDkxOTE0MTkxMloXDTIzMDkxOTE0MTk0Mlow
QDEVMBMGA1UEChMMRXhhbXBsZSBMYWJzMScwJQYDVQQDEx5FeGFtcGxlIExhYnMg
SXNzdWluZyBDQSB2MS4xLjEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASlLm4w
h3+pun+oJ1LMvJ22rZRgxSPOkJJPBCUKONeM6n5Q0HF4ntcfzW0WHJfYDZWy1J0C
D0sFTMTMye5DSAQSo4HfMIHcMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG
AQH/AgEAMB0GA1UdDgQWBBTeP7i4LfEzeB9vbvT3GlLQb0mnxTAfBgNVHSMEGDAW
gBTWcE1m0LHFXpHrZqaGizi1A1PjtzA/BggrBgEFBQcBAQQzMDEwLwYIKwYBBQUH
MAKGI2h0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2lfaW50L2NhMDUGA1UdHwQu
MCwwKqAooCaGJGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2lfaW50L2NybDAK
BggqhkjOPQQDAgNIADBFAiAOkTLPtqcMIhm7MxRJ+TEUlqo6uyeBSgMSdlTslL3q
xwIhALzEQPavjZDWvMqrqv++RPaEO2Jn9LqIrIDMjRwHwzFX
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIB1zCCAX2gAwIBAgIRAOOa8Aj6bj5pqA/EADwcgRQwCgYIKoZIzj0EAwIwNDEQ
MA4GA1UEChMHRXhhbXBsZTEgMB4GA1UEAxMXRXhhbXBsZSBMYWJzIFJvb3QgQ0Eg
djEwHhcNMjIwOTE5MTQwOTQyWhcNMjcwOTE5MTQxOTQyWjA+MRAwDgYDVQQKEwdF
eGFtcGxlMSowKAYDVQQDEyFFeGFtcGxlIExhYnMgSW50ZXJtZWRpYXRlIENBIHYx
LjEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASBVFRRBZk5UAk5Cc6ikbrexDaQ
+HqhGEoANTQni7aGAPIBL7jnTjk9XP4qovrdtWCfc5xc4Q3DsZmmCuJieqbko2Yw
ZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQU
1nBNZtCxxV6R62amhos4tQNT47cwHwYDVR0jBBgwFoAUU8mUMKg2/sC1c1b+QhK3
MQkdqXAwCgYIKoZIzj0EAwIDSAAwRQIga3l340j1PM+2DCno8mXqJg0fpc8a01Cf
KyxEpOG9t14CIQDO0AIX9uIcg7bvraAIGMjQ7iEXMo36Op0gt/gI3XUymQ==
-----END CERTIFICATE-----
$ openssl x509 -in cert.crt -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
4f:45:0c:53:13:8d:79:4c:03:4e:b2:bc:c2:ba:59:0a:87:83:27:d0
Signature Algorithm: ecdsa-with-SHA256
Issuer: O = Example, CN = Example Labs Issuing CA v1.1.1
Validity
Not Before: Sep 19 14:42:54 2022 GMT
Not After : Oct 21 14:43:24 2022 GMT
Subject: O = Example, CN = test.example.com
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:55:f8:7c:56:f4:3f:78:a7:8a:aa:f2:18:1e:fb:
b9:f5:ca:40:af:e1:bf:69:75:39:c4:a8:5d:ff:f8:
27:db:07:41:99:a5:02:c9:97:63:6a:ab:54:a3:59:
ae:3c:a8:c0:85:13:9a:36:e9:17:16:ef:03:78:96:
09:26:81:09:d9
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment, Key Agreement
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Auth
X509v3 Subject Key Identifier:
59:5F:22:27:00:D7:4E:E0:13:48:86:0F:AD:DD:50:62:...
X509v3 Authority Key Identifier:
keyid:DE:3F:B8:B8:2D:F1:33:78:1F:6F:6E:F4:F7:1A:...
Authority Information Access:
CA Issuers - URI:http://127.0.0.1:8200/v1/pki_iss/ca
X509v3 Subject Alternative Name:
DNS:test.example.com
X509v3 CRL Distribution Points:
Full Name:
URI:http://127.0.0.1:8200/v1/pki_iss/crl
Signature Algorithm: ecdsa-with-SHA256
30:45:02:20:74:6a:cb:d9:1c:33:81:fd:74:06:90:49:1d:f4:
e0:86:b5:72:fc:95:b1:03:b1:a7:2f:c5:c9:21:cd:65:26:61:
02:21:00:89:55:a0:0d:74:4d:26:64:f2:96:ce:73:7a:76:ea:
8d:0c:c4:95:7b:15:17:d2:2d:26:47:c2:78:22:59:a0:2b

Renew Certificate

By default, the certificate (as a non-renewable secret) is automatically renewed by Consul-Template at 90% of its TTL. The default TTL has been set to one month in theexamplerole. To test more often, let’s reduce the TTL to 30 seconds to see a renewal of the certificate (ttl=30s).

Consul-Template can optionally run arbitrary commands when the template is rendered and the output has changed. The exec block in the configuration should be added to execute a command.

exec {
command = [ "systemctl", "reload”, "nginx" ]
timeout = “30s”
}

So, you can reload or restart a service or an application that uses the certificate. For the sake of the demo, the command outputs the rendered template (cat cert.key) or the parsed certificate (openssl x509 -in cert.crt -issuer -subject -startdate -enddate -noout).

Here is the updated consul-template configuration file.

$ cat <<EOF > consul-template.hcl
vault {
address = "http://127.0.0.1:8200"
token = "root"
renew_token = false
}
template {
contents = <<EOH
{{- with secret "pki_iss/issue/example" "common_name=test.example.com" "ttl=30s" -}}
{{ .Data.private_key }}
{{- end }}
EOH
destination = "cert.key"
exec {
command = [ "cat", "cert.key" ]
}
}
template {
contents = <<EOH
{{- with secret "pki_iss/issue/example" "common_name=test.example.com" "ttl=30s" -}}
{{ .Data.certificate }}
{{- end }}
EOH
destination = "cert.crt"
exec {
command = [
"openssl", "x509", "-in", "cert.crt",
"-issuer", "-subject", "-startdate", "-enddate",
"-noout"
]
}
}
template {
contents = <<EOH
{{- with secret "pki_iss/issue/example" "common_name=test.example.com" "ttl=30s" -}}
{{- range .Data.ca_chain -}}
{{ . }}
{{ end -}}
{{- end -}}
EOH
destination = "cert.ca"
exec {
command = [ "cat", "cert.ca" ]
}
}
EOF

So, let’s start the consul-template daemon.

$ consul-template -config=consul-template.hcl
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDe+3rXfvouGK/bymTvuCG++mVtJU+V5/VF2/ouBrp1moAoGCCqGSM49
AwEHoUQDQgAERpPVrgsyT3OvTfKqxEkFqSklcKYShrWuWHlZpWwNTa0WMHS6eTvm
1Ly7WLeMkMPHgsoalMEjFx6HIVgWZjEvAw==
-----END EC PRIVATE KEY-----
issuer=O = Example, CN = Example Labs Issuing CA v1.1.1
subject=O = Example, CN = test.example.com
notBefore=Sep 19 16:30:24 2022 GMT
notAfter=Sep 19 16:31:24 2022 GMT
-----BEGIN CERTIFICATE-----
MIICWzCCAgGgAwIBAgIUTknVQDg1TmMqIYU/H4Z0j9q6q2IwCgYIKoZIzj0EAwIw
PjEQMA4GA1UEChMHRXhhbXBsZTEqMCgGA1UEAxMhRXhhbXBsZSBMYWJzIEludGVy
bWVkaWF0ZSBDQSB2MS4xMB4XDTIyMDkxOTE1MDQzOFoXDTIzMDkxOTE1MDUwOFow
OzEQMA4GA1UEChMHRXhhbXBsZTEnMCUGA1UEAxMeRXhhbXBsZSBMYWJzIElzc3Vp
bmcgQ0EgdjEuMS4xMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWlU5PUeiItWC
YyltWZaS4W8fNANZkSjmd2oShXp2nSVDwN4+Dvf2ixFdzUWbklo5cBRLZxweL59c
hbYsbVgL9qOB3zCB3DAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIB
ADAdBgNVHQ4EFgQUqUDW46qmyyNELSER3ILqIXbmgOIwHwYDVR0jBBgwFoAU1v5V
z/G4Ai+KsgSqLatba/3vJMIwPwYIKwYBBQUHAQEEMzAxMC8GCCsGAQUFBzAChiNo
dHRwOi8vMTI3LjAuMC4xOjgyMDAvdjEvcGtpX2ludC9jYTA1BgNVHR8ELjAsMCqg
KKAmhiRodHRwOi8vMTI3LjAuMC4xOjgyMDAvdjEvcGtpX2ludC9jcmwwCgYIKoZI
zj0EAwIDSAAwRQIhALbt1mZx0pLyGMO8hyUDHfQpRAah0c3dZn5vusIW+vcCAiAz
/WTjjxMm5QeaEh9KBLQrcXXN2jXV7PI886sLBE/xCQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIB2DCCAX2gAwIBAgIRAMlAuLmxYK0hqR4uaheiDMowCgYIKoZIzj0EAwIwNDEQ
MA4GA1UEChMHRXhhbXBsZTEgMB4GA1UEAxMXRXhhbXBsZSBMYWJzIFJvb3QgQ0Eg
djEwHhcNMjIwOTE5MTQ1NTA4WhcNMjcwOTE5MTUwNTA4WjA+MRAwDgYDVQQKEwdF
eGFtcGxlMSowKAYDVQQDEyFFeGFtcGxlIExhYnMgSW50ZXJtZWRpYXRlIENBIHYx
LjEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATBv71in6dt4FYoFIUWPEl8/PTk
N7vIryxD2sff7PY421erP9ZlWjG3dG5+1kFvHUrp26PGBYid304oVPIYWPHIo2Yw
ZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQU
1v5Vz/G4Ai+KsgSqLatba/3vJMIwHwYDVR0jBBgwFoAUwkn7K1yzMRL7FLDIlZxg
7z5RvXYwCgYIKoZIzj0EAwIDSQAwRgIhAIgYXf1QhKLiAZxp08viP0jU57qRkNMP
xYSDwURq/rvoAiEAuq3Wytby/RsaMUvPhjOT0CR7fim9Pwaw1W5HdZ1WWg0=
-----END CERTIFICATE-----

After 27 seconds (=30*0.90), the following output is shown. Only the new cert.key and cert.crt are displayed because the CA chain is unchanged.

-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBn/Km4pnfB8OTOfmiSjHeqUtfdUtQVJ/kSX1pdRwM05oAoGCCqGSM49
AwEHoUQDQgAEALSYUgTvSjZw8Eb5gEL7RLtcpDSpi1neCM2dHXY+XbeKeDKWxYP3
DUHYu0A5mJZFzt/Yy/CZFkg50JABNPRyjg==
-----END EC PRIVATE KEY-----
issuer=O = Example, CN = Example Labs Issuing CA v1.1.1
subject=O = Example, CN = test.example.com
notBefore=Sep 19 16:30:50 2022 GMT
notAfter=Sep 19 16:31:50 2022 GMT

Conclusion

This article shows how to issue, deploy and renew your private certificates using Vault PKI and Consul-Template.

Consul-Template features are also available in:

  • Vault Agent on VM
  • Vault Agent on Kubernetes (using vault-k8s)
  • Nomad

Vault Agent can use Auto-Auth to simplify the generation of the required token to query Vault.

Nomad replaces the exec block with thechange_modeparameter that specifies the behavior Nomad should take if the rendered template changes.

On top of this, if you have to manage multiple nodes and applications, Nomad is easier than Consul-Template daemons or Vault Agent to configure and manage.

Next Steps

The next article (Part 3) explains how to rotate your CA seamlessly.

References

--

--