Using Let's Encrypt on Debian

Have you heard the good word? The days of having to shell out $300/year for an SSL certificate are no more! Rejoice!

Let's Encrypt

Let's Encrypt is not your parents' Certificate Authority (CA). For one thing, their certificates are FREE. For another, validation happens entirely server-side. Both of these quirks are tremendously helpful both in terms of deployment and automation, but because there is no web control panel to turn to, there is a bit of a learning curve.

Choices

Let's Encrypt makes certificate requests, renewals, validations, etc., available through a public API. On the server-side, you just need to install a program to wrap these things for you (or if you're crazy, write your own). As a command line aficionado, I prefer the lightweight BASH implementation dehydrated (f. letsencrypt.sh), which is what this tutorial focuses on. There are alternatives, however, so if you prefer Python or whatever, just ask Uncle Google.

Installation

deydrated is available through Backports for Jessie, or the main repos for Stretch and beyond.

# Jessie (be sure to enable Backports in /etc/apt/sources.list first)
sudo apt-get -t jessie-backports install dehydrated

# Stretch and later
sudo apt-get install dehydrated

Configuration

Start by saving a main configuration file to /etc/dehydrated/conf.d/config.sh:

CONFIG_D=/etc/dehydrated/conf.d
BASEDIR=/var/lib/dehydrated
WELLKNOWN="${BASEDIR}/.well-known"
DOMAINS_TXT="/etc/dehydrated/domains.txt"

Then create a domains file at /etc/dehydrated/domains.txt and populate it with any domains you want certificates issued for. Each line is one certificate and can include any reasonable number of domains separated by a space.

domain1.com www.domain1.com api.domain1.com
domain2.com www.domain2.com
...

With this configuration, we're using /var/lib/dehydrated/.well-known as a central repository for all "challenge" files, keeping a bunch of garbage from ending up in each site's webroot. First of all, create that directory if it doesn't exist. Then, make sure your domains are able to resolve it. Nginx users can do something like the following:

# some global conf file, e.g. /etc/nginx/global/letsencrypt.conf
location ~ /\.well\-known {
	root /var/lib/dehydrated;
	access_log off;
	log_not_found off;
	allow all;
}

# then in your virtual host config, e.g. /etc/nginx/sites-enabled/domain.com.conf
server {
	...
	include /etc/nginx/global/letsencrypt.conf;
	...
}

After saving your Nginx changes, run:

# make sure the config is good
nginx -t

# restart Nginx
service nginx restart

Using It!

Crazy difficult, type:

dehydrated -c

Done.

Every domain in your domains.txt list will be checked automatically, certificates issued or renewed as appropriate. It is important to note that the "Wellknown" challenge must be accessible through the domain, so i.e. the DNS must resolve to your server or it will fail.

Nginx Config

All that's left is to tell the web server about the new certificates. All Let's Encrypt certificates are stored in a predictable way, making this rather easy. Add the following to each domain's server{} block:

ssl_certificate /var/lib/dehydrated/certs/domain.com/fullchain.pem;
ssl_certificate_key /var/lib/dehydrated/certs/domain.com/privkey.pem;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /var/lib/dehydrated/certs/domain.com/chain.pem;
add_header Strict-Transport-Security "max-age=31536000";

(See also: A+ SSL Configuration for Nginx)

The paths above are actually symlinks so will remain consistent as certificates are renewed. Just remember to restart Nginx whenever a certificate is re-generated so it can pull in the updated content.

Final Thoughts

Let's Encrypt is a wonderful thing. It makes managing SSL certificates for hundreds of sites a breeze. I still have to occasionally deal with old-fashioned CAs, but I'm not happy about it.

Josh Stoik
20 December 2016
Previous A+ SSL Configuration for Nginx
Next Optimizing SVGs For Web Use