Lets Encrypt organized for reverse nginx proxy

This is a short post on using Let's Encrypt to get TLS certificates for use in Nginx virtual host sites. This post assumes:
- Ubuntu
- NGINX (running a number of domains)
- Some form of scheduler (Cron, Rundeck, ..)
Let's Encrypt
Let's Encrypt is an initiative meant to allow anyone to receive a 3rd party Domain Validated (DV) certificate for server authentication (HTTPS, SMTPS, IMAPS, ..). They're here, also: Github.
Initial setup
In my home network, I have a number of VM's running various web services. As I have only one public facing IP, I use a web proxy to host my sites. This proxy runs an NGINX server with a number of virtual hosts each with their configuration. Most of the configs end with a proxy_pass
statement redirecting traffic to the intended server.
In my setup, I wanted to be able to:
- Quickly add new sites / domains
- Let the proxy handle SSL (ssl acceleration)
- Let the proxy worry about renewals
- Generalize the configuration into "snippets" or similar reference-able things
Solution
I installed the letsencrypt
package which gives you the letsencrypt
binary at /usr/bin/letsencrypt
. Using this, we can run the necessary commands to invoke the apis.
I created an NGINX configuration snippet at /etc/nginx/snippets/letsencrypt.conf
with the following text:
location '/.well-known/acme-challenge' {
default_type "text/plain";
root /tmp/letsencrypt-auto;
}
This configuration instructs NGINX to forward the path /.well-known/acme-challenge
to a special folder at /tmp/letsencrypt-auto
(you can chose any folder). This snippet can be included in a lets encrypt site, like this:
include /etc/nginx/snippets/letsencrypt.conf;
Setting up a new site
These are the steps for a new site (here letsencrypt.mbwarez.dk
). First, create the configuration at /etc/nginx/sites-enabled/letsencrypt.mbwarez.dk
(I name all my configs after their FQDN).
server {
listen 80;
#listen 443 ssl;
server_name letsencrypt.mbwarez.dk;
#ssl_certificate /etc/letsencrypt/live/letsencrypt.mbwarez.dk/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/live/letsencrypt.mbwarez.dk/privkey.pem;
include /etc/nginx/snippets/letsencrypt.conf;
include /etc/nginx/snippets/hpkp-sts.conf;
root /var/www/letsencrypt.mbwarez.dk;
}
Observe how the certificate lines are commented out. This is due to the files being missing right now, and NGINX would refuse to start. We also only listen to HTTP (port 80) now. Also note that I have included the aforementioned snippet.
Then run the letsencrypt
command:
/usr/bin/letsencrypt certonly --text --non-interactive --keep --email TODO_EMAIL --webroot --webroot-path=TODO_PATH -d TODO_DOMAIN
With relevant values replaced:
# /usr/bin/letsencrypt certonly --text --non-interactive --keep --email certificate-admin@mbwarez.dk --webroot --webroot-path=/tmp/letsencrypt-auto -d letsencrypt.mbwarez.dk
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/letsencrypt2.mbwarez.dk/fullchain.pem. Your
cert will expire on 2016-10-05. To obtain a new version of the
certificate in the future, simply run Let's Encrypt again.
- If you like Let's Encrypt, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
What happened was that the LE server attempted to fetch a file at http://letsencrypt.mbwarez.dk/.well-known/acme-challenge/some-file-name
, which was redirected to the file at /tmp/letsencrypt-auto
. As LE got what they expected, they issued a certificate in the requested name, and placed it at /etc/letsencrypt/live/letsencrypt.mbwarez.dk/
.
Now we can simple uncomment the commented lines in the config:
server {
listen 80;
listen 443 ssl;
server_name letsencrypt.mbwarez.dk;
ssl_certificate /etc/letsencrypt/live/letsencrypt.mbwarez.dk/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/letsencrypt.mbwarez.dk/privkey.pem;
include /etc/nginx/snippets/letsencrypt.conf;
include /etc/nginx/snippets/hpkp-sts.conf;
root /var/www/letsencrypt.mbwarez.dk;
}
.. reload NGINX and off we go.
Renewing a certificate
Once every 3 months, you must renew your certificates (before they expire). I've set a scheduled task to run every 7 days, which just runs the exact same command as before. In fact, I created a script for it:
#!/bin/bash
DIR=/tmp/letsencrypt-auto
TOOL=/usr/bin/letsencrypt
mkdir -p $DIR
function updatecert() {
DOMAIN=$1
echo "UPDATING cert for $DOMAIN"
$TOOL certonly --text --non-interactive --keep --email certificate-admin@mbwarez.dk --webroot --webroot-path=$DIR -d $DOMAIN
}
updatecert blog.mbwarez.dk
updatecert cygwin.mbwarez.dk
updatecert letsencrypt.mbwarez.dk
updatecert mbwarez.dk
updatecert plex.mbwarez.dk
updatecert scm.mbwarez.dk
echo "RESTARTING nginx"
service nginx configtest && service nginx reload
ErrCode=$?
exit $ErrCode
(also, when adding new sites, I simply add them to here instead of running the letsencrypt
command myself).