Set up Let’s Encrypt with NGINX web server with certbot webroot plugin

2017-01-26

Introduction

This is one (of many) methods to speed up creating free SSL certificates with Let's Encrypt.

It configures the NGINX web server to serve /.well-known/acme-challenge/ for each domain. This path is used by the webroot plugin.

We’ll need to make a directory to servie the challenge files from, we’ll call this /home/www/letsencrypt from now on, and we’ll need to make sure this is set up with suitable permissions such that

  • NGINX can serve these files to the public
  • whomever is running certbot-auto can write to the directory

In NGINX’s config, we want to add the following to each server block configuration:

location ^~ /.well-known/acme-challenge/ {
    default_type "text/plain";
    root /home/www/letsencrypt;
}

When this is in place, whenever a HTTP client makes a request to /.well-known/acme-challenge/TOKEN for the your domain, it will serve the file from /home/www/letsencrypt/TOKEN. This is how the HTTP-01 challenge works.

Generally, adding this in /etc/nginx/global/global.conf and including this file (usually already done by a default install) in each *.conf file in /etc/nginx/conf.d/ is recommended.

The way I normally do this is to create a file /etc/nginx/global/letsencrypt.conf with:

location ^~ /.well-known/acme-challenge/ {
    default_type "text/plain";
    root /home/www/letsencrypt;
}

Then in each server block, for the port 80 listener, I have:

server {
    listen 80;
    server_name www.MYDOMAIN.com;
    include global/letsencrypt.conf;
    location / {
        return 301 https://$server_name$request_uri;
    }
}

This is a normal practice, that I redirect HTTP requests to HTTPS. However, any HTTP request to /.well-known/acme-challenge/ (used by the HTTP-01 challence), is served from /home/www/letsencrypt

Install certbot

Install via snapd

Install certbot - for most distributions, it requires snapd

Ensure that your version of snapd is up to date

snap install core
snap refresh core

Remove old certbot-auto, by running these as needed, as needed by your distribution

apt-get remove certbot
dnf remove certbot
yum remove certbot

Install certbot

snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot

Install by native package manager

Some distributions may have their own certbot distribution available by their package manager. In which case you can use yum install certbot or apt-get install certbot.

Install by Python pip

Refer to certbot instructions.

Add a certificate for a domain

certbot certonly --webroot -w /home/www/letsencrypt -d domain.com

You need to make sure certbot has write permissions to the direction given with the -w parameter.

If you want to do a dry run, to check whether the HTTP-01 challenge is successful or not, without actually creating a certiticate - you can run:

certbot certonly --webroot -w /home/www/letsencrypt -d domain.com --dry-run

Configure NGINX server blocks

In each domain’s server block add the follow, but use your own domain name for the certificate paths.

listen               443 ssl;
ssl_certificate      /etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key  /etc/letsencrypt/live/domain.com/privkey.pem;
include              ssl/ssl.conf;

In ssl/ssl.conf we have:

ssl_session_timeout       5m;
ssl_protocols             TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers               "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS";
ssl_prefer_server_ciphers on;
ssl_session_cache         shared:SSL:10m;
ssl_dhparam               /etc/nginx/ssl/dhparams.pem;

The above SSL config is my personal preference, but you can adjust to your liking.

If you don't have a DH parameters file configured, you can generate one with

openssl dhparam -out /etc/nginx/ssl/dhparams.pem 2048

Renew all certs if near expiry

For domains which have previously had a certificate issued, you can renew them with

certbot renew

You don't need to use --webroot -w /home/www/letsencrypt again.

This can be added as a cron job.

00 2 * * * root /usr/local/bin/certbot renew 2> /dev/null

If you wish the automation to apply for a single domain only, use:

00 2 * * * root /usr/local/bin/certbot renew --cert-name NAME 2> /dev/null

Note that this will renew the certificates, but your NGINX web server will not know they have renewed. You need to reload NGINX's config files to make sure new certificates have been read. You could make a cron job to do this, some short time after the renewal attempt:

15 2 * * * root nginx -s reload 2> /dev/null

Certificate names are usually the same as the domain name, however this may not always be the case. You can check your certificate names with:

certbot certificates

To test configuration and check that HTTP-01 challenge and verification will work. This is a a good idea if you've been editing the NGINX config files where you could have broken something.

certbot renew --dry-run

This will check for all domains configured for certbot

Other useful certbot commands

Deleting certificates

certbot delete --cert-name DOMAIN

Viewing installed certificates

certbot certificates