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