Nginx

Nginx Reverse Proxy with HTTPS via LetsEncrypt

This is a follow-up on my previous post where we setup a simple reverse proxy server using Nginx. In this post, we will secure the connection between client and the reverse proxy server using free TLS (a.k.a SSL) certificate from LetsEncrypt. I encourage you to check out the aforementioned post on reverse proxy for the basics.

Prerequisites

  1. A server with static public IP. This is where Nginx is running.
  2. Backend servers with the intended website running over HTTP
  3. A registered domain name. I will be using ranvirslog.com as my primary domain name and the two websites are at FQDNs — ww1.ranvirslog.com and ww2ranvirslog.com

Setup

So the IP addresses have changed since the last time, since I am doing this setup again. Here are the new IPs and hostnames.

VM/Hostname Public IP Private IP Role/Function
ReverseProxy 68.183.214.151 10.135.127.136 TLS termination point and reverse proxy server
web1 N/A 10.135.126.102 Hosting ww1.ranvirslog.com

website over port 80 HTTP

web2 N/A 10.135.126.187 Hosting

ww2.ranvirslog.com

website over port 80 HTTP

The DNS records are setup as such both the websites (different subdomains) are pointing to the same static public IP. This happens to be our Nginx reverse proxy’s IP address:

A Record Value
ww1.ranvirslog.com 68.183.214.151
ww2.ranvirslog.com 68.183.214.151

To make our reverse DNS work over unencrypted HTTP, we created two files in /etc/conf.d/ named ww1.conf and ww2.conf each with the following configuration:

/etc/conf.d/ww1.conf

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

server_name ww1.ranvirslog.com;

location / {
proxy_pass http://10.135.126.102/;
proxy_buffering off;
proxy_set_header X-Real-IP $remote_addr;
}
}

/etc/conf.d/ww2.conf

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

server_name ww2.ranvirslog.com;

location / {
proxy_pass http://10.135.126.187/;
proxy_buffering off;
proxy_set_header X-Real-IP $remote_addr;
}
}

The operating system we are using is Ubuntu 18.04 LTS and we have removed the file /etc/nginx/sites-enabled/default so Nginx can act purely as a reverse DNS using the configs shown above.

Objective

With the reverse DNS (and the backend websites) already up and running, our objective is to install a single TLS certificate for both the FQDNs (that’s ww1.ranvirslog.com and ww2.ranvirslog.com)  on our Nginx reverse proxy.

The traffic between any client and the reverse proxy is going to be encrypted but the traffic between the reverse proxy and the backend servers is not encrypted. However, this is still an infinitely more secure option than not having HTTPS at all. For cases where the reverse proxy and the various web servers are on the same host, say if you are using Docker containers to host all on the same VPS, then even this unencrypted traffic is contained on a single host.

Installing Certbot

Certbot is a client program that will run on our reverse proxy server and negotiate a TLS certificate with LetsEncrypt. It will prove to LetsEncrypt that the server does in fact have control of the FQDNs that it claims to have control over. We won’t worry about how Certbot does it.

Traditionally, you can use Certbot as a standalone software which will just get the certificates (which are basically just long cryptographic keys) and save it on the server. But thankfully, for most operating systems there are custom plugins for Nginx, Apache and other softwares. We will install the Certbot with Nginx plugin. This will automatically configure Nginx to use the newly obtained keys and get rid of insecure rules like listening for HTTP on port 80.

If you are using Debian based systems, like in my case I am using Ubuntu 18.04 LTS, then the installation is a breeze.

$ sudo apt update
$ sudo apt install software-properties-common
$ sudo add-apt-repository universe
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt update
$ sudo apt install python-certbot-nginx

Other operating systems, your RedHat, Gentoo, Fedora can follow the official instructions as listed here.

Once you have installed Certbot with Nginx Plugin for your combination of OS we can get down to business.

Getting TLS certificates

To get the TLS certificate for the first time, run the following command:

$ sudo certbot --nginx

This is going to run through a series of interactive questions, as shown below:

  1. Enter your email

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Enter email address (used for urgent renewal and security notices) (Enter ‘c’ to cancel): [email protected]

  1. Agree to TOS

Please read the Terms of Service at https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must agree in order to register with the ACME server at https://acme-v02.api.letsencrypt.org/directory
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
(A)gree/(C)ancel: A

  1. Optional Newsletter

– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
Would you be willing to share your email address with the Electronic Frontier Foundation, a founding partner of the Let’s Encrypt project and the non-profit organization that develops Certbot? We’d like to send you email about our work encrypting the web, EFF news, campaigns, and ways to support digital freedom.
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
(Y)es/(N)o: Y

  1. It will then detect the domain names on your server, and if you want to select all the domains simply press

Which names would you like to activate HTTPS for?
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
1: ww1.ranvirslog.com
2: ww2.ranvirslog.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. Redirect everything to TLS. I chose the option 2, to redirect everything to SSL but your use case might differ. For new backend installations it is safe to pick option 2.

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): 2

If everything went well, it will show you this message, just for your domain names instead.

Congratulations! You have successfully enabled https://ww1.ranvirslog.com and https://ww2.ranvirslog.com You can visit the FQDNs and notice that the websites now have the padlock sign suggesting everything is encrypted.

Look at the configuration files

If you view the configuration files we created earlier, namely /etc/conf.d/ww1.conf and /etc/conf.d/ww2.conf, you will notice that all the “Listen 80” rules have vanished and a few new lines have been added tell server that the communication needs to be encrypted and the location of the certs and keys to perform the said encryption.

I strongly recommend looking through the config files, since that can also teach you how to properly install certs and write configuration files.

Certification Renewal

Typical LetsEncrypt certificates are valid for 90 days and before they expire you need to renew them. You can use Certbot to first dry run the renewal, by running the command:

$ sudo certbot renew --dry-run

If the operation succeeds you will see the following message:

Congratulations, all renewals succeeded. The following certs have been renewed:

/etc/letsencrypt/live/ww1.ranvirslog.com/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates above have not been saved.)

Now you can add a Cron job which will attempt renewal every week or so. Certbot won’t renew the certs unless they are really due for that, so you don’t have to worry. The command for actual renewal is:

$ certbot renew

Add it to root’s cron job by using:

$ sudo crontab -e

In the following prompt, select your favorite editor (Pick Nano if you are unsure) and add the following lines at the end of the now opened file:

....
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h  dom mon dow   command
* 2 * * 2    certbot renew

This will run the certbot renew command at 2 in the morning at any random minute, on the second day of every week.

Conclusion

If you are new to TLS certificates, experimenting with things like HSTS can be risky. Since these changes are irreversible. However, if you do want to go down the rabbit hole of security I can highly recommend Troy Hunt’s blog which is one of the main inspiration behind this write up.

About the author

Ranvir Singh

I am a tech and science writer with quite a diverse range of interests. A strong believer of the Unix philosophy. Few of the things I am passionate about include system administration, computer hardware and physics.