On my Debian mail server and web server, I’m using Let’s Encrypt SSL certificates to provide secure communication between clients and my mail and web services.

In this article, I’ll describe how to setup an automatic SSL certificate renewal solution.

Debian provides the Let’s Encrypt client certbot via its official APT repositories, we can simply install it using: apt install certbot

Setting up the certificates for a given domain is as easy as executing the following command (here, I’m creating a certificate for the common name “mail.heissler.at”, which is the address of my mail server; a similar command has been executed on the web server):

/usr/bin/certbot certonly --webroot --cert-name mail.heissler.at -w /var/www/html -w /var/www/html -d mail.heissler.at

Certbot then communicates with Let’s Encrypt to request the certificates and perform any necessary challenges as defined in the ACME standard (see https://letsencrypt.org/docs/challenge-types/). In our case, ownership can be proven through the HTTP challenge, which automatically adds a file on our web server (e.g. /var/www/html/.well-known/acme-challenge/<token>).

If everything went ok, Certbot confirms that the certificates are enabled and outputs the details.

After deploying these certificates, we must ensure that they are renewed every 3 months.

As the Debian comes with systemd as the init system, a systemd service and timer has been configured and activated during certbot package installation to automatically renew the certificates using the certbot client. Thus, no cron job scheduling is required anymore.

The certbot timer configuration for systemd /etc/systemd/system/timers.target.wants/certbot.timer looks as follows (here, it checks twice a day if certificates are due to renewal):

[Unit]
Description=Run certbot twice daily

[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true

[Install]
WantedBy=timers.target

You can check the status of the certbot systemd timer by executing systemctl status certbot.timer:

● certbot.timer - Run certbot twice daily
   Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; vendor preset: enabled)
   Active: active (waiting) since Thu 2021-06-12 18:40:09 UTC; 7 days ago
  Trigger: Sat 2021-06-19 19:25:28 UTC; 5h 14min left

To ensure, that all services which are relying on the SSL certificates are picking up the changes, I’ve configured certbot to use so-called renew hooks (you can find more details in the offical certbot docs).

I’ve configured the post renewal hook for the mail server as follows in /etc/letsencrypt/renewal-hooks/post/01_install_mail_cert.sh:

#!/bin/bash
set -e
case $RENEWED_DOMAINS in
	*)
		cat $RENEWED_LINEAGE/privkey.pem $RENEWED_LINEAGE/fullchain.pem >/etc/ssl/certs/server-combined.pem
		/bin/systemctl restart exim4.service
		for i in courier-authdaemon.service courier-pop.service courier-imap.service ; do
			/bin/systemctl restart $i
		done
	
	;;
esac

The created server-combined.pem file will be used by the Courier services. It is referenced through a symlink in /etc/courier:

drwxr-xr-x  6 root  courier  4096 Nov 14  2020 .
drwxr-xr-x 87 root  root     4096 Aug 19 12:29 ..
...
lrwxrwxrwx  1 root  root       34 May 11  2019 imapd.pem -> /etc/ssl/certs/server-combined.pem
...
lrwxrwxrwx  1 root  root       34 May 11  2019 pop3d.pem -> /etc/ssl/certs/server-combined.pem

The Exim mail server is configured to use the exim.crt and exim.key files, which are linked to the current letsencrypt fullchain.pem certificate and corresponding private key files.

drwxr-xr-x  3 root root  4096 May  7  2021 .
drwxr-xr-x 87 root root  4096 Aug 19 12:29 ..
...
lrwxrwxrwx  1 root  root   52 Dec 27  2020 exim.crt -> /etc/letsencrypt/live/mail.heissler.at/fullchain.pem
lrwxrwxrwx  1 root  root   50 Dec 27  2020 exim.key -> /etc/letsencrypt/live/mail.heissler.at/privkey.pem
-rw-r--r--  1 root root 49784 Dec 27  2020 exim4.conf
...

The Nginx service on my web server is also configured to use the current certificate and private key for the given domain (a similar letsencrypt files and directory structure has been created there); the default configuration file is located at /etc/nginx/sites-enabled/default:

server {
	listen 80 default_server;
	listen [::]:80 default_server;

	# SSL configuration
	#
	listen 443 ssl default_server;
	listen [::]:443 ssl default_server;
	ssl_certificate /etc/letsencrypt/live/heissler.at/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/heissler.at/privkey.pem;

The letsencrypt renewal post hook configuration /etc/letsencrypt/renewal-hooks/post/01_install_web_cert.sh looks as follows there:

/etc/letsencrypt/renewal-hooks/post/01_install_mail_web_cert.sh
#!/bin/bash
set -e
case $RENEWED_DOMAINS in
	*)
		/bin/systemctl restart nginx
	;;
esac