Configuring LetsEncrypt on a CentOS 6 NGinx Reverse Proxy

For those who haven't come across it, LetsEncrypt allows you to obtain free DV SSL Certificates but requires a server side script to be run periodically in order to renew the certificates (for better or worse, a 90 day expiration period has been used).

Although the provided script has plugins to allow support for automatically generating SSL certs based on NGinx and Apache configurations, the script assumes that the server is the origin and that the relevant docroot is available for writing to.

In the case of a reverse proxy - this won't be the case. We want the certificate on the Reverse Proxy (being the endpoint the client connects to) but the websites files are hosted on another server.

This documentation details a simple way to work around that on a NGinx reverse proxy (it should be possible to adjust the config for Apache's mod_proxy if needed).

 

Installing Let's Encrypt

There are a few extra steps required in order to get LetsEncrypt working on CentOS 6 - we need Python 2.7 rather than the default 2.6. We need to use both the EPEL and IUS repos. Run the following as root

wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
rpm -Uvh epel-release-6*.rpm
rpm -ivh https://rhel6.iuscommunity.org/ius-release.rpm
yum --enablerepo=ius install git python27 python27-devel python27-pip \
python27-setuptools python27-virtualenv

The likelihood is that virtualenv will have been installed as virtualenv-2.7 so we need to create a symlink in $PATH so that LE can find it

ln -s /usr/bin/virtualenv-2.7 /usr/bin/virtualenv

Next, we need to install Let's Encrypt itself

git clone https://github.com/letsencrypt/letsencrypt
cd ./letsencrypt/
./letsencrypt-auto --help

At this point LetsEncrypt will check (and update!) it's dependancies and create a virtualenv to use

 

Configuring NGinx

During the authentication procedure, LetsEncrypt will need to temporarily create files "within" your site. As this is a reverse proxy, we've no access to the actual docroot itself, so lets configure NGinx to directly handle the location LetsEncrypt needs

First we need to create a location to write to

mkdir /usr/share/nginx/letsencryptbase

Next we want to configure the relevant server blocks to use this path when necessary. So within your NGinx configuration (for every site you intend to use LE on), add the following within the server block

location /.well-known/ {
    root /usr/share/nginx/letsencryptbase;
    try_files = $uri =404 ;
}

If you haven't already, you also need to make sure there's a Port 80 listener for each host, even if it then just redirects to the https site (for a new site, don't implement the redirect until after you've obtained a certificate).

Reload NGinx to bring the new configuration into play

service nginx reload

 

First Run

The first time you run it, Let's Encrypt will ask you for an email address (so you can receive warnings if your host doesn't renew) and to agree to the terms and conditions, so it's worth an initial manual run

/root/.local/share/letsencrypt/bin/letsencrypt certonly --webroot \
-w /usr/share/nginx/letsencryptbase -d site.example.invalid --rsa-key-size 4096

Where site.example.invalid is the FQDN we're trying to obtain a certificate for. If you monitor your access logs you should see a request like the following come in shortly before your certificate is issued

66.133.109.36	-	-	[09/Feb/2016:13:17:08 +0000]	"GET /.well-known/acme-challenge/xFCTN-OhSJy90L5lVIHjz8vhW9bpfzk54OD-M4Zk0ds HTTP/1.1"	301	184	"-"	"Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"	"-"	"site.example.invalid"

Shortly after, Lets Encrypt will provide some status output

- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/site.example.invalid/fullchain.pem. Your
cert will expire on 2016-05-09. To obtain a new version of the
certificate in the future, simply run Let's Encrypt again.
- Your account credentials have been saved in your Let's Encrypt
configuration directory at /etc/letsencrypt. You should make a
secure backup of this folder now. This configuration directory will
also contain certificates and private keys obtained by Let's
Encrypt so making regular backups of this folder is ideal.

Repeat this section for every site you intend to use LE for

 

Configuring NGinx to use the Certificate

Now we've obtained a certificate we need to tell NGinx to use it. LetsEncrypt will always provide the newest version of the certificate under the live directory, but I prefer to keep to the more traditional layouts in /etc so we'll use symlinks to provide the certs.

cd /etc/pki/tls/certs
ln -s /etc/letsencrypt/live/site.example.invalid/fullchain.pem site.example.invalid.pem
cd /etc/pki/tls/private
ln -s /etc/letsencrypt/live/site.example.invalid/privkey.pem site.example.invalid.pem

Now we need to tell NGinx to use the new cert/key, so adjust the relevant server block to include

ssl_certificate      /etc/pki/tls/certs/site.example.invalid.pem;
ssl_certificate_key  /etc/pki/tls/private/site.example.invalid.pem;

Reload NGinx's configuration

service nginx reload

And your site should now be using the LetsEncrypt certificate. Once you're happy it's working, repeat this section for every site you intend to use LE for

 

Automating Renewal

The final step is to automate renewal, as you don't want to be having to manually renew every 90 days. Place the following script somewhere and make it executable

#!/bin/bash
#
# LetsEncrypt wrapper written for LAN-77
# Copyright (C) 2016 B Tasker
# Released under GNU GPL V2
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt # domains="site.example.invalid site2.example.invalid" # which domains? n_mail="me@example.invalid" # where should we send reports? function fetchcert(){ domain=$1 echo "Refreshing cert for $domain" >> /tmp/le_output.txt /root/.local/share/letsencrypt/bin/letsencrypt certonly --webroot \
-w /usr/share/nginx/letsencryptbase -d "$domain" \ --rsa-key-size 4096 -n | egrep -v -e "NOTES|Donating to|supporting our work by|^$" >> /tmp/le_output.txt echo >> /tmp/le_output.txt } echo "LetsEncrypt run started at `date`" > /tmp/le_output.txt echo "" >> /tmp/le_output.txt for i in $domains do fetchcert "$i" done service nginx reload >> /tmp/le_output.txt echo >> /tmp/le_output.txt echo "Observed Log Entries" >> /tmp/le_output.txt echo "-------------------------------" >> /tmp/le_output.txt grep "acme-challenge" /var/log/nginx/access.log >> /tmp/le_output.txt echo >> /tmp/le_output.txt echo "Run complete at `date`" >> /tmp/le_output.txt cat /tmp/le_output.txt | mail -s "LetsEncrypt Run on $HOSTNAME - `date +'%Y-%m-%d %H:%M'`" $n_mail

Adjust the variables domains and n_mail to reflect your setup. Give the script a quick test run to ensure it works, and then schedule in crontab to run once a month

Once a month you should then get a basic report detailing which SSL certificates have been updated, and (hopefully) should transparently renew your certificates once every 90 days