Let's Encrypt!

Let's Encrypt is a free certificate authority that was launched earlier this year. What that means is you no longer have to choose between self-signed certificates or buying certificates to secure your web site and other services. Buying certificates could cost $20-$50 per domain, and it was always a hassle to renew them. Now, you can use the Let's Encrypt tools to create certificates and automate renewals. Using these certificates also means an end to annoying 'untrusted site' popups for your users.

My Setup

I have a CentOS 6 box running apache for a webmail interface. This machine also uses sendmail to send and receive mail for the domain, and supports encrypted imap via dovecot. My goal was to use the same certificate (named mail.example.com) to encrypt all three.

Getting Started

First, I followed the guide to download and install certbot, the Let's Encrypt client. Highlights:

# mkdir /root/bin
# cd /root/bin
# wget https://dl.eff.org/certbot-auto
# chmod a+x certbot-auto
# /root/bin/certbot-auto --apache --email [email protected] --agreetos --domain mail.example.com certonly

Note that I used the certonly option to only generate the certificates, not install them in apache. I initially let certbot automatically configure my apache, but it got all confused about my virtual host setup. It still worked, but ultimately it seemed simpler to do the config file editing myself.

The other options bypass the questions in the automated installer. The address you give the --email should be a valid address which can receive mail. The --domain argument specifies the domain you are creating the certificate for. This confused me at first, because I thought I was supposed to use the actual dns domain (example.com). What you actually need to put here is the name your webserver presents to the outside world, in this case mail.example.com. This name will be used to generate your SSL certificates and any service using it will have to use the same name. Wildcard certificates are not supported. In my case this wasn't a problem because apache, sendmail, and dovecot all run on the same box as mail.example.com. Obviously if your services run under different dns names then you will need to call certbot with multiple domain arguments to generate certs for each one.

Certbot will validate that your webserver actually is authoritative for your domain, and then it will issue you a certificate. Once that process is done, your new certificate is in /etc/letsencrypt/live/mail.example.com.

Setting Up Apache

I have one virtual host in apache, and it's serving the Squirrelmail webmail client. I of course want to encrypt all traffic to and from this webserver because potentially sensitive information is involved (user mail). To make that happen, I configured my VirtualHost entries in /etc/httpd/conf/http.conf as follows:

<VirtualHost *:443>
  ServerName mail.example.com
  ServerAdmin [email protected]
  DocumentRoot /usr/share/squirrelmail
  ErrorLog     /var/log/httpd/mail.example.com-mail-error_log
  CustomLog    /var/log/httpd/mail.example.com-mail-combined_log combined

  SSLEngine on
  SSLCertificateChainFile /etc/letsencrypt/live/mail.example.com/chain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/mail.example.com/privkey.pem
  SSLCertificateFile /etc/letsencrypt/live/mail.example.com/cert.pem

<VirtualHost *:80>
  ServerName mail.example.com
  RedirectMatch permanent ^/(.*) https://mail.example.com/$1

The virtual host on port 80 redirects all traffic to port 443 to force https. The server on port 443 uses my Let's Encrypt certificates to ssl-encrypt all connections.

Once I restarted my apache server, I found that everything worked automatically and all my web traffic was now encrypted. So far so good.

Setting Up Dovecot

I use dovecot as an imap server so that users can read mail externally with a client like thunderbird. Again, this needs to be encrypted. Previously I had been using self-signed certificates. To fix that, all I had to do was edit my /etc/dovecot/dovecot.conf and point it at my new certificates:

ssl = required
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem

After I restarted dovecot, I found that imap clients could connect on port 993 and use the new Let's Encrypt certificate with no problems.

Setting Up Sendmail

Yes, I realize everyone will tell me to use postfix, but this is an old linux installation and I've never gotten around to converting. So, I've got to figure out how to set up the latest version of sendmail to use my certifcate. Since I'm not insane, I don't hand-edit sendmail.cf, I use /etc/mail/sendmail.mc. Here are the changes I made to that file:

define(`CERT_DIR', `/etc/letsencrypt/live/mail.example.com')
define(`confCACERT_PATH', `CERT_DIR')
define(`confCACERT', `CERT_DIR/cert.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')

After that, I ran make in /etc/mail and restarted sendmail. However, it didn't work! Sendmail printed the message

sendmail[28624]: STARTTLS=server: file /etc/letsencrypt/live/mail.example.com/privkey.pem unsafe: World readable file

and refused to use the certificates. I did a bunch of unsatisfactory googling about this, and the consensus was that sendmail was more paranoid about file and directory permissions than Let's Encrypt. I made two changes. First, I added the following to sendmail.mc:


and then I restricted permissions on the key file (/etc/letsencrypt/archive/mail.example.com/privkey1.pem) to mode 640. The combination of those two things seemed to do the trick, and sendmail stopped complaining on startup.

Automated Renewals

The final part of the puzzle was to set up automated renewals. Let's Encrypt certificates expire in 90 days, so you have to be prepared to renew them frequently. Fortunately, certbot makes this easy. I created a script /etc/cron.daily/certbot with the following:


# auto-renew letsencrypt certificates
/root/bin/certbot-auto renew --quiet --no-self-upgrade

and made that script mode 700 (owned by root). Since it's in the cron.daily directory, it gets run every day. It's fine to run it every day because certbot won't actually do anything until it's time to renew the certificate.


That's all there was to it! My server is now using an offically signed certifcate for web, imap, and mail traffic. My users get a better experience, and I don't have to ever worry about renewing certifcates. Overall I'm pretty impressed with Let's Encrypt.


Our Founder
ToolboxClick to hide/show