Free SSL for multiple domains with Letsencrypt and Nginx on a Raspberry PI

In the Netherlands we have some amazing bandwith (300Mbit/30Mbit) at our homes. Combined with these dead cheap but very capable Raspberry Pi's readily available, I figured that I could easily run a couple of web servers at home. It's no problem to run Wordpress or a .Net Core web site (Scott Hanselman) on a Pi. The only issue I faced is that I have single public ip-address, but multiple sites to host. The other thing is that I wanted the traffic to these site encrypted with an SSL certificate. I don't want to log in with forms authentication over an unencrypted wire.

I've had been working on a Raspberry Pi Wordpress site before and back then noticed that Nginx is not just a web server, but a proxy server as well. Hmmm... let's combine everything and let's see if we can get this to work. The goal here is to host multiple web sites on multiple Raspberry Pi's behind a proxy. I have a domain name mertarauh.com, where I want to host a Wordpress site at the root, and my SmartPortal (energie consumption monitor) at a sub domain smartportal.mertarauh.com

So let's get started with these 3 basic steps:

  1. Setup the DNS at the domain registar for both the root and sub domain to point to the external ip.
  2. Install Nginx (not the php part) on a clean Raspbian Jessie Light and assign a static internal ip address.
  3. Configure your router to forward all request at port 80 and 443 to the configured internal ip address.

All traffic will now be forwarded to my Nginx service running at the Raspberry Pi.

Let's get some SSL certificates. There is an almost perfect blog post on how to install and configure Letsencrypt on a Debian ditribution (like Raspbian). So just follow that one while you replace the domain names and add yours. We can now make a secure connection to our Nginx service, it is just that the service doesn't know what to do with it. Besides that there is a minor mistake in the configuration which prevents succesfull renewal of your certificates. So how should your Nginx config look?

upstream smartportal {
        server 192.168.24.65;
}

upstream wordpress {
        server 192.168.24.70;
}

server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name _;
        root /var/www/mertarauh.com;

        location / {
                return 301 https://$host$request_uri;
        }

        #letsencrypt
        location ^~ /.well-known/ {
                allow all;
        }
}

server {
        listen 443 ssl http2 default_server;
        listen [::]:443 ssl http2 default_server;
        include snippets/ssl-mertarauh.com.conf;
        include snippets/ssl-params.conf;
}

server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name mertarauh.com;
        include snippets/ssl-mertarauh.com.conf;
        include snippets/ssl-params.conf;
        proxy_buffering off;

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

server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name smartportal.mertarauh.com;
        include snippets/ssl-mertarauh.com.conf;
        include snippets/ssl-params.conf;
        proxy_buffering off;

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

Let me explain what is going on here. First I define my two up stream applications.

Then I configured a catch all default_server on port 80 which will redirect all requests to the https equivillent except for requests to the /.well-known/ directory. This is required for autorenewal of your SSL certificates (<- this is missing in the blog post).

Then I have a default_server without a server_name on port 443. This is purely for the SSL hand shake to succeed. The hand shake takes place before the host name is known.

And finally I configured the two servers for which I like to do SSL termination. I simply forward the requests.

DONE! 

No Comments