Let's Encrypt with Apache, dovecot, and sendmail

From evermeet.cx Wiki
Jump to navigation Jump to search


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.


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

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 IP_ADDRESS:443>
    ServerName HOSTNAME:443

    # Your "real" configuration here

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')


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
 1 #!/bin/bash
 3 set -e
 7 LOG="/var/log/letsencrypt/renewal.log"
 9 for domain in $RENEWED_DOMAINS; do
10     case $domain in
11         DOMAIN)
12             RESTART_HTTPD=1
13             RESTART_MISC=1
14             DT=`date +"%Y-%m-%d %H:%M:%S %z"`
15             echo "$DT   [ Certificate ]   $domain" >>$LOG
16             # it's the main cert -> set correct permissions
17             chmod 640 "$RENEWED_LINEAGE/privkey.pem"
18             ;;
19         *)
20             RESTART_HTTPD=1
21             DT=`date +"%Y-%m-%d %H:%M:%S %z"`
22             echo "$DT   [ Certificate ]   $domain" >>$LOG
23             ;;
24     esac
25 done
27 if [ "$RESTART_HTTPD" == "1" ]; then
28     DT=`date +"%Y-%m-%d %H:%M:%S %z"`
29     echo "$DT   [   Restart   ]   httpd" >>$LOG
30     /usr/local/apache/bin/apachectl -k graceful
31 fi
33 if [ "$RESTART_MISC" == "1" ]; then
34     DT=`date +"%Y-%m-%d %H:%M:%S %z"`
35     echo "$DT   [   Restart   ]   dovecot, sendmail" >>$LOG
36     systemctl restart dovecot
37     systemctl restart sendmail
38 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