I'm not expecting anyone to notice, apart from my colleague at work who I've been badgering for help, but I've moved my blog to blog.swakes.co.uk. Two main reasons. One was that my SSL certificate had expired, took two days to notice, and the other was 'restructuring'. If you had a goose at my old certificate it contain around 50 SAN entries. Everything ranging from store, blog, camera, media to fishtank.swakes.uk. In pure haste and excitement, I created every possible SAN I would possibly use not understanding and probably giving security a thought.

Now this lovely new domain, which I've had for years, will be the public facing side for anything SWAKES hosting related. The top domain, swakes.co.uk, simply hosts one of my favourites, a glitchy GIF ... until I can figure out what to do with the space.

In regards to the good old swakes.uk, I'm keeping that restricted for my internal shit. Any who let's get to the good stuff, NGINX. For some context, I'm too lazy at home to remember IP's and port numbers for internal sites such as backend router, Plex server etc, so I've assigned my most used links to domains e.g. 10.1.2.3:32400 > https://plex.swakes.uk. Using some shite HTML , I've host a sort of 'landing' page internally, mainly due to pure idleness. It's handy as shit though.

How is all this possible you're not saying. Well tough. I am going to painstakingly document this out. This is mainly 10% for anyone who is interested, and 90% for myself. I regularly make the mistake of not documenting any of my setups/installs and then having to spent long nights going through the same pain. I've ultimately been putting this post off for some time so let's get this shit done.


NGINX - Lean Mean SSL Reverse Proxy Machine

So what is NGINX? I'm going to be lazy and simply copy and paste the following (saucy) ...

NGINX is open source software for web serving, reverse proxying, caching, load balancing, media streaming, and more. It started out as a web server designed for maximum performance and stability. In addition to its HTTP server capabilities, NGINX can also function as a proxy server for email (IMAP, POP3, and SMTP) and a reverse proxy and load balancer for HTTP, TCP, and UDP servers.

For our purpose we are going to combine NGINX with LetsEncrypt to allow us to create a free, multi SAN entry SSL certificate ... Niiice. Once obtained, we'll be able to configure NGINX to host sites or redirect those internal IP's (192.168.1.1) to something more memorable (router.swakes.co.uk). All with the brucie bonus of being covered by an SSL certificate. Let's crack on ...

Things you'll need:

Some key bits you will need to get this up and running:

Domain Name - You'll need your own domain name and access to the 'backend/web portal' to make some DNS changes. I regularly use NameCheap for my domains.

Raspberry Pi/Linux OS - Ultimately you need somewhere to host the NGINX server. A Raspberry Pi is ideal however any Linux environment will do (You can do this on Windows but I can't be arsed to do two guides - Google that shit). It's also worth taking into consideration that this server may need to be on 24/7 if you're going to be hosting sites etc (ideal using a Raspberry Pi).

Port Forwarding - Make sure you can access your routers 'backend' portal to setup port forwarding


Certbot and LetsEncrypt

We are going to using a handy tool called Certbot to request our certificate. To begin we'll need to add Certbot's PPA:

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update

Now it knows where to find it, let's install:

sudo apt-get install certbot

Cool beans. Before we go ahead and use Certbot, we'll need to setup some DNS records for your chosen domain. Depending on who your domain name provider is, you'll need to log in and access the 'Advanced DNS' settings to add 4 records:

A Record	*			YourExternalIP
A Record	@			YourExternalIP

CAA Record	@			0 issue "letsencrypt.org"
CAA Record	YourDomainName		0 issue "letsencrypt.org"

TTL needs to be set to Automatic

The two A Record are to point your domain name to your server IP (with some port forwarding magic). The other two CAA Records are for extra security ... Copy and pasted definition below:

Certificate Authority Authorization (CAA) is a standard designed to prevent bad actors from creating unauthorized SSL/TLS certificates. CAA records allow domain owners to specify which Certificate Authorities (CAs) are permitted to issue certificates.

Now that's done, let's setup some port forwarding. This time you'll need to setup these two ports to the internal IP of the server (192.168.1.123).

  • Start/End Ports: 80 and 433
  • Service Type: TCP and UDP
  • Listening IP: YourServerIP

Nice. At this point we should have your domain name pointing to your external IP and ports 80 and 433 open for your server. Everything is now ready for Certbot to request that cert'. Run the following command and list out your domains similar to this request for swakes.co.uk:

sudo certbot -d swakes.co.uk -d www.swakes.co.uk -d blog.swakes.co.uk -d design.swakes.co.uk ...

Before you go crazy and list every possible subdomain under the sun (58 SAN entries I had at one point), you need to make sure that you list your domains (swakes.co.uk, www.swakes.co.uk in the example above) first before any other subdomain (blog.swakes.co.uk, design.swakes.co.uk).

Now you've got your domains ready (-d ... -d ... -d ...) it's worth double checking the spelling before hitting that enter key.

If all has gone well with ports and IP's being setup correctly, you'll start to see the script go through and validate each domain before finishing with a 'Success!' message and the location to your private key and certificate. Take note of these locations (usually /etc/letsencrypt/live/DomainName/fullchain.pem or privkey.pem) as we'll need them for the next step. NGINX.


NGINX

Now to the second part of this setup. The lean mean NGINX Reverse SSL Proxy server machine. Let's start with the following:

sudo apt update
sudo apt install nginx

After that we can check if NGINX is up and running:

sudo systemctl status nginx

Good stuff. Now go to http://ServerIP and hopefully you should see 'Welcome to NGINX'

In case you need to stop, start or restart NGINX ...

sudo systemctl stop nginx
Stop the service
sudo systemctl start nginx
Start the service
sudo systemctl restart nginx
Restart the service
sudo systemctl reload nginx
Reload the service after configuration changes
sudo systemctl disable nginx
Disable the service on startup
sudo systemctl enable nginx
Enable the service on startup

Setting up a Test Site

Now NGINX is installed we can start to setup our sites. First we'll create an example site (change example.com to your domain!)

sudo mkdir -p /var/www/example.com/html
Create a folder in www
sudo chown -R $USER:$USER /var/www/example.com/html
Allocate yourself ownership of the folder (and contents)
sudo chmod -R 755 /var/www/example.com
Allocate yourself permissions for the folder (and contents)
nano /var/www/example.com/html/index.html
Using nano editor, create and edit the file index.html
<html>
    <head>
        <title>Welcome to the wonderful Example.com!</title>
    </head>
    <body>
        <h1>Three cheers to the shop keep!  The example.com server example is working!</h1>
    </body>
</html>
Copy in some test text
sudo nano /etc/nginx/sites-available/example.com
Now create a configuration file in sites-available
server {
        listen 80;
        listen [::]:80;

        root /var/www/example.com/html;
        index index.html index.htm index.nginx-debian.html;

        server_name example.com www.example.com;

        location / {
                try_files $uri $uri/ =404;
        }
}
Copy in the example configuration
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
Create a symbolic link from sites-available to sites-enabled
sudo nano /etc/nginx/nginx.conf
A quick config update before we go live ...
server_names_hash_bucket_size 64;
Amend the hash bucket size to 64
sudo nginx -t
Ensure settings are correct
sudo nginx -s reload
Reload NGINX with new configuration and settings

Magic time! Now pop over to your domain http://example.com ...

Good stuff. Let's start to setup NGINX and crack on with some proper shit ..


NGINX Server Configuration

In order to use our brand new SSL certificate, we need to make some changes to the NGINX configuration. It's handy to know the lay of the land with NGINX as if you're manage a few sites or make regular changes, you will most likely be in digging around and amending settings quite a bit. Here is a general overview of the NGINX configuration folder structure:

  • /etc/nginx: The NGINX configuration directory. All of the NGINX configuration files reside here.
  • /etc/nginx/nginx.conf: The main NGINX configuration file. This can be modified to make changes to the NGINX global configuration.
  • /etc/nginx/sites-available/: The directory where each site configuration can be stored.  NGINX will not use the  configuration files found in this directory unless they are linked to  the sites-enabled directory. Typically, all site configuration is done in this directory, and then enabled by linking to  the other directory.
  • /etc/nginx/sites-enabled/: The directory where enabled site configurations are stored. Typically, these are created by linking to configuration files found in the sites-available directory.
  • /etc/nginx/snippets: This directory contains configuration fragments that can be included elsewhere in the NGINX configuration. Potentially repeatable configuration segments are good candidates for refactoring into snippets.

The first order of business is to create two snippet files in /etc/nginx/snippets. These configuration files can be referenced in each necessary site config saving the hassle of copying each snippet of code into every site setup.

local.conf

This file will allow you to specify which IP address/s can access your site by simple 'allow' or 'deny' statements.

sudo nano /etc/nginx/snippets/local.conf

allow 192.168.2.0/24;
allow 192.168.2.1;
allow 127.0.0.1;

deny  all;

This statement allows anyone on the 192.168.2.* network (example home network) access whilst blocking all external traffic (deny all;). This is handy to keep any of your internal domains (router, Plex server ...) inaccessible to anyone outside and still keeping your SSL connection internally. So what if you want to make a site viewable to internet such as Ghost Blog?  We'll show you how in a few moments ...

cert.conf

Here is where the super encrypted magic happens. This configuration file will outline the location of our certificate and private key (did you take note from earlier?) and also the TLS encryption standards. Again, this is handy as fuck as you'd have to ensure all of this snippet was included in your site config without it.

sudo nano /etc/nginx/snippets/certs.conf

ssl_certificate /etc/letsencrypt/live/YourDomain/fullchain.pem; 
ssl_certificate_key /etc/letsencrypt/live/YourDomain/privkey.pem;

ssl_protocols TLSv1.3 TLSv1.2;# Requires nginx >= 1.13.0 else use TLSv1.2
ssl_prefer_server_ciphers on; 
ssl_dhparam /etc/nginx/dhparam.pem; # openssl dhparam -out /etc/nginx/dhparam.pem 4096
ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
ssl_session_timeout  10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7
resolver_timeout 5s; 
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

You can always keep up to date with the latest and strongest Ciphers here.

error.conf

If you may have guessed it, this file manages your error pages/responses such as 404 or 503's. NGINX has its own 'internal' error pages by default but keep an eye out soon as I'll show you how to setup custom error pages and handling (the post is still draft stage).


Creating Site Configurations

Now we can include the 'snippets' in the site configuration file. As we are planning to serve up this page over HTTPS with our super cert', we need to change the listen port from 80 to 443:

server {
        listen 443;
        listen [::]:443;

        root /var/www/example.com/html;
        index index.html index.htm index.nginx-debian.html;
        
        include /etc/nginx/snippets/certs.conf;
        include /etc/nginx/snippets/local.conf;
        
        server_name example.com www.example.com;

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

We've also included the local.conf allowing certain users access to view the page.

sudo nginx -t
Run this to test that your configuration is correct before going live!
sudo nginx -s reload
Once tested, run this to apply the new configuration and reload NGINX.

Now navigate to your site using https://example.com. You'll now see the green 'seal of approval' in your browser confirming. To double check everything, inspect your certificate in your browser. In Chrome simply click the green padlock icon in the browser navigation bar then Certificate.

In Firefox, again click the Padlock icon, then the arrow next to Connection, More Information and then View Certificate.

I've had 174 visitors this month

Fantastic! We've now managed to setup a test site using our SSL certificate and some NGINX magic. So what's next?

Everything looks good locally so now run your domain (https://) through Qualys SSL Labs and/or SSL Shopper to see if your security is up to par. These will not only check the certificate but also give an overall rating and point out what is good and/or bad with your configuration (supports old TLS versions, no DNS CAA ...).

The DNS CAA entries and ssl_ciphers we defined earlier will help get a higher 'grade' but at the end of day the it makes your site/server more secure. What can be wrong with that? (While saying that ... excluding certain old or dated ciphers will end up causing some incompatibility issues with older browsers, but hey ... Who the fuck uses Internet Explorer Version 6 anymore?).


Example NGINX Site Configuration

Not to leave you in the dark, here is an example of my Ghost Blog configuration file. A bit more detailed than the previous setup, this listens out for a port rather than static file/folder location. In this case, Ghost Blog is hosted on the same server as the NGINX server (server 127.0.0.1) and operates on port 2368.

# configuration file /etc/nginx/sites-enabled/blog.swakes.co.uk:
server {
        listen 80;
        server_name blog.swakes.co.uk;
        return 301 https://$host$request_uri;
}
upstream blog {
	server 127.0.0.1:2368;
	keepalive 16;
}
server {
        listen 443 ssl http2;
	client_max_body_size 512m;
        include /etc/nginx/snippets/certs.conf;
        server_name blog.swakes.co.uk;
        include /etc/nginx/snippets/error.conf;
        location / {
               #include /etc/nginx/snippets/local.conf;
                proxy_pass http://127.0.0.1:2368;
		proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "Upgrade";
                proxy_ssl_verify off;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
        }
}

To edit this example for your own site, you'll need to amend the following values:

server_name example.com;		// Replace with your domain name


upstream blog {				// Change blog to site name
	server 127.0.0.1:2368;		// Your server IP and port


proxy_pass http://ServerIp:Port;	// Youro server IP and port

Whenever your creating new sites. you need run sudo ln -s /etc/nginx/sites-available/... to create a symbolic link within sites-enable and then finally sudo nginx -t followed by sudo nginx -s reload to go live!


For now I'm going to leave you there. Hopefully you've managed to get through this long ass post, ended up with a custom SSL certificate for your domain and got that configured correctly in NGINX to serve up some HTTPS goodness.

If not, It's also worth checking out the errors logs for NGINX at /var/log/nginx/error.log to see whats occuring (or not). That plus a little Google can usually point you in the right direction. If like me you've spent hours and still no luck, let me know in the comments below and I'll try and help you out!