How to use LetsEncrypt certs with email

I’m using the most excellent Dehydrated utility, which is both easy to use and lightweight (few dependencies).

These instructions assume you have control of a web server that can serve requests for the same domain as the mail server.

  • Define MAILHOST to the FQDN of your mail host.
  • Define WEBROOT to the directory of your mail server host in your web server, e.g., export WEBROOT=/var/www/htdocs/$MAILHOST

First, grab Dehydrated. I put it in /usr/local:

cd /usr/local
sudo git clone https://github.com/lukas2511/dehydrated

LetsEncrypt offers a couple of ways of verifying that you own the domain, but the one I’m using depends on LetsEncrypt being able to query a specific asset on the server. To support this, create a .well-known/acme-challenge directory in the web root for the mail server domain of your web server:

sudo mkdir $WEBROOT/.well-known/acme-challenge

Now, configure Dehydrated with that informtion. Per the dehydrated instructions, you also need to put your domain(s) in a file called domains.txt.

cd dehydrated
sudo cp docs/examples/config .
sudo sed -i "s%^#WELLKNOWN=.*%WELLKNOWN=$WEBROOT/.well-known/acme-challenge%" config
echo $MAILHOST > domains.txt

I also set a value for CONTACT_EMAIL. For testing, you should also change to the LetsEncrypt staging server; in case you have to run multiple times, you don’t want to be blacklisted:

sudo sed -i 's%^#CA=.*%CA="https://acme-staging.api.letsencrypt.org/directory"%' config

Run dehydrated:

cd /usr/local/dehydrated
sudo ./dehydrated -c

You now should have certificates in the /usr/local/dehydrated/certs directory. You can reference these directly with both Dovecot and Postfix. Dovecot accesses certs as root; however, Postfix’s TLS agent runs as postfix, so it can’t read the certs which dehydrated sets to 700 for perms. I weakened the security of the certs a bit by making them group readable for the postfix group. This is all on Ubuntu; it may be different for other distributions.

sudo hgrp -R postfix certs
sudo find certs -type d -exec chmod 750 {} \;
sudo find certs -type f -name \*.pem -exec chmod 640 {} \;

Now, it’s just telling Dovecot and Postfix where to find the certs, and restart the services:

# Dovecot
sudo sed -i "s%^ssl_cert.*%ssl_cert = </usr/local/dehydrated/certs/$MAILHOST/fullchain.pem%" /etc/dovecot/dovecot.conf
sudo sed -i "s%^ssl_key.*%ssl_key = </usr/local/dehydrated/certs/$MAILHOST/privkey.pem%" /etc/dovecot/dovecot.conf
# Postfix
sudo sed -i "s%^smtpd_tls_cert_file.*%smtpd_tls_cert_file = /usr/local/dehydrated/certs/$MAILHOST/fullchain.pem%" /etc/postfix/main.cf
sudo sed -i "s%^smtpd_tls_key_file.*%smtpd_tls_key_file  = /usr/local/dehydrated/certs/$MAILHOST/privkey.pem%" /etc/postfix/main.cf
sudo service dovecot restart
sudo service postfix restart

Now test:

# Dovecot
openssl s_client -connect mail.germane-software.com:993
# Postfix
openssl s_client -connect mail.germane-software.com:587

If everything is working, change the LetsEncrypt server back to the non-staging server and re-run dehydrated with -x (force renew) to get a real cert:

sudo sed -i 's%^CA=%#CA=%' config
sudo ./dehydrated -x
sudo service dovecot restart
sudo service postfix restart

Test again, and openssl should stop complaining about being unable to find the SSL root certificate for Dovecot, at least. able to find the SSL root certificate for Dovecot, at least.