Lets Encrypt, Nginx proxy, CentOS 7

I've been interested in the Lets Encrypt project since I first heard about it, quite some time ago. It's a cool idea, automatic free certificates for everyone!  I've been using startcom for the majority of my personal sites, minus swbcrawler, which I purchased a cheap commercial cert for.  A few months back, Lets Encrypt finally opened up for publi use, and I giave it a little poke.  I didn't end up putting any of its certs live at the time though.

Well, June 1st my startcom certs all expired, and it was time to renew.  This was just enough motivation to get me to give Lets encrypt another go. 

Let's Encrypt has a command line tool, which, if done right, automatically verifies your site, and gets you a Lets Encrypt signed certificate.  It even puts it in place for you.  All you have to do is configure your web server to use it and you're done! It also only issues 3 month certs though.  Renewal can be automated, so that's a requirement for me.  It can integrate with Apache, but I moved to an NGINX reverse http proxy a while back, so I needed it to run there.  It also has some dependencies that are hard to come by on RHEL/CentOS 6, so I was held back by the version of OS i was running.  Well I recently build a new Proxy on CentOS 7, so I was all set.

A quick google search found me someone elses instructions on how to get Lets Encrypt going with NGINX, of course, he was on Ubuntu.  Isn't everybody?  This isn't the first time I've had ot translate an ubuntu setup to a RHEL setup, so I found it informative enough to get me rolling.  Here are my adapted directions.  Please, read over his linked article for reference.

First, you need EPEL, you can get it from their site, OR just install it like this.

# yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
Next, we just need to install certbot.
# yum install certbot
Loaded plugins: fastestmirror


Dependencies Resolved

 Package                     Arch        Version                Repository
 certbot                     noarch      0.6.0-2.el7            epel       15 k
Installing for dependencies:
 pyOpenSSL                   x86_64      0.13.1-3.el7           base      133 k
 python-cffi                 x86_64      0.8.6-2.el7            base      131 k
 python-chardet              noarch      2.2.1-1.el7_1          base      227 k
 python-cryptography         x86_64      0.8.2-1.el7            base      435 k
 python-enum34               noarch      1.0.4-1.el7            base       52 k
 python-ndg_httpsclient      noarch      0.3.2-1.el7            epel       43 k
 python-parsedatetime        noarch      1.5-3.el7              epel       61 k
 python-ply                  noarch      3.4-10.el7             base      123 k
 python-psutil               x86_64      2.2.1-1.el7            epel      114 k
 python-pyasn1               noarch      0.1.6-2.el7            base       91 k
 python-pycparser            noarch      2.14-1.el7             base      104 k
 python-requests             noarch      2.6.0-1.el7_1          base       94 k
 python-six                  noarch      1.9.0-2.el7            base       29 k
 python-urllib3              noarch      1.10.2-2.el7_1         base      100 k
 python-zope-component       noarch      1:4.1.0-1.el7          epel      110 k
 python-zope-event           noarch      4.0.3-2.el7            epel       79 k
 python-zope-interface       x86_64      4.0.5-4.el7            base      138 k
 python2-acme                noarch      0.6.0-1.el7            epel      161 k
 python2-certbot             noarch      0.6.0-2.el7            epel      334 k
 python2-configargparse      noarch      0.10.0-1.el7           epel       28 k
 python2-dialog              noarch      3.3.0-6.el7            epel       94 k
 python2-mock                noarch      1.0.1-9.el7            epel       92 k
 python2-pyrfc3339           noarch      1.0-2.el7              epel       13 k
 pytz                        noarch      2012d-5.el7            base       38 k

Transaction Summary
Install  1 Package (+24 Dependent packages)

This installs a number of dependencies as well, as you can see.
Now, I have my proxy hosts setup so that all http requests redirect to https, and then they just proxy_pass to my backend host.  I've also got caching setup, and as mentioned, I already have (expired) startcom certs in place. 
Let's Encrypt uses a protocol called ACME to validate your site and issue your cert.  The client generates a code, and puts it in domain.com/.well-known.  This is all done by the letsencrypt client, on the nginx host.  So for each site, I made a place to locally store this .well-known location, and added it to my nginx proxy host config.  So now all of my sites have a /.well-known directory, which lives on my nginx host, everything else proxies over to apache on my web server.

I added this to the http and https versions of my host:

   location /.well-known/ {
      allow all;
      root /var/lib/nginx/undrground-wellknown;

Then created /var/lib/nginx/undrground-wellknown, and restorecon-vFR’d it.  My selinx policy already treats /var/lib/nginx as the web tree for nginx.  This is apparently the default in RHEL/CentOS.

Once that is done, test your nginx config, and reload.  You should now have a .well-known folder under your domain.  It's empty of course.

Now run the lets encrypt client.  On the first run it will install a bunch of dependencies and get things setup. After that, it's pretty speedy.

# /bin/certbot certonly -a webroot --webroot-path=/var/lib/nginx/undrground-wellknown/ -d undrground.org -d www.undrground.org

This will ask you a simple question or two, your email address, and whether you accept their TOS.  Then it'll do its thing, and assuming you've got everything setup as described, it should tell you that it's created your cert in /etc/letsencrypt/live/domain.com/
Then I went back into my nginx config, and changed my ssl cert and key to look like this:
    ssl_certificate     /etc/letsencrypt/live/undrground.org/fullchain.pem;
    ssl_certificate_key  /etc/letsencrypt/live/undrground.org/privkey.pem;

Then test, and reload nginx, you should be live with your new cert!
Now, I mentioned the 3 month validity.  The lets encrypt client can do renewals too.  In fact, it will test each cert it knows about, and renew it if its close to expiring by simply running "letsencrypt-auto renew".  So I made a pretty simple bash script to run weekly.  The problem is, i cant tell if it's working until it actually has a cert to renew. 
It will run the renewal, and then use find to see if any files in /etc/letsencrypt/live were modified today.  If they were, it reloads nginx.
/bin/certbot renew
TEST=`find /etc/letsencrypt/live`
if [ "$TEST" ]
    systemctl reload nginx
At least, I think that's what it'll do.  Let me know if you see a problem.  My preliminary testing checks out.  There may also be a better way to do this.
After some exchanges with @j4cob on twitter.  I've found that the obove script can be replaced with a simple CLI option to certbot.  --post-hook.
# certbot renew --post-hook="systemctl reload nginx"
I've replaced my hacky little find with the above command.  What it should do is, if certbot does anything, it will execute the post-hook.  Which will reload nginx.  Thanks @j4cob!
Well, that's it!  We'll see in 3 months if this all works out.  So far, so good.