While my domain registrar provided me with a single SSL certificate included with the cost of my registration, I had run across a couple of situations where I needed a second certificate for some CNAMEs and had to use self-signed certificates.

I had read about the EFF’s Let’s Encrypt project, but their documentation for FreeBSD was (as of this writing) not very thorough, and despite a thorough Googling I was not able to find a good explanation of how to get things set up.

My server is set up with Apache running in a FreeBSD jail.

I decided to install the Let’s Encrypt client from Ports.

% cd /usr/ports/security/py-certbot/
% sudo make install clean

Next, I verify that I know the full path for the folder my jailed Apache server is serving up as the root for my domain. In this case, it’s

/usr/jails/httpd/usr/local/www/danm.io/

The FreeBSD Port of CertBot doesn’t incorporate all of the features that the online man page says it does, so we’ll have to do a few extra, manual steps. For starters, let’s fire off CertBot so that it can prove to the EFF that we own the domain that we want a certificate for. It will do this by adding a random file to our web root folder, then instructing the EFF’s server to fetch the file. Assuming this is successful, CertBot will clean up the files that it added and download our certificate.

% sudo certbot certonly --webroot --webroot-path /usr/jails/httpd/usr/local/www/danm.io/ -d www.danm.io -d danm.io

The “certonly” option tells CertBot not to try to fiddle with our Apache config or anything of that sort - just get us a certificate! The –webroot option tells CertBot that we’re already running an http server on Port 80, so it doesn’t need to run it’s own (and if it tries, it’ll fail in my case since pf is re-routing all traffic on Port 80 to the jail with Apache). –webroot-path is the full path to the web root that the jailed Apache is serving up. Finally -d is used to specify which domain names we want certificates for. You can request as many certificates for a given domain as you need, but you will need to specify each one separately with a -d.

Assuming everything went as expected, you should now have four certificate files in /usr/local/etc/letsencrypt/live// : cert.pem, chain.pem, fullchain.pem, and privkey.pem.

Let’s copy these files to a location that’s accessible from our Apache jail.

% sudo cp -L -R /usr/local/etc/letsencrypt/live/ /usr/jails/httpd/usr/local/etc/apache24/certs/

The -L option is required in this case, since the certificate files in the /usr/local/etc/letsencrypt/live/ are actually symlinks. The -L option will force the actual file to be copied, which is important since just copying the link will be useless, as the jail does not have access to the file linked to.

Now let’s configure our Apache server to use our new certificates! fullchain.pem and privkey.pem should be the only two files you actually need.

LoadModule ssl_module modules/mod_ssl.so

Listen 443
<VirtualHost *:443>
	ServerName danm.io
	SSLEngine on
	SSLCertificateFile "/usr/local/etc/apache24/certs/danm.io/fullchain.pem"
	SSLCertificateKeyFile "/usr/local/etc/apache24/certs/danm.io/privkey.pem"
</VirtualHost>

And restart Apache.

If you haven’t set up Apache with SSL before, I highly suggest you read up on how to properly configure everything. Good resources include Raymii’s Strong SSL Security tutorial and of course the official Apache docs. This sort of configuration is beyond the scope of this blog post, and the above snippet of an Apache config file is just to illustrate how to specify which cert files to use.

It’s important to realize that the Let’s Encrypt certificates only last 90 days. The CertBot script has the ability to automatically renew your certificates if they will expire within the next 30 days, so let’s write a shell script that we can fire off via cron or periodic on a weekly basis:

#/bin/sh

/usr/local/bin/certbot renew

if ! diff "/usr/local/etc/letsencrypt/live/danm.io/fullchain.pem" \
"/usr/jails/httpd/usr/local/etc/apache24/certs/danm.io/fullchain.pem"  >/dev/null 2>&1; then
        cp -L -R /usr/local/etc/letsencrypt/live/ /usr/jails/httpd/usr/local/etc/apache24/certs/
        /usr/sbin/jexec httpd sh -c 'service apache24 restart'
fi

When run as root, this script will run CertBot in renew mode, meaning that Certbot will check the expiration dates of all of your certificates and renew the certificates that expire within 30 days. If no certificates are close to expiring, it will do nothing.

Next, the script will diff the .pem file that CertBot has control over with the copy we made for the Apache jail. If the files are different, the script will copy the new certificates to the jail, then restart the Apache service so that it will use the new certs.

Save the script somewhere sane, and add it to crontab or periodic and set it to run weekly.

You should be good to go! You should now be able to use your Let’s Encrypt certificates to serve HTTPS or any other thing that you might commonly do with a certificate.