A new service is born: Let’s Encrypt. It offers free SSL certificates that you can use for web servers, email servers or whatever service you want to secure with TLS. This blog post presents my strategy to automate certificate creation and renewal. Please, install Let’s Encrypt on your web server box before you start to follow the presented strategy.
The key to success is to have Let’s Encrypt running without any further interaction. I use webroot authentication – which allows me to leave the productive web service up and running while the certificates are being issued or renewed. Therefore, I created a file named “myserver.ini” in folder /etc/letsencrypt. This configuration file contains all details that are required for the certification process;
1 2 3 4 5 6 7 8 | rsa-key-size = 4096 authenticator = webroot webroot-path = /path/to/webroot/ server = https://acme-v01.api.letsencrypt.org/directory renew-by-default = True agree-tos email = <my-email-address> domains = domain1.com, domain2.com |
The second component of my strategy is the central piece: a script called “renewCertificates.pl”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #!/usr/bin/perl my $DOMAINS = { 'myserver' => { 'configFile' => '/etc/letsencrypt/myserver.ini', 'leSubDir' => 'domain1.com', 'certDir' => '/var/www/domain1.com/certs', }, }; my $domain; my $renewed = 0; chdir ('/usr/local/letsencrypt'); foreach $domain (keys(%{$DOMAINS})) { print "INFO - $domain - START\n"; my $cmd = '/usr/local/scripts/checkCertExpiry.sh 30 '.$DOMAINS->{$domain}->{'certDir'}.'/cert.pem >/dev/null'; my $rc = system($cmd); if ($rc) { $cmd = './letsencrypt-auto certonly --config '.$DOMAINS->{$domain}->{'configFile'}.' --renew-by-default'; $rc = system($cmd); if (!$rc) { $cmd = 'cp /etc/letsencrypt/live/'.$DOMAINS->{$domain}->{'leSubDir'}.'/* '.$DOMAINS->{$domain}->{'certDir'}.'/'; $rc = system($cmd); if ($rc) { print "ERROR - $domain - Cannot deploy\n"; } else { print "INFO - $domain - Deployed\n"; $renewed = 1; } } else { print "ERROR - $domain - Cannot generate certificates\n"; } } else { print "INFO - $domain - Certificate does not expire within 30 days\n"; } print "INFO - $domain - END"); } if ($renewed) { system("/etc/init.d/apache2 reload"); } exit 0; |
This scripts allows renewal of multiple certificates by supporting multiple configurations. Lines 3-9 describe these configurations. leSubDir (line 6) is the sub directory that Let’s Encrypt creates in the certification process. It is the name of the first domain specified in the configuration file, here: domain1.com. certDir (line 7) is the target path where the certificates will be deployed to.
A second script supports this procedure by telling whether a certificate will expire within a certain number of days (see line 16 above):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #!/bin/bash # First parameter specifies if certificate expire in the next X days DAYS=$1 target=$2 if [ ! -f "$target" ]; then echo "Certificate does not exist (RC=2)" exit 2; fi openssl x509 -checkend $(( 86400 * $DAYS )) -enddate -in "$target" >/dev/null 2>&1 expiry=$? if [ $expiry -eq 0 ]; then echo "Certificate will not expire (RC=0)" exit 0 else echo "Certificate will expire (RC=1)" exit 1 fi |
This script returns 0 when the given certificate will not expire, otherwise it returns a non-0 value. The Perl script above will renew certificates 30 days before expiration only.
The last piece is the Apache configuration to be used on these domains:
1 2 3 4 | SSLEngine on SSLCertificateFile /var/www/domain1.com/certs/cert.pem SSLCertificateKeyFile /var/www/domain1.com/certs/privkey.pem SSLCertificateChainFile /var/www/domain1.com/certs/fullchain.pem |
I run the central Perl script above daily and do not need to worry about certificates anymore 🙂