SSL/TLS enabled websites with LetsEncrypt

Let's face it; in today's world, it's much easier to make a case for setting up SSL/TLS support on your website then to not. And no, Self-Signed certificates don't count. That's fine for your home router or one-man OwnCloud site, but if you care about the data being served up, get yourself some (properly signed) SSL certs!

The problem used to be that if you wanted SSL certs, you had to pay, sometimes a lot. Verisign/Symantec certs can start in the $400 range and go up from there. Conversely Comodo SSL certs start at $79.95. Now, we won't go into the "market topology" of why Verisign is more expensive than Comodo, but to some extent you get what you pay for.

LetsEncrypt aims to change that. They are currently (June2016) offering certificates for free (valid for 90 days). Period. This coalition (most notably backed by Mozilla, Cisco, EFF, and others) are aiming to put an SSL cert into the hands of anybody who wants one.

There will be a TLDRScript™ at the end of this, but keep in mind, the initial setup with certbot requires answering to interactive prompts, so you can't truly script it. Additionally, use that script with caution, it will add custom configs to your apache setup, and that may not be what you want, Caveat emptor.

Setup

Depending on which set of directions you follow from the letsencrypt website, the client (certbot-auto) might be able to obtain and install your certificate in one fell swoop, automatically. Depending on your web server setup and its complexity, that may be completely fine for you. In the setup below, we will have an Apache web server that will be acting both a traditional web server hosting up static content, as well as a reverse proxy for some other applications (maybe even a blog)! As usual, the following assumptions are made:

  • CentOS 7.latest (7.2.1511 at the time of writing)
  • Apache 2.4 (latest)
    • mod_ssl (latest)
  • Apache installed and running with typical vhost style configuration
  • SELinux enabled
  • FirewallD enabled

Getting the warez

First thing we need to do is download certbot-auto from EFF and put it in your path:

wget https://dl.eff.org/certbot-auto
chmod a+x ./certbot-auto
mv certbot-auto /usr/local/sbin/.

Laying down configs

From here, you'll need to lay down your Apache config and create the necessary directories. Take note that all of these commands/configs should be used on your forward most web server (proxy or otherwise), as that is the web server that will be providing the SSL connection to your clients. Let's make some directories:

mkdir /var/www/acme/
restorecon -vr /var/www/acme/
chown apache.apache /var/www/acme/

And laydown an Apache config:

/etc/httpd/conf.d/000-zeroent.net.conf

<VirtualHost *:443>  
        ServerName zeroent.net
        SSLEngine On
        SSLProtocol all -SSLv2 -SSLv3
        SSLHonorCipherOrder On
        SSLCipherSuite ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS:!RC4

        DocumentRoot /var/www/html
        #SSLCertificateChainFile /etc/letsencrypt/live/zeroent.net/chain.pem
        #SSLCertificateFile /etc/letsencrypt/live/zeroent.net/cert.pem
        #SSLCertificateKeyFile /etc/letsencrypt/live/zeroent.net/privkey.pem
        SSLCertificateFile /etc/pki/tls/certs/localhost.crt
        SSLCertificateKeyFile /etc/pki/tls/private/localhost.key

</VirtualHost>

<VirtualHost *:80>  
        ServerName zeroent.net
        DocumentRoot /var/www/html

        RedirectMatch 301 ^/((?!\.well-known).*)$ https://zeroent.net
        Alias /.well-known/ "/var/www/acme/.well-known/"
        <Directory "/var/www/acme/.well-known/">
                Options FollowSymLinks
                AllowOverride None
                Require all granted
        </Directory>

</VirtualHost>  

The Alias statement sends any requests for the .well-known directory to a separate location, the same location we'll specify in the certbot-auto command. This allows you to not touch your web application and still have certbot work. Additionally, if you're trying to do this for a reverse proxied site, add the following to the config (near the proxy line):

        ProxyPass /.well-known !

This will prevent apache from sending ACME protocol requests to your proxied site.

The RedirectMatch statements lets everything get redirected to your HTTPS site page except the .well-known directory, which the ACME protocol (used by certbot-auto) uses to put answers to the challenge-response queries. It is important to note, this directory must never get auto-redirected to an HTTPS site, with or without a valid SSL cert; the letsencrypt.org ACME server will barf and your cert creation/renewal will fail.

Note

The SSL portion of that config can be obtained from Mozilla's SSL configuration generator. They'll make best-practice recommendations on which ciphers to allow and which ones to omit. Want to verify your config? Head over to SSLLabs to test your website.

At this point, we should [re]start/enable Apache, and add some firewall exception:

systemctl start httpd  
systemctl enable httpd  
firewall-cmd --add-service=http --permanent  
firewall-cmd --add-service=https --permanent  
firewall-cmd --reload  

Running certbot-auto

Let't get some certs! Note that the -auto version of the certbot script will automatically install any packages necessary for it to successfully run using your system's package manger (in our case YUM).

certbot-auto certonly --webroot -w /var/www/acme -d zeroent.net

If you want to get multiple domains covered by a single cert (with Subject Name Alternatives), you can use multiple -d arguments like so:

certbot-auto certonly --webroot -w /var/www/acme -d zeroent.net -d blag.zeroent.net -d www.zeroent.net

Note that the first specified domain will be the "primary" domain on the cert.

Once you run the certbot-auto command, it will go through requesting the certs, it will prompt you for an e-mail address to register them to so in the event that they need to contact you (certs coming close to expiring, need to be revoked, etc.), they have a means of contacting you. If you are setting this up in a production/enterprise environment, I'd recommend using an alias like [email protected] rather than [email protected]; nothing is more embarrassing than having your production SSL certs expire and not knowing it was going to happen.

Assuming everything goes successfully, you'll see a message like this

IMPORTANT NOTES:  
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/zeroent.net/fullchain.pem. Your cert
   will expire on 2017-03-25. To obtain a new or tweaked version of
   this certificate in the future, simply run certbot-auto again. To
   non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Using the new certificates.

Now that you have the new certs, you need to get Apache to use them. Simply comment out the lines pointing to localhost and point to the new cert files created by certbot:

        SSLCertificateChainFile /etc/letsencrypt/live/zeroent.net/chain.pem
        SSLCertificateFile /etc/letsencrypt/live/zeroent.net/cert.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/zeroent.net/privkey.pem
        #SSLCertificateFile /etc/pki/tls/certs/localhost.crt
        #SSLCertificateKeyFile /etc/pki/tls/private/localhost.key

Now, lets move on to sustainment.

Certificate Renewal

Any process worth doing is worth automating, and that's just what we're going to do here. Because we're cool and use SystemD (instead of a cronjob), we'll lay down SystemD service and timer units that will check your certificates to determine if they need renewal, and if so, renew them. Note, by default, certbot renews the certs when they are 30 days from expiring. So if something goes wrong with automation, you have some breathing room to get your certs in order.

The service that executes the certbot - /etc/systemd/system/le-renew.service

[Unit]
Description=LetsEncrypt certificate renew job  
Wants=le-renew.timer

[Service]
Type=simple  
ExecStart=/usr/local/sbin/certbot-auto renew --post-hook "systemctl restart httpd"

[Install]
WantedBy=basic.target  

The timer that runs that service twice a day at 1 AM and PM - /etc/systemd/system/le-renew.timer

[Unit]
Description=LetsEncrypt Renew 2/day timer

[Timer]
OnCalendar=*-*-* 1,13:19:45  
Persistent=true  
Unit=le-renew.service

[Install]
WantedBy=basic.target  

Once you've gotten those unit files in place, poke SystemD to recognize them and start the timer.

systemctl daemon-reload  
systemctl enable le-renew.timer  
systemctl start le-renew.timer  

If you need to check the status of the last run (determine if any certificates were renewed, any errors occurred, etc.), use the friendly JournalD to see the logs.

journalctl -u le-renew.service -e  

If you ever want to manually trigger a certbot-auto run, you can just start the le-renew.service

systemctl start le-renew.service