7.2 KiB
ACME - Automatic HTTPS Certificates
Until there is a custom puppet module to configure ACME via certbot, it is possible to do this with already existing Hiera/Puppet features. Below you find two configuration examples:
- normal webserver (here
nginx) - docker service (also
nginx)
Note that instead of nginx this can be adapted to any other type of (web)service requiring TLS/SSL certificates and these are the steps needed to configure certbot.
Configuration with docker is down below.
Before starting the actual configuration you need to get for your team the "EAB-KID" (account ID) and "EAB-HMAC-KEY" (password) from the Network Team.
Configuration for Webserver/Webservice
- Best place the "EAB-KID" and the "EAB-HMAC-KEY" encrypted as variables
acme::eab-kidandacme::eab-hmac-keyon your top level YAML file so that they are available on all your nodes if needed. Please ensure you encrypt them as string and not as files as newlines which got encrypted will break the script. - Install the required software, at least
certbot. - Prepare a renewal script required to roll out new certificates to your (web)server and place it in
/etc/letsencrypt/renewal-hooks/post/ - Create a service which initializes
certbotwith all the required configuration. If you want to use more alias domain names, add them at the end of thecertbotcommand as additional--domainparameters. At the end this initial command should also call the renewal-hook script to prepare the (web)server. - Enable the
certbot-renewtimer which automatically keeps the certificates updated and will call the renewal-hook when required.
Full Hiera example:
# these variables are best configured in the top level YAML file of your Hiera environment
acme::eab-kid: >
ENC[PKCS7,MIIBmQYJ ...
acme::eab-hmac-key: >
ENC[PKCS7,MIIBygYJ ...
# Install certbot and nginx packages
base::pkg_group::extra:
- "nginx"
- "certbot"
base::package_groups:
- "extra"
files::files:
# Post hook to reload nginx after certificate renewal
/etc/letsencrypt/renewal-hooks/post/nginx.sh:
mode: "0544"
content: |
#!/bin/bash
systemctl reload nginx
# Initial setup of certbot
/usr/local/libexec/certbot-initial:
mode: "0500"
content: |
#!/bin/bash
# initial certificate order and setup on the first run only
[ -f /etc/letsencrypt/live/%{::fqdn}/fullchain.pem ] && exit 0
/usr/bin/certbot certonly --standalone \
--server https://one.digicert.com/mpki/api/v1/acme/v2/directory \
--non-interactive --agree-tos --register-unsafely-without-email \
--cert-name %{::fqdn} --new-key \
--eab-kid %{lookup('acme::eab-kid')} \
--eab-hmac-key %{lookup('acme::eab-hmac-key')} \
--domain %{::fqdn} \
--domain my-alias.psi.ch \
&& /etc/letsencrypt/renewal-hooks/post/nginx.sh
# Service which sets up certbot
base::services:
'certbot-initial':
enable: true
options:
Unit:
After: 'network-online.target'
Wants: 'network-online.target'
Install:
WantedBy: 'multi-user.target'
RequiredBy: 'multi-user.target'
Service:
Type: 'oneshot'
ExecStart: '/usr/local/libexec/certbot-initial'
RemainAfterExit: 'true'
# Enable certbot renewal timer
base::timers:
certbot-renew:
enable: true
!!! note
if you are using RHEL8 then --new-key is not supported yet by certbot.
!!! note
if you are using RHEL10 then substitutions like %{::fqdn} have to be replaced by StructuredFacts like %{facts.networking.fqdn}.
Keys managed by certbot will be saved in /etc/letsencrypt/live:
Certificate: /etc/letsencrypt/live/<your-domain>.psi.ch/fullchain.pem
Key: /etc/letsencrypt/live/<your-domain>.psi.ch/privkey.pem
Either configure your webserver to use the generated certificates and keys from there, or link them to the location that is expected by your webserver configuration. Your renewal script may also copy the files instead and also change permissions so it fits whatever the (web)server needs.
!!! note Important! Best use fullchain.pem as some tools like curl test might not have the necessary intermediate certificates!
Additional aliases can be added to the config by adding additional --domain <name> options in the /usr/local/libexec/certbot-initial file.
If you want to do changes, e.g. to the aliases, then best revoke the old certificate:
certbot revoke --cert-name $FQDN
or whatever was the original --cert-name.
Then adapt the configuration in Hiera and run Puppet. By restarting the certbot-initial.service (or rebooting) it will get a new certificate with the new configuration:
puppet agent -t
systemctl restart certbot-initial.service
Configuration with Docker
There are several images that use certbot integrated into whatever service you are using. (nginx for example) These images are not always maintained by the official project therefore I'd not recommend using them unless you know what you are doing and trust the maintainer.
In this documentation the nginx container is called nginx, please be aware of that when copying the documentation!
To get your certificates inside a container you need to volume mount /etc/letsencrypt/live into your container, where you would expect your certificates to be. Be careful, the certificate and key are in the same folder!
Also you need to "manually" reload the webserver using the certificate, when "ordering" a certificate and when its renewed by certbot. Below are the steps needed. The code blocks assume that you configured the cli.ini file with puppet module files:files already.
nginx:
image: nginx:latest
container_name: nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- /etc/letsencrypt/archive:/etc/letsencrypt/archive:ro
- /etc/letsencrypt/live:/etc/letsencrypt/live:ro
And point your virtual host configuration to the correct folder:
server {
listen 80;
server_name myhost.psi.ch;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name myhost.psi.ch;
ssl_certificate /etc/letsencrypt/live/myhost.psi.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myhost.psi.ch/privkey.pem;
location / {
proxy_pass https://myapp:443;
}
}
Getting the certificate with reloading the nginx inside the container, create a file (mine is called get-cert.sh)
And execute ./get-cert.sh myhost.psi.ch manually for each certificate you need.
/root/get-cert.sh
mode: 0744
content: |
#!/bin/bash
set -e
# Check for domain argument
if [ -z "$1" ]; then
echo "Usage: $0 <domain>"
exit 1
fi
DOMAIN="$1"
# Run Certbot with certonly
certbot certonly \
-d "$DOMAIN" \
--config /etc/letsencrypt/cli.ini
# Reload Nginx in the Docker container
docker exec nginx nginx -s reload
And create the post renewal hook:
/etc/letsencrypt/renewal-hooks/post/nginx.sh:
mode: '0544'
content: |
#!/bin/bash
docker exec nginx nginx -s reload