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.
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
- 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;
-
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;
}