Enabling TLS for NGINX with Let’s Encrypt & Certbot

In my last blog I wrote about setting up NGINX to act as a reverse proxy for websites – this allowed for multiple web services to be proxied behind a single NGINX server acting as a gateway for them all. We stopped once we had HTTP services successfully reachable from the Internet. In this post we’re going to explore how to secure these services with TLS and make them reachable via HTTPS.

For this, we’ll need to ensure that port 443 is open and forwarded to our NGINX Server. For the next step, opinions vary but I’m a fan of setting up an automatic redirect from port 80 to port 443. This is quite easy to do, we just modify our original NGINX configuration file:

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        #
        # Redirect HTTP Requests
        #
        return 301 https://$host$request_uri;

       server_name _;

        location / {
                try_files $uri $uri/ =404;
        }
}

The return 301 instructs the server to .. well return a 301 (Permanently moved) when the HTTP server is requested. This is followed by the URL that should be redirected to, in this case we return the original host and URI, but now as an HTTPS request. The browser will automatically look up this new URL. Next, we need to configure NGINX to respond to the HTTPS requests for the web applications. Most of the default settings can be copied from the /etc/nginx/sites-available/default file. I’m going to use a production site for this example, as when it comes to generating the certificate, we need a reachable site.

server {
        listen 443;
        server_name jewhurst.com www.jewhurst.com;
###
# SSL Config
###
        ssl_certificate /etc/ssl/temp.cert;
        ssl_certificate_key /etc/ssl/temp.key;

        ssl on;
        ssl_protocols   TLSv1.2;
###
        access_log      /var/log/nginx/jewhurst_com.log;

        location / {
                proxy_pass      http://10.10.10.10:80;
                proxy_set_header        Host    $host;
                proxy_set_header        X-Real-IP       $remote_addr;
                proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header        X-Forwarded-Proto       $scheme;
        }
}

With this configuration we turn on SSL and specify a location for the certificate which should be served for the site. Unless you already have an SSL certificate from a provider, use a temporary location for the time being. We also specify that we’d like to use only TLS 1.2 to ensure we’re on the most secure version of the protocol (TLS 1.3 is out there, but we’ll deal with that another time).

Next, install certbot which will automate the process of getting a free SSL certificate from Let’s Encrypt and applying it to the server:

stan@nginx~# sudo apt install certbot

With that installed, and with the NGINX server reachable via port 80 and port 443, run certbot with the NGINX option:

stan@nginx~# certbot --nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx

Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: www.jewhurst.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

Selecting your website and pressing return will cause certbot to reach out to the Let’s Encrypt servers and generate a certificate. This certificate will be verified using a local challenge (it creates a verification file locally, then connects to the server to ensure it can read it) and downloads to the server. Note that you will likely need to run this command as root.

Obtaining a new certificate
Performing the following challenges:
http-01 challenge for www.jewhurst.com
Waiting for verification...
Cleaning up challenges
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/www.jewhurst.com

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

For the above step, no redirect is necessary, as we have already configured this manually.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled https://www.jewhurst.com

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

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/www.jewhurst.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/www.jewhurst.com/privkey.pem
   Your cert will expire on 2020-06-18. 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"

And with that your site should now be reachable via HTTPS, with a verified, trusted certificate. An added advantage of certbot is that it adds a cron job to automatically renew your certificates, ensuring you don’t need to manually run the command and risk your server expiring:

stan@nginx~# cat /etc/cron.d/certbot
# /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.
#
# Important Note!  This cronjob will NOT be executed if you are
# running systemd as your init system.  If you are running systemd,
# the cronjob.timer function takes precedence over this cronjob.  For
# more details, see the systemd.timer manpage, or use systemctl show
# certbot.timer.
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

Leave a Reply

Your email address will not be published. Required fields are marked *