Today I had an incident that caused my webserver to serve expired certificates. My blog relies on Let’s Encrypt for SSL/TLS certificates, which have to be renewed every 3 months. Usually, the cronjob that runs certbot --renew takes care of it automatically. However, there is one step missing: the server must reload the renewed certificates. Most of the time, the server gets reloaded often enough, so everything is okay, but today, it’s been quite a while since the last time the nginx server was restarted, so expired certificates were served and the blog became unavailable.
To work around it, we can make sure nginx reloads its configuration after each successful certificate renewal. The automatic renewal is defined in /etc/cron.d/certbot. The default contents under Debian Jessie are as follows:
# /etc/cron.d/certbot: crontab entries for the certbot package
#
# Upstream recommends attempting renewal twice a day
#
# Eventually, this will be an opportunity to validate certificates
# haven't been revoked, etc. Renewal will only occur if expiration
# is within 30 days.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 */12 * * * root test -x /usr/bin/certbot && perl -e 'sleep int(rand(3600))' && certbot -q renew
The last line makes sure certificate renewal runs twice a day. Append --renew-hook "/etc/init.d/nginx reload" to it, so it looks like this:
0 */12 * * * root test -x /usr/bin/certbot && perl -e 'sleep int(rand(3600))' && certbot -q renew --renew-hook "/etc/init.d/nginx reload"
The --renew-hook runs the next argument after each successful certificate renewal. In our case, we use it to reload the nginx configuration, which also reloads the newly renewed certificates.
Update 2019-02-27:
renew-hook has been deprecated in recent versions of certbot. Plus, Debian moved from using cronjobs for automatic renewals to a systemd timer if they are available. On the other hand, now certbot supports having hooks in configuration files. So, instead of what is described above, I would suggest creating a file /etc/letsencrypt/renewal-hooks/deploy/01-reload-nginx with the following content:
#! /bin/sh
set -e
/etc/init.d/nginx configtest
/etc/init.d/nginx reload
don’t forget to make the file executable.