Configuring SSL with letsencrypt certbot on NGINX reverse proxy

In a previous article we configured a Nginx reverse proxy to work behind a single public IP on a Proxmox node.

We are now able to send requests from Nginx to our internal network, the focus in this guide is on how to get SSL termination on the Nginx reverse proxy in order to serve HTTPS content. The configuration of SSL will only take place in Nginx as our backend server, Apache, will reply in HTTP over the private network back to Nginx which will then send the request to the client over HTTPS.

We will use two tricks to make this work in our reverse proxy setup.

1 – We will add the .well-known location described in RFC-5785 in our Nginx configuration which sets up a webroot on the Nginx server instead of proxying it to the backend server. This folder will allow us to validate the SSL certificate using the Automatic Certificate Management Environment with Certbot.

2 – The Apache module mod_rpaf will help setting our HTTP headers to the right values to fetch our visitors information instead of the proxy’s and allow our SSL certs to work with any websites on apache without further configuration.

Installing Certbot

Certbot is an automated python script to set up letsencrypt certificates on your website. These SSL certificates are recognized by every major browsers, which means you will get the green lock on your website once installed. These certificates have a validity of 3 months and must be renewed every so often, thankfully Certbot can automate the process for us.

Let’s start by installing Certbot, this is straightforward on Ubuntu 16.04 which I will be using in this guide. Check Certbot website for directions on your own distribution.

add-apt-repository ppa:certbot/certbot
apt-get update
apt-get install certbot

And you’re done, now we will continue with Nginx configuration.

Setting up Nginx .well-known folder

If you followed my previous guide to setup your Nginx reverse proxy, your /etc/nginx/sites-available/default config file must look something like this.

server {
listen 80;
listen [::]:80;
server_name website1.mydomain.com;

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

location / {
include proxy.conf;
proxy_pass http://192.168.0.4:80/;
}
}

As we can see here we are redirecting everything to 192.168.0.4:80.
We want to add the .well-known location to this configuration, in order to do this we need to create this folder first. You can create it wherever you want on your Nginx server.

mkdir -p /var/www/ssl/website1/.well-known
echo "WEBSITE1 SSL TEST" > /var/www/ssl/website1/.well-known/test.html

Next we are adding it to our Nginx configuration. Note that we are still using HTTP as this point.

server {
listen 80;
listen [::]:80;
server_name website1.mydomain.com;

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

location /.well-known {
root /var/www/ssl/website1/;
}

location / {
include proxy.conf;
proxy_pass http://192.168.0.4:80/;
}
}

Reload Nginx configuration service nginx reload and you should now be able to access your URL, in my case, http://website1.mydomain.com/.well-known/test.html.

Using Certbot to generate an SSL certificate for your domain

Start with the following command to launch Certbot automated certificate generation.

certbot certonly

You will first be prompted for the authentication method to use with the ACME CA. Select 1: Place files in webroot directory (webroot).
Next you are asked to enter your domain name, in my case website1.mydomain.com. And finally you are asked for the webroot for your domain. Here type /var/www/ssl/website1/ or your own folder created previously.

If you did all the previous steps correctly you should now have been issued a letsencrypt SSL certificate directly installed on your server. We’ll now see how to configure it in Nginx and secure it enough to get a ‘A’ grade on SSL testers.

Setting up Nginx for SSL termination

Back in /etc/nginx/sites-available/default we are going to add a block to handle HTTPS connections and redirect all HTTP to the HTTPS version of our website. Replace your previous configuration with this one.

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

server_name website1.mydomain.com;
return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl;
listen [::]:443 ssl;
server_name website1.mydomain.com;

ssl on;
ssl_certificate /etc/letsencrypt/live/website1.mydomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/website1.mydomain.com/privkey.pem;

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

location /.well-known {
root /var/www/ssl/website1/;
}

location / {
include proxy.conf;
proxy_pass http://192.168.0.4:80/;
}
}

In order to get the ‘A’ grade for your SSL configuration there is a few tweaks to do, I won’t go through all of them here, but I recommend reading about setting up strong SSL encryption here.

If you used the Nginx configuration described in my previous guide you should already have part of the configuration required for the ‘A’ grade, you will just need to add one thing for your domain.

Using openSSL, generate a new 2048 bits Diffie-Hellman group, replace with your own path.

openssl dhparam -out /etc/letsencrypt/live/website1.mydomain.com/dhparams.pem 2048

Then add it in your /etc/nginx/sites-available/default configuration.

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

server_name website1.mydomain.com;
return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl;
listen [::]:443 ssl;
server_name website1.mydomain.com;

ssl on;
ssl_certificate /etc/letsencrypt/live/website1.mydomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/website1.mydomain.com/privkey.pem;
ssl_dhparam /etc/letsencrypt/live/website1.mydomain.com/dhparams.pem;

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

location /.well-known {
root /var/www/ssl/website1/;
}

location / {
include proxy.conf;
proxy_pass http://192.168.0.4:80/;
}
}

Finally service nginx reload and check your server’s grade at SSLlabs. This is not an extensive SSL configuration guide and I would strongly recommend that you read more on the subject, use the links provided in this article as a starting point to get better security.

Configuring Apache mod_rpaf

Back on your apache server on 192.168.0.4.

apt-get install unzip build-essential apache2-dev
wget https://github.com/gnif/mod_rpaf/archive/stable.zip
unzip stable.zip
cd mod_rpaf-stable
make
make install

Create a file in/etc/apache2/mods-available to load the rpaf module.

nano /etc/apache2/mods-available/rpaf.load

Add the following line in the file rpaf.load.

LoadModule rpaf_module /usr/lib/apache2/modules/mod_rpaf.so

Create the module configuration file.

nano /etc/apache2/mods-available/rpaf.conf

Add the following lines inside rpaf.conf

RPAF_Enable On
RPAF_Header X-Real-Ip
RPAF_ProxyIPs 192.168.0.2
RPAF_SetHostName On
RPAF_SetHTTPS On
RPAF_SetPort On

Activate the module with a2enmod rpaf and reload apache configuration with service apache restart.

Now any website you add on Apache behind Nginx will use SSL without further configuration.

Conclusion

This guide allowed us to create a full web platform that can be very easily saved and restored to others servers. Using Proxmox built-in backup and restore system you are now able to replicate your platform from one Proxmox server to another as well as your SSL certificates which will still work if moved. In the next guide we will see how to install Piwik to provide analytics in this kind of environment.

6 thoughts on “Configuring SSL with letsencrypt certbot on NGINX reverse proxy”

  1. Followed the directions with the only exception is the site name and directory. Certbot returns with the error:
    The client lacks sufficient authorization
    Invalid response from MY_HOST…
    404 Not Found
    The webroot works – I can hit it using the test.html file in .known_hosts.

    nginx is proxying an internal, separate server.

    Suggestions?

    1. Are you running these commands as root? Try sudo? Otherwise check the permissions on the well-known folder and files, make sure it is set to 755? Hope this helps.

  2. Reading through this article I realised that the only thing stopping certbot is that it cant catch requests to the .well-known folder when a reverse proxy is configured, so I just commented out the three lines for reverse proxy, ran `certbot` (no params), and everything was installed & configured correctly. After un-commenting the reverse proxy lines again everything works as expected, and SSL report gives me an ‘A’ rating with a stock nginx install on debian 9.

Leave a Reply

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