Nicolas Bello Camilletti

Automating the letsencrypt certificate renewal

March 11, 2016 - 4 min read

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. letsencrypt nginx in docker

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.



Nicolas Bello Camilletti

Written by Nicolas Bello Camilletti (@nbellocam). Developer. Geek. Speaker. Always looking for new technologies. I work at SOUTHWORKS. Microsoft MVP and Google Developer Expert (GDE).