Skip to content

Instantly share code, notes, and snippets.

@casebeer
Created May 11, 2024 20:40
Show Gist options
  • Save casebeer/5c4ead8e23613a091c7b6fe8dfe90c05 to your computer and use it in GitHub Desktop.
Save casebeer/5c4ead8e23613a091c7b6fe8dfe90c05 to your computer and use it in GitHub Desktop.
Nginx reverse proxy config for Ruckus Unleashed controller w/ backend failover support
#
# Reverse proxy config for Ruckus Unleashed controllers
#
# Requirements:
#
# - DNS entries for all unleashed<IPv4 last octet of controllers>.example.com
# - DNS entry for unleashed.example.com
# - (DNS entries to point to Nginx reverse proxy)
# - Wildcard TLS cert on reverse proxy (or SAN cert covering all above mentioned names)
#
# Problem:
#
# Unleashed controllers elect one controller to be the primary. All other controllers
# will 302 redirect to that primary. However, Nginx open source edition cannot detect
# this 302 as a "backend down" signal, so we instead map those redirects to backend-specific
# frontend hostnames, then use those hostnames on the reverse proxy to determine the
# correct Unleashed controller to hit.
#
# We could probably also set a cookie with the correct backend to hit and avoid needing
# DNS and certificates for multiple hostnames, but this is fairly simple assuming internal
# DNS and wildcard ACME certs already configured on the Nginx reverse proxy.
#
# hostname-only (via search domain) to FQDN redirect
server {
server_name "~^unleashed\d{0,3}$" ;
listen 80;
listen [::]:80; # ipv6only=on;
return 302 https://unleashed.example.com$request_uri;
}
# Map frontend (i.e. reverse proxy) hostname to specific backend IP address
# Required because Unleashed controllers elect a primary amongst themselves,
# then any non-primaries simply 302 redirect to the primary. Ideally, we'd
# have the reverse proxy do a health check and fail any backends issuing these
# "I'm not primary" 302s, but that's an Nginx Plus subscription-only feature.
map $host $backend_ip {
hostnames;
"~*^unleashed(?<octet>\d{1,3})\.?.*$" 192.168.0.$octet;
default 192.168.0.123; # default Unleashed primary controller
}
server {
server_name "~^unleashed\d{0,3}.example.com$";
if ($https = "") {
return 302 https://$server_name$request_uri;
}
# docker DNS resolver
resolver 127.0.0.11 valid=30s;
# need separate v4 and v6 listens to avoid `bind() to [::]:80 failed (98: Address already in use)`
listen 80;
listen [::]:80; # ipv6only=on;
listen 443 ssl;
listen [::]:443 ssl; # ipv6only=on; # if your nginx version is >= 1.9.5 you can also add the "http2" flag here
access_log /dev/stdout combined_host; #client_certs;
## TLS config
#add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
ssl_certificate /certs/*.example.com_ecc/fullchain.cer;
ssl_certificate_key /certs/*.example.com_ecc/*.example.com.key;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_ecdh_curve X25519:prime256v1:secp384r1;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384::ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
location / {
# backend IP is determined by frontend hostname via map section above
proxy_pass https://$backend_ip;
# override the Host header going to backend
# Unleashed will respond to any Host we provide, and by spoofing Host to the proxy (not
# backend) FQDN, we'll get Redirect locations etc. set properly by the backend
proxy_set_header Host $host;
proxy_ssl_server_name off;
# Intercept "I'm not the Unleashed Primary" redirects
# These are always to the bare host of the primary, with no path. Handle by
# changing frontend domain to backend-specific hostname.
proxy_redirect "~^https://192.168.0.(?<octet>\d{0,3})" https://unleashed$octet.example.com;
# normal backend redirect handle as normal
# Any redirects with a path, even just `/` are "normal" redirects.
proxy_redirect "~^https://192.168.0.\d{0,3}/" https://$host;
proxy_ssl_trusted_certificate /certs/unleashed-backend.example.com_SELFSIGNED_cert.pem;
proxy_ssl_verify off; # verify off since still WIP to get trustable self-signed certs on Unleashed controllers
proxy_ssl_verify_depth 3;
proxy_ssl_session_reuse on;
proxy_ssl_protocols TLSv1.1 TLSv1.2;
proxy_ssl_ciphers HIGH:!aNULL:!MD5;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment