Let's Encrypt with Apache, dovecot, and sendmail
Introduction
Let's Encrypt makes it easy to get your own SSL/TLS certificates. There are numerous clients available to automate the retrieval and installation of certificates. The recommended client is certbot which also provides different plugins to configure/change the web server config files and restart the server automatically.
However, if you have concerns (like me) that a program is messing around with your precious config files, worry not. One can use certbot
to only retrieve the certificates as well.
The sections below show my settings for dovecot and sendmail as a reference. I read up on that topic and all of the answers and blog posts are actually wrong or miss the background.
certbot
The certbot
utility supports several plugins, but most of them reconfigure and restart the web server during the issuance process and then restart the web server again afterwards. This might be useful for personal web servers, but is utterly useless for production web servers that are continously used.
The webroot
plugin creates a temporary file in ${webroot-path}/.well-known/acme-challenge
which is validated by the CA during the issuance process by accessing this location via port 80. If you have a lot of virtual hosts this can be taxing, but I have created a config that uses a single dedicated directory on the server for the ACME challenge.
Put the following in a separate file (e.g. /etc/httpd/extra/acme-and-redirect.conf
) and replace YOUR_DEDICATED_PATH
:
Alias "/.well-known/acme-challenge/" "/YOUR_DEDICATED_PATH/acme-challenge/.well-known/acme-challenge/"
<Directory "/YOUR_DEDICATED_PATH/acme-challenge/">
Require all granted
</Directory>
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/.*
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [QSA,L,R=301]
Now you can use the following configuration for all your virtual hosts:
<VirtualHost IP_ADDRESS:80>
ServerName HOSTNAME:80
Include /etc/httpd/extra/acme-and-redirect.conf
</VirtualHost>
<VirtualHost IP_ADDRESS:443>
ServerName HOSTNAME:443
# Your "real" configuration here
</VirtualHost>
This allows you to use standard HTTP for the challenge protocol (as required by certbot) and all other traffic will be forwarded to HTTPS.
See also the renew-hook script.
dovecot (dovecot.conf
)
ssl_cert = </etc/letsencrypt/live/CERTNAME/fullchain.pem ssl_key = </etc/letsencrypt/live/CERTNAME/privkey.pem
sendmail (sendmail.mc
)
define(`CERT_DIR', `/etc/letsencrypt/live/CERTNAME') define(`confCACERT_PATH', `CERT_DIR') define(`confCACERT', `CERT_DIR/fullchain.pem') define(`confSERVER_CERT', `CERT_DIR/cert.pem') define(`confSERVER_KEY', `CERT_DIR/privkey.pem') define(`confCLIENT_CERT', `CERT_DIR/cert.pem') define(`confCLIENT_KEY', `CERT_DIR/privkey.pem') define(`confDONT_BLAME_SENDMAIL',`groupreadablekeyfile')dnl
sendmail does not like the key with permission 644
, thus it starts up, but it does not accept TLS connections.
To solve this problem, one has to set the permission of the private key as follows:
chmod 640 /etc/letsencrypt/live/CERTNAME/privkey.pem
As for confCACERT
: this parameter is supposed to hold the CA bundle, but most systems do not have a recent version that does include the CA and intermediary certs of Letsencrypt. There are several options to solve this problem:
- download a newer ca-bundle (curl has usually very recent ones, mozilla is also a good source)
- add the letsencrypt root and intermediate certs to your bundle manually
- use a workaround (other would call it a hack) and use the fullchain.pem, since it includes all necessary certs
The confCLIENT_*
parameters are not really needed but don't hurt either. Letsencrypt does not provide client certs so setting up a "local" CA that allows people to use client certs to authenticate against sendmail is pretty much useless.
Sendmail also likes to complain about certs that are group readable. Either change the permission of the certifcates to 600
, or use the confDONT_BLAME_SENDMAIL
directive.
In any case, the renewal of certs will change the permissions back to 644
and sendmail will stop working again. Two options are available:
- ask the developers of certbot to change the certbot script to set the correct permission for the private key
- use a
--renew-hook
script to do the work
renew-hook script
You can run the following renewal command every day. The renew-hook script is only called, when at least one certificate has been renewed.
./certbot-auto renew -q --no-self-upgrade --renew-hook /usr/local/sbin/renew-hook.sh
#!/bin/bash
set -e
RESTART_HTTPD=0
RESTART_MISC=0
LOG="/var/log/letsencrypt/renewal.log"
for domain in $RENEWED_DOMAINS; do
case $domain in
DOMAIN)
RESTART_HTTPD=1
RESTART_MISC=1
DT=`date +"%Y-%m-%d %H:%M:%S %z"`
echo "$DT [ Certificate ] $domain" >>$LOG
# it's the main cert -> set correct permissions
chmod 640 "$RENEWED_LINEAGE/privkey.pem"
;;
*)
RESTART_HTTPD=1
DT=`date +"%Y-%m-%d %H:%M:%S %z"`
echo "$DT [ Certificate ] $domain" >>$LOG
;;
esac
done
if [ "$RESTART_HTTPD" == "1" ]; then
DT=`date +"%Y-%m-%d %H:%M:%S %z"`
echo "$DT [ Restart ] httpd" >>$LOG
/usr/local/apache/bin/apachectl -k graceful
fi
if [ "$RESTART_MISC" == "1" ]; then
DT=`date +"%Y-%m-%d %H:%M:%S %z"`
echo "$DT [ Restart ] dovecot, sendmail" >>$LOG
systemctl restart dovecot
systemctl restart sendmail
fi
Explanation of the script
Line | Description |
---|---|
7 | Location and name of the log file |
11 | DOMAIN is the name of the domain used for sendmail and dovecot (and maybe for the web server). If you use different domain names, separate them with | .
|
17 | change permissions of the key certificate to 640 (TLS in sendmail won't work when cert is world readable)
|
30 | command to restart the web server |