[2/2] Install NGINX with Let's Encrypt on Debian 9 (automatic certificate renewal)

You must have followed the steps in the previous guide, start from here: [1/2] Install NGINX with Let's Encrypt on Debian 9 (automatic certificate renewal)

Reminders

Sites structure (domains name):

1
2
3
- contoso.com           (ASP.Net Core Web Application)
- blog.contoso.com (Static HTML blog)
- php.contoso.com (PHP Website)

Sites structure (filesystem)

1
2
3
- contoso.com      => /home/www/contoso.com
- blog.contoso.com => /home/www/blog.contoso.com
- php.contoso.com => /home/www/php.contoso.com

NGINX configuration files

1
2
3
- contoso.com      => /etc/nginx/sites-available
- blog.contoso.com => /etc/nginx/sites-available
- php.contoso.com => /etc/nginx/sites-available

Notes

The goal of Let’s Encrypt is to provide free SSL/TLS certificates to let us secure our webservers automatically. As the goal is to renew without any human intervention the certificates, they only lasts for a short period (90 days). That forces us to set an automatic certificate renewal that will reinforce the security of our webservers.

We’re going to use certbot which is a Let’s Encrypt client available on multiple UNIX distributions (there even is unofficial Windows clients!). It handles everything we need to get and set up our certificates.

Installation

All installation instructions for every supported distributions can be found here: https://certbot.eff.org/

Install certbot for NGINX from Stretch (= Debian 9) backports:

1
sudo apt-get install python-certbot-nginx -t stretch-backports

If you get this error message:

1
2
3
[17:44]lyyn@mayako:~$ sudo apt-get install python-certbot-nginx -t stretch-backports
Reading package lists... Done
E: The value 'stretch-backports' is invalid for APT::Default-Release as such a release is not available in the sources

This means that you haven’t added the Stretch backports repository. To add it, I suggest to create a new file that will contains your custom repositories link. Here, I’m going to name this file custom.list. Debian reads each file having the .list extension in the /etc/apt/sources.list.d/ folder.

1
sudo nano /etc/apt/sources.list.d/custom.list

Add this line in your file:

1
deb http://ftp.debian.org/debian stretch-backports main

Then retry to install Certbot:

1
sudo apt-get install python-certbot-nginx -t stretch-backports

Configuration

To validate the certificates, Certbot create a .well-known directory in the root folder of each sites that will contained in your certificate. This isn’t an issue when you only use static files or the FastCGI bridge, but when you have a reverse proxy (e.g. here, with the contoso.com), you need to specify to NGINX where to read the certificates from.

In the default configuration file (/etc/nginx/sites-available/default), add the default rule for reading the .well-know directory:

1
2
3
4
location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";
}

In the contoso․com configuration file (/etc/nginx/sites-available/contoso.com), add this:

1
2
3
4
# Let's Encrypt -> specify where this folder is in the filesystem
location /.well-known {
alias /home/www/contoso.com/.well-known;
}

As usual, we need to reload NGINX to take into account the new configuration:

1
sudo service nginx restart

Getting the certificate

We’re now ready to get the certificates! Launch certbot and tell it to use the webroot plugin on an nginx server:

1
sudo certbot --authenticator webroot --installer nginx

First, certbot will ask for which names (= NGINX servers) you want to activate HTTPS. Enters 1,2,3 to select all your sites:

1
2
3
4
5
6
7
8
Which names would you like to activate HTTPS for?
-------------------------------------------------------------------------------
1: contoso.com
2: blog.contoso.com
3: php.contoso.com
-------------------------------------------------------------------------------
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 1,2,3

Certbot will then ask to enter the webroot of your first domain. You need to enter the absolute filesystem path where this site files are stored. Following the example of this guide, the webroot will be /home/www/contoso.com

Then, Certbot will re-ask this for each remaining site. You’ll need to “Enter a new webroot” each time, since each of your sites are in separated directories:

1
2
3
4
5
6
Select the webroot for blog.contoso.com:
-------------------------------------------------------------------------------
1: Enter a new webroot
2: /home/www/contoso.com
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 1

Now you’ll be asked if you want to let Certbot changes your sites configuration to redirect all HTTP trafic to HTTPS. The first time, I said yes (option #2) but Certbot broke my sites configuration sooo… Say no (option #1). We’re going to do it after we obtain our certificate!

1
2
3
4
5
6
7
8
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
-------------------------------------------------------------------------------
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 1

Certbot is now requesting the certificate and testing it for each site. When it’s finished, it will show this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-------------------------------------------------------------------------------
Your existing certificate has been successfully renewed, and the new certificate
has been installed.

The new certificate covers the following domains: https://contoso.com, https://blog.contoso.com and https://php.contoso.com

You should test your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=contoso.com
https://www.ssllabs.com/ssltest/analyze.html?d=blog.contoso.com
https://www.ssllabs.com/ssltest/analyze.html?d=php.contoso.com
-------------------------------------------------------------------------------

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/contoso.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/contoso.com/privkey.pem
Your cert will expire on 2018-11-26. To obtain a new or tweaked
version of this certificate in the future, simply run certbot again
with the "certonly" option. To non-interactively renew *all* of
your certificates, run "certbot renew"
- If you like Certbot, please consider supporting our work by:

Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le

In the IMPORTANT NOTES section, you’ll find where your certificate and private key has been stored. This is obviously important and you’ll need to add the path to these files in each site configuration (as we didn’t let Certbot touch these configuration files!)

For this example, the certificate (chain) has been stored in /etc/letsencrypt/live/contoso.com/fullchain.pem and the private key file in /etc/letsencrypt/live/contoso.com/privkey.pem.

Tell NGINX to only use HTTPS

Our certificate are ready to use, we only need to tell NGINX to use them and to redirect all HTTP trafic to HTTPS. The trafic redirection is not mandatory but really recommanded for multiple reasons (security, Google ranking, …). We’re going to tell NGINX to listen on two ports now: 80 for HTTP trafic and 443 for HTTPS trafic.

Also, this is a good time to note that if you add more sites later, you can use the same certificate and keep all HTTPS advantages BUT you will get a certificate error for the non-covered sites. This can be useful if you want to hide a specific site from your certificate (as all covered sites are listed in the certificate). We also could use a wildcard certificate but the certbot version available for Debian 8 and 9 does not cover it ATM.

In the default configuration file (/etc/nginx/sites-available/default), add these lines:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# SSL configuration    
listen 443 ssl default_server;
listen [::]:443 ssl default_server;

# Redirect http to https
if ($scheme != "https") {
return 301 https://$host$request_uri;
}

# Let's Encrypt certificate
ssl_certificate /etc/letsencrypt/live/contoso.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/contoso.com/privkey.pem;

# Certbot SSL configuration for NGINX
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

Here we tell NGINX to listen on the port 443, using the SSL protocol. Then we rewrite each URL to redirect these that start with http to HTTPS. And finally, we tell where to read the certificate and its private key and we include the SSL rules given by Certbot.

Now we need to add listen 443 ssl; to each site file configuration. For contoso.com, we’ll have:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80;
listen 443 ssl;
server_name contoso.com;

location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $http_host;
proxy_cache_bypass $http_upgrade;
}
}

This is mandatory to do it for every site configuration file as each server configuration block can’t inherit some parameters from the default configuration file.

Finally, we reload NGINX to apply the new settings:

1
sudo service nginx restart

Try each of your site to see if you’re correctly redirected to HTTPS and if the certificate is valid.

Automatic renewal

Before setting up the automatic renewal, you can try if the renewal process can be executed succesfully:

1
sudo certbot renew --dry-run

If you don’t get any error, you can add a new cron job that will renew your certificate daily. Normally, Certbot will add this cron job during the certificate configuration, we’re going to check this.

Each file inside the /etc/cron.d folder are executed daily (they’re called “crontab fragment”), you should find a certbot file with this content:

1
2
3
4
5
6
7
8
9
10
11
# /etc/cron.d/certbot: crontab entries for the certbot package
#
# Upstream recommends attempting renewal twice a day
#
# Eventually, this will be an opportunity to validate certificates
# haven't been revoked, etc. Renewal will only occur if expiration
# is within 30 days.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew

If this is not the case, create this file with the root user as the owner (sudo chown root:root certbot) and set the permission to rw-r-r (read-write for the owner, root, and only read for the other user, type sudo chmod 0644 certbot).

Notes

You now have a NGINX server providing HTTPS only trafic that can handle static websites, php websites and even ASP.NET Core web applications!

I need to say that some console output or indications can be slightly different than what you’ll get. I created this serie of guides based on my proper (but a bit dirty) notes, but it should be clear enough to help you.

Useful links