Come creare un Reverse Proxy con NGINX

Un Reverse Proxy è un server che si frappone tra i client ed uno o più server. Fa letteralmente da intermediario ed ha un comportamento dettato da regole relative a varie caratteristiche delle richieste che riceve. Vediamo insieme come configurare NGINX come Reverse Proxy.

Condividi
Come creare un Reverse Proxy con NGINX

Introduzione

Un Reverse Proxy è un server che -in parole povere- si frappone tra i client ed uno o più server. Fa letteralmente da intermediario, ha un comportamento dettato da regole relative a varie caratteristiche delle richieste che riceve, ed in generale aumenta la sicurezza di una infrastruttura.

Per fare un esempio pratico, facciamo finta di avere un sito business.firenetwork.it, con varie funzionalità:

  • una landing page, raggiungibile su business.firenetwork.it/home
  • un fileserver web, raggiungibile su business.firenetwork.it/upload
  • una pagina di status dei servizi, raggiungibile su business.firenetwork.it/status

Senza un Reverse Proxy, gli unici modi per ottenere un sito web simile sono:

  • gestire i vari path lato server web, di conseguenza serve avere tutte le applicazioni web sullo stesso server web.
  • tenere le tre applicazioni web su tre server web separati, ma dover raggiungere i tre servizi usando FQDN, IP o Porte diverse a seconda della destinazione.

Avendo un Reverse Proxy invece, è possibile far passare tutto il traffico destinato a business.firenetwork.it su NGINX, che poi a seconda del path richiesto dal browser ci reindirizzerà sul webserver di competenza in modo completamente trasparente (il browser non sa niente di ciò che avviene dietro le quinte).

‼️
FUN FACT
state raggiungendo questo sito proprio attraverso un NGINX che fa da Reverse Proxy 😁

Come creare una regola di Reverse Proxy (HTTP)

Come abbiamo visto nella guida Introduzione ad NGINX, le regole di NGINX sono dei file in /sites-available symlinkati su /sites-enabled, perciò il primo passo da compiere è quello di creare un nuovo file in /sites-available.

nano /etc/nginx/sites-available/test

Compiliamo il file con il seguente contenuto (tra poco lo analizzeremo riga per riga)

server {
    listen 80;
    server_name test.firenetwork.it;
    location / {
        proxy_pass http://192.168.0.1:8080;
        proxy_set_header X-Forwarded-Host $host:$server_port;
        proxy_set_header X-Forwarded-Server $host:$server_port;
        proxy_set_header X-Forwarded-For $host:$server_port;
        proxy_set_header Host "test.firenetwork.it";
    }
}

Creiamo un symlink al file verso /sites-enabled per abilitarlo

ln -s /etc/nginx/sites-available/test /etc/nginx/sites-enabled/test

Salviamo il file e ricarichiamo NGINX

sudo systemctl reload NGINX

Cosa contiene il file che abbiamo appena creato?

  • il blocco server indica ad NGINX che deve tirare su un server web
  • la direttiva listen specifica la porta su cui deve rimanere in ascolto
  • server_name indica invece che NGINX deve gestire una richiesta seguendo questa regola quando il client sta richiedendo un URL di test.firenetwork.it
  • il blocco location può essere presente più volte all'interno del blocco server, ed indica regole diverse che si applicano a seconda del path richiesto. In questo esempio, c'è una sola regola che si applica a tutti i path derivati da test.firenetwork.it (ciò è indicato dal carattere /)
  • la direttiva proxy_pass specifica ad NGINX quale è il server di backend a cui deve fare riferimento per questo tipo di richieste
  • le direttive proxy_set_header modificano alcune intestazioni della richiesta, prima di girarla al server di backend. Le approfondiremo in un articolo separato, per ora vi basta sapere che proxy_set_header Host indica l'URL che verrà visualizzato sul browser del client una volta che riceverà la risposta dal backend

Censimento record DNS

Prima di poter utilizzare la regola di Reverse Proxy che abbiamo appena creato, ci manca un ultimo passaggio: dobbiamo inserire un record DNS in modo che test.firenetwork.it venga reindirizzato sul nostro nodo di NGINX (nel mio caso 192.168.0.16)

Potete farlo dal vostro server DNS, che sia AdGuard Home, il server DNS di Windows Server, oppure il vostro Gateway.

Se avete più istanze di NGINX a cui girare queste richieste, potete inserire due record DNS.

Come risultato, quando un client richiederà il nostro sito (http://test.firenetwork.it), interrogherà NGINX (http://192.168.0.16) e riceverà il contenuto del webserver (http://192.168.0.1:8080)

Come creare una regola di Reverse Proxy (HTTPS)

Nel caso in cui vogliate creare un Reverse Proxy che funzioni in HTTPS, la procedura di base rimane la stessa appena vista, ma serve qualche informazione in più nel file della regola, oltre ovviamente ad un certificato valido da collegare.

Vediamo insieme come fare.

In questo esempio, il server web http://192.168.0.1:8080 non ha attivo e non supporta HTTPS. Per rendere più sicura la comunicazione con questo server web, abbiamo deciso di creare un Reverse Proxy con terminazione TLS.

Censire il certificato

Prima di tutto, importiamo il certificato che NGINX dovrà usare per criptare la connessione.

Create una cartella in /etc/nginx/certs per contenere i due file .pem

sudo mkdir /etc/nginx/certs/test.firenetwork.it

Copiate in questa cartella i due file .pem del vostro certificato e ricordatevi il loro percorso per esteso, perché dovremo indicarlo a mano nella regola.

Creazione della regola

Seguite i passaggi indicati per la regola in HTTP nel capitolo precedente, ma stavolta create un file con il contenuto di questo modello:

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

upstream test-default {
    zone test 64k;
    keepalive 2;
    server 192.168.0.1:8080;
}

server {
    listen 80;
    listen [::]:80;
    server_name test.firenetwork.it;
    if ($host = test.firenetwork.it) {
        return 301 https://$host$request_uri;
    }
    return 404;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl http2;
    server_name test.firenetwork.it;
    client_max_body_size 525M;
    ssl_certificate /etc/nginx/certs/test.firenetwork.it/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/test.firenetwork.it/privkey.pem;
    location / {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        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_pass http://test-default;
    }
}
  • la sezione map ci serve per garantire stabilità alle connessioni websocket. Si tratta di connessioni che necessitano di una certa persistenza, che non è possibile garantire con le impostazioni di default di NGINX. Infatti, di default NGINX chiude la comunicazione appena riesce ad inoltrare la risposta del backend al client.
  • la sezione upstream (il cui nome viene poi richiamato nel server HTTPS) indica ad NGINX quali server di backend utilizzare per l'inoltro delle richieste. All'interno di questa sezione, indicate alla direttiva server l'indirizzo e la porta del vostro server di backend.

Poi, sono presenti due sezioni server:

La prima, in ascolto sulla porta 80 in HTTP, serve solamente a mandare al browser un segnale di redirect per reindirizzarlo correttamente sulla porta 443 in HTTPS.

La seconda invece contiene tutte le direttive necessarie a gestire l'inoltro al server di backend, e le impostazioni necessarie per HTTPS:

  • valorizzate ssl_certificate e ssl_certificate_key con il full path dei vostri due file .pem relativi al certificato SSL
  • Tutte le direttive all'interno della location sono propedeutiche all'instaurazione di una connessione HTTPS
  • La direttiva proxy_pass invece fa puntamento all'upstream definito più in alto

Ora, come già fatto per il Reverse Proxy in HTTP, ricaricate la configurazione di NGINX e censite la entry nel server DNS per la risoluzione.

Risultato e considerazioni

Come risultato avrete implementato HTTPS verso un webserver che non lo supporta, lasciando ad NGINX tutto ciò che concerne la criptazione della connessione.

Una considerazione ovvia ma importante: il traffico tra il client ed NGINX sarà criptato, ma il traffico da NGINX al webserver di backend sarà comunque in HTTP.

Detto ciò, valutate con cognizione di causa se impostare una terminazione SSL è la scelta migliore nel vostro caso.