HTTPS load balancing using NGINX

HTTP(S) load balancing is an invaluable tool for scaling your website or web application, allowing you to route your traffic through a single IP and distribute it across multiple backends.

load balancer architecture

Figure 1: Architecture of NGINX-based HTTPS load balancer (Google Cloud Community Tutorials 2015)

Create a container

$ lxc launch ubuntu:22.04 loadbalancer
Creating loadbalancer
Starting loadbalance

Upgrade packages

# login `loadbalancer` container
$ lxc exec loadbalancer -- su ubuntu

# update
$ sudo apt update 
# upgrade
$ sudo apt upgrade

Install Nginx

# install
$ sudo apt install nginx -y

# show version
$ nginx -v
nginx version: nginx/1.18.0 (Ubuntu)

$ openssl version
OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)

Create a Self-Signed SSL Certificate

$ sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt

# create a strong Diffie-Hellman (DH) group
$ sudo openssl dhparam -out /etc/nginx/dhparam.pem 4096

Configuring Nginx to Use SSL

  1. Creating a Configuration Snippet Pointing to the SSL Key and Certificate
    $ sudo nano /etc/nginx/snippets/self-signed.conf
    

    Within this file will look like the following:

    ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
    
  2. Creating a Configuration Snippet with Strong Encryption Settings

    $ sudo nano /etc/nginx/snippets/ssl-params.conf
    

    Within this file will look like the following:

    ssl_protocols TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_dhparam /etc/nginx/dhparam.pem; 
    ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    ssl_ecdh_curve secp384r1;
    ssl_session_timeout  1d;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    
    # Enable TLSv1.3's 0-RTT. Use $ssl_early_data when reverse proxying to
    # prevent replay attacks.
    #
    # @see: https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_early_data
    ssl_early_data on;
    
    
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
    
    # Disable strict transport security for now. You can uncomment the following
    # line if you understand the implications.
    #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";
    

Adjusting the Nginx Configuration to Use SSL

   $ sudo nano /etc/nginx/sites-available/default

Configuring HTTP/2 Support Within this file will look like the following:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

# Create a new top-level upstream directive and add your backend server instances to it

upstream backend_crm {
  server IP_ADDRESS_1:443;
  server IP_ADDRESS_2:443;
  server IP_ADDRESS_3:443;

  # Limiting the number of idle keepalive connections 
  # stored in each worker process cache
  keepalive 32;
}

# Default server configuration
#
server {
        #listen 80 default_server;
        #listen [::]:80 default_server;

        # SSL configuration
        #
        listen 443 ssl http2 default_server;
        listen [::]:443 ssl http2 default_server;
        #
        # Note: You should disable gzip for SSL traffic.
        # See: https://bugs.debian.org/773332
        #
        # Read up on ssl_ciphers to ensure a secure configuration.
        # See: https://bugs.debian.org/765782
        #
        # Self signed certs generated by the ssl-cert package
        # Don't use them in a production server!

        #
        # include snippets/snakeoil.conf;

        include snippets/self-signed.conf;
        include snippets/ssl-params.conf;

        root /var/www/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name LOAD_BALANCER_IP;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                # try_files $uri $uri/ =404;

                #recommended Nginx header forwarding settings
                include proxy_params;

                #
                proxy_pass https://backend_crm;
                
                
                # Intercept Link header and initiate requested Pushes
                http2_push_preload on;

                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;

                

        }
}
# This is a bare-bones configuration that listens on port 80 and performs the redirect to HTTPS.

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

    server_name LOAD_BALANCER_IP;

    # Redirect

    # return 302 https://$server_name$request_uri;
    # Permanent 
    return 301 https://$server_name$request_uri;
}

Reference