Automating the letsencrypt certificate renewal
In my previous post I explain how to set up nginx running in a docker container to use a SSL certificate created by letsencrypt. The problem with that approach is that you need to update the certificate manually once every 90 days. Is nearly 100% sure that you will forget to do that, at least once. In this post, I will show you how to automatize the letsencrypt certificate renewal process with some easy steps.
For the renewal of the certificate, we'll use another of the strategies/plugins that letencrypt client support, the webroot plugin. This plugin obtains the certificate by writing a special file in the /.well-known directory within your document root of an already running webserver. The special file will then be opened (through your web server) by the Let's Encrypt service for validation. Depending on your configuration, you may need to explicitly allow access to the /.well-known directory. To ensure that the directory is accessible to Let's Encrypt for validation, you can update the nginx configuration by adding the following inside the server node.
location ~ /.well-known {
allow all;
}
The letsencrypt client support using a configuration file instead of parameters. This is ideal for this scenario where we plan to automatize the letsencrypt certificate renewal process, as it will be easier to update any of the parameter without needing to update the scripts. First you will need to create the configuration file, to do this, copy the one from the examples in the client folder.
sudo cp /opt/letsencrypt/examples/cli.ini /usr/local/etc/le-renew-webroot.ini
Edit the new file with your information. Note that in addition to the email and domains parameter, you need to specify the webroot-path parameter. This parameter specify the path to the folder that nginx (or your web server) is serving. In this case, that path is the local path that is linked to the /usr/share/nginx/html folder in docker.
rsa-key-size = 4096
email = [email protected]
domains = nbellocam.me, www.nbellocam.me
webroot-path = /local/path/to/www
Now that you have everything in place, test the configuration by executing the following command. Remember that nginx needs to be running and configured as described in my previous post.
/opt/letsencrypt/letsencrypt-auto certonly -a webroot --agree-tos --renew-by-default --config /usr/local/etc/le-renew-webroot.ini
This command is the same as the one below, with the difference that one uses the configuration file while the other uses configuration by parameter.
/opt/letsencrypt/letsencrypt-auto certonly -a webroot --agree-tos --renew-by-default --webroot-path=/local/path/to/www --email [email protected] -d nbellocam.me -d www.nbellocam.me
Now, that everything is in place, you will be able focus on automatize the letsencrypt certificate renewal process. In order to do this, create a new file, e.g. /usr/local/sbin/le-renew-webroot and add execution permissions.
sudo touch /usr/local/sbin/le-renew-webroot sudo chmod +x /usr/local/sbin/le-renew-webroot
Now, edit the new file and add the following snippet, replacing the value of the compose_file_path and config_file variables.
#!/bin/bash
compose_file_path='/path/to/your/nginx/docker-compose.yml'
config_file="/usr/local/etc/le-renew-webroot.ini"
le_path='/opt/letsencrypt'
exp_limit=30;
if \[ ! -f $config_file \]; then
echo "\[ERROR\] config file does not exist: $config_file"
exit 1;
fi
domain=\`grep "^\\s\*domains" $config_file | sed "s/^\\s\*domains\\s\*=\\s\*//" | sed 's/(\\s\*)\\|,.\*$//'\`
cert_file="/etc/letsencrypt/live/$domain/fullchain.pem"
if \[ ! -f $cert_file \]; then
echo "\[ERROR\] certificate file not found for domain $domain."
fi
exp=$(date -d "\`openssl x509 -in $cert_file -text -noout|grep "Not After"|cut -c 25-\`" +%s)
datenow=$(date -d "now" +%s)
days_exp=$(echo \\( $exp - $datenow \\) / 86400 |bc)
echo "Checking expiration date for $domain..."
if \[ "$days_exp" -gt "$exp_limit" \] ; then
echo "The certificate is up to date, no need for renewal ($days_exp days left)."
exit 0;
else
echo "The certificate for $domain is about to expire soon. Starting webroot renewal script..."
$le_path/letsencrypt-auto certonly -a webroot --agree-tos --renew-by-default --config $config_file
echo "Reloading $web_service"
docker-compose -f $compose_file_path restart
echo "Renewal process finished for domain $domain"
exit 0;
fi
Test the script by executing it with sudo /usr/local/sbin/le-renew-webroot
. You should see the output saying that the certificate is up to date. The idea is to renew it just when its necessary.
Finally, add a new cron task to execute the le-renew-webroot script. To do this, execute sudo crontab -e
and add the following line that will execute the le-renew-webroot command every Monday at 2:30 am. The output produced by the command will be piped to a log file located at /var/log/le-renewal.log.
30 2 \* \* 1 /usr/local/sbin/le-renew-webroot >> /var/log/le-renewal.log
Now that you configure and automatized the full letsencrypt certificate renewal process, you can relax and enjoy the benefits of having a free and automatically updated Let's Encrypt TLS/SSL certificate to securely serve HTTPS content.