We need a TLS certificate to encrypt communication. It will be used in two places:
- Postfix (to encrypt SMTP communication where possible)
- Dovecot (to encrypt IMAP communication and deny any unencrypted traffic)
But before requesting a certificate from the CA, we need to choose a hostname for the mail server.
Choosing a Hostname for Mail Server
A certificate is issued for a fully qualified domain name like mail.example.com
and it will be assigned to the Common Name (CN)
attribute in a certificate. This attribute must correspond the hostname during communication, otherwise the certificate will be rejected. But a mail server can be responsible for multiple domain names.
So what to do ?
Solution 1: A generic name and a single certificate
The simple solution is to chose a generic name. Some mail provider uses the name mail.domain-name.com
for all customers and all domains hosted on their mail server.
Most users do not care about the host name as long as they get their emails. So I would generally recommend this approach due to its simplicity.
Solution 2: Multiple names/certificates & SNI
Some people however want to give their mail server as many names as they have domains. Suppose that you have three domains: example1.com
, example2.com
and example3.com
. Users of example1.com
can use mail.example1.com
while users of example2.com
use mail.example2.com
.
As you can imagine this would not work if you had a single certificate because the latter can contain only one CN. If your CN is mail.example1.com
then talking to your server by the name mail.example2.com
would lead to a certificate error.
So your mail server needs multiple certificates – one for each host name. And depending on how a user connects to your server you need to send the matching certificate. If the user wants to speak to mail.example1.com
then your server needs to send the certificate with CN mail.example1.com
.
Fortunately that problem has been addressed by a technique called Server Name Indication (SNI)
. A client talks to the server and even before the encrypted connection is established it tells the server that it expects a certificate for the intended host name. The server can then load the matching certificate and initiate the encryption process.
SNI has long been a problem for mail servers. The mail user agent (e.g. Thunderbird) needs to support it as well as Postfix and Dovecot. Postfix has finally added SNI in version 3.4 so that we can use it.
While adding multiple host names needs extra work, it also has a benefit. If you want to move domains to other servers (e.g. when upgrading your server) you can move one domain at a time.
Obtain a Let's Encrypt Certificate
Here we are using Let's Encrypt as a Certificate Authority to sign the certificate we are going to generate. We will use the certbot tool to request an encryption certificate from LetsEncrypt and it will ask us to prove ownership of the domain we are requesting a certificate for.
The process of obtaining the certificate will be as follows:
- Certbot creates a private key and a certificate request. It sends the certificate request to the LetsEncrypt server.
- The LetsEncrypt server replies with a challenge/token.
- Certbot puts that token into a file in the
/var/www/webmail.example.org/.well-known/acme-challenge
directory. - The LetsEncrypt server does an HTTP connection to http://example.com/.well-known/acme-challenge/... and expects to find that token. This verifies that you are in charge of the domain and the web server.
- If all works well the LetsEncrypt server signs your certificate request and thus creates the actual certificate.
- Certbot receives the certificate and puts it into
/etc/letsencrypt/archive/webmail.example.org/
Method 1 — Generating a LetsEncrypt Certificate (Apache Web Server)
You need to have an Apache virtual host for mail.example.com
before obtaining Let’s Encrypt TLS certificate. Create the virtual host file:
sudo nano /etc/apache2/sites-available/mail.your-domain.com.conf
Then add the following text into the file:
<VirtualHost *:80>
ServerName mail.example.com
DocumentRoot /var/www/html/
</VirtualHost>
Save and close the file. Enable this virtual host:
sudo a2ensite mail.your-domain.com.conf
Then disable the default virtual host, because it might interfere with other virtual hosts:
sudo a2dissite 000-default
Reload Apache for the changes to take effect:
sudo systemctl reload apache2
Let’s check if the configuration works. Put a test file into your web root directory:
echo "Just a test" > /var/www/example.com/test
Finally send a HTTP request to http://mail.example.com/test endpoint to test the web server.
Now we are ready request a certificate for the domain:
certbot certonly --webroot --webroot-path /var/www/example.com -d example.com
-d
: Domain name
Method 2 — Generating a LetsEncrypt Certificate (Standalone)
sudo certbot certonly -d noradrenaline.atomicl.net -m vselvarajah@atomicl.net --standalone --agree-tos --no-eff-email
--agree-tos
: Agree to terms of service.--no-eff-email
: Don’t receive emails from EFF foundation.-d
: Host for which requesting a certificate.
Automatic Certificate Renewal
The certbot package automatically adds a timed job that runs twice a day at random times. The random part is important to avoid millions of server hammering the LetsEncrypt service at the same second.
Cerbot uses Systemd timer instead of a Cron job. You can find the timer definition in the /lib/systemd/system/certbot.timer
file. That timer triggers the renewal service defined in /lib/systemd/system/certbot.service
. To find out if the service has run and when the next occurence will be, run systemctl status certbot.timer
.
There is also a Cron file at /etc/cron.d/certbot
but it do nothing due to the -d /run/systemd/system
condition that checks if systemd is installed. And majority of GNU/Linux systems nowadays uses systemd.
So the renewal already happens automatically. Should it fail then LetsEncrypt start sending you reminder emails that your certificate should be renewed. That’s a clear sign that something went wrong with the automatic renewal.
There is one puzzle piece missing though. Even if the renewal worked, it will only update the certificate files. But the software components – Postfix and Dovecot – will not notice the change. So we need to add a so called post-hook to certbot that triggers a restart of all processes thereafter.
For that purpose edit the /etc/letsencrypt/cli.ini
file and add:
post-hook = systemctl restart postfix dovecot apache2
You have implemented Let’s Encrypt for all your services now. Let’s move on.