Dev Notes

Software Development Resources by David Egan.

Let's Encrypt & Apache on Ubuntu Xenial


Apache, LetsEncrypt, Security, Sysadmin, Ubuntu
David Egan

Let’s Encrypt is a free, automated, and open Certificate Authority. The service is provided by the Internet Security Research Group.

If you’re managing a Virtual Private Server and you want to enable encrypted connection to websites by means of HTTPS(SSL/TLS), Let’s Encrypt is a good choice.

Let’s Encrypt Client/Certbot

Certbot is the name of the client that deploys and manages Let’s Encrypt certificates on your server. Confusingly, depending on your system and the version you install, the client may have a different name.

Certbot on Ubuntu 16.04 Xenial

The package is called python-letsencrypt-apache. To install:

sudo apt-get install python-letsencrypt-apache

This installs Certbot - but you’ll be using the command letsencrypt.

Set Up a New Cert: Apache

Certbot/Letsencrypt has an Apache plugin that sets up the required Apache configuration. To run:

sudo letsencrypt --apache

This triggers a ncurses dialog that prompts you to select the domains for which you’re enabling SSL. You’ll also be prompted to choose whether or not all resources should be served over HTTPS, or if the site should serve mixed content.

As part of the cert setup, existing config files in /etc/apache2/sites-available are used to set up new configurations for port 443 - and a rewrite rule is added to the exisiting config for port 80 (HTTP traffic). Depending on the selections you make during install, the rewrite rule redirects HTTP traffic to HTTPS.

I have noticed one glitch in this process. If the existing config file already contains a rewrite rule, the amended config does not include the necessary RewriteEngine on statement to actually activate the rewrite. You can see an example of this here:

<VirtualHost *:80>
        ServerName example.com
        ServerAlias www.example.com
        ServerAdmin info@example.com
        DocumentRoot /var/www/html/example.com/web
        <Directory /var/www/html/example.com/web>
                Options -Indexes +FollowSymLinks
                AllowOverride None
                Require all granted
        </Directory>
        <Files "wp-login.php">
                Require ip X.X.X.X
        </Files>
        <Directory /var/www/html/example.com/path/to/admin-dir>
                AllowOverride None
                Require ip X.X.X.X
        </Directory>
        # WordPress Config
        <Directory "/var/www/html/example.com/web">
                <IfModule mod_rewrite.c>
                RewriteEngine On
                RewriteBase /
                RewriteRule ^index\.php$ - [L]
                RewriteCond %{REQUEST_FILENAME} !-f
                RewriteCond %{REQUEST_FILENAME} !-d
                RewriteRule . /index.php [L]
                </IfModule>
        </Directory>
        ErrorLog ${APACHE_LOG_DIR}/example.com.error.log
        CustomLog ${APACHE_LOG_DIR}/example.com.access.log combined
RewriteEngine on # This line needed to be added manually
# Rewrite rule added by Letsencrypt - in this case, all traffic on
# this port is sent to the equivalent HTTPS.
RewriteCond %{SERVER_NAME} =example.com [OR]
RewriteCond %{SERVER_NAME} =www.example.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
</VirtualHost>

This isn’t a disaster, but it does mean that you’ll either need to amend the config (and reload the Apache configuration) or amend the root URL in your app (or database) to reference the HTTPS address.

I always amend the app URL in any case - this means that traffic will be sent straight to HTTPS rather than being redirected.

Renew Certificate

Test renewal:

letsencrypt renew --dry-run --agree-tos

If this works, run:

letsencrypt renew --agree-tos

let’s Encrypt suggest running this script twice per day as a cron job on a random minute in the hour. Certificates are valid for 90 days, and renewal won’t proceed unless the certificate is nearing it’s expiry date.

Add a New Site to an Existing Certificate

To achieve this, run:

sudo letsencrypt --apache

…and follow the on-screen instructions. All sites on the server will be listed, and pre-selected for inclusion in the cert. You are then given an option to expand the existing certificate to include the new site. Agree to this and the new site will be added. Don’t forget to check the port 80 Apache config file if redirects are not working.

Common Name

You may end up with the wrong domain name as the common name attached to the certificate. You can force this by running:

sudo letsencrypt --apache -d primary-domain.com -d www.primary-domain.com -d secondary-domain.com -d www.secondary-domain.com --force-renewal

This will result in a certifcate with the common name set to the first domain (in this case, primary-domain.com).

Moving Servers: Client Confusion!

During a recent server upgrade, I moved Let’s Encrypt certificates to the new server. Description here. This is a fairly straightforward process, and allows sites to be migrated without downtime.

However, I experienced a problem when renewing the Certificate.

The old server (Ubuntu 14.04) was running a version of Certbot cloned from the GitHub repo (which is actually a pretty good option - it self updates every time you run it). On the new server (Ubuntu 16.04), I followed the Certbot install instructions for Ubuntu 16.04 as outlined here. This caused problems because the config file (created on the original server) was actually created by a newer version of Certbot than that found in the Ubuntu repos for 16.04. The OLD server had newer software than the NEW server.

The error:

KeyError: 'server'

The fix: You could revoke the cert, remove the client, install Certbot as before and re-install the certificates. In other words, take off and nuke it from orbit (I always wanted to use that expression in a blog post). Alternatively append the following line to your renewal config files (see: /etc/letsencrypt/renewal):

server = https://acme-v01.api.letsencrypt.org/directory

If you have a [[webroot_map]] entry in the renewal config file, add the server line above that.

Kudos to Brad Warren for sharing this solution (ref below) in the Certbot issue tracker.

Remove Cert: The Nuclear Solution

Move into /etc/letsencrypt and remove everything apart from the accounts directory:

cd /etc/letsencrypt
sudo rm -rf archive csr keys live options-ssl-apache.conf renewal

Disable the SSL enabled sites by running:

sudo a2dissite example.com-le-ssl.conf
# ...

Remove all Apache ssl config files from /etc/apache2/sites-available and reload Apache:

cd /etc/apache2/sites-available
sudo rm example.com-le-ssl.conf # etc
sudo service apache2 reload

Note: Unless you’ve removed the redirect in the port 80 virtual host directive, and/or updated your app’s home URL, your site will now be broken. Take care. You should temporarily switch the site to HTTP whilst re-issuing the certificate - I’m assuming you know how to do that.

Run:

sudo letsencrypt --apache

This will reissue the certificate.

This is very much a nuclear option - there are better ways to use the letsencrypt client to amend certificates and settings.

References


comments powered by Disqus