NGINX, reverse proxy e HTTP_X_FORWARDED_FOR

Premessa: se a parte hwupgrade c’è un forum più specializzato su questi temi.. fatemelo sapere.
Detto questo.

Oggi ho avuto uno incontro scontro in cui io sostengo una cosa, un mio collega un’altra.
Cerco di esporre i fatti nel modo più preciso possibile.
Server web linux su apache in DMZ (chiamiamolo FRANCO)
Reverse proxy che risponde all’url https://pippo.example.com e manda la richiesta a FRANCO
Il reverse proxy ha impostato questo nella sua configurazione

proxy_set_header X-Forwarded-For $remote_addr;

Per quello che si capisce il comando dice "Passa a FRANCO un header con il parametro X-Forwarded-For e valore l’ip dell’utente remoto.

Ora ci sono in realtà 3 personaggi
QUI dice che che il campo X-Forwarded-For indica l’indirizzo ip della rete privata dell’utente
QUO dice che quel campo è manipolabile dall’utente e quindi può mettere quello che vuole
QUA dice che che quel valore è l’ip dell’utente (o al massimo un altro proxy in mezzo) e non si può toccare e quindi è affidabile (ovviamente escludiamo il caso in cui si riesca a manipolare il pacchetto TCP)

Chi ha ragione?

E’ un po’ che non smanetto su apache ma se non ricordo male, impostare proxy_set_header X-Forwarded-For $remote_addr; sulla configurazione del reverse proxy sovrascrive completamente qualsiasi header X-Forwarded-For pre-esistente e lo imposta al valore di $remote_addr, che è l’indirizzo IP effettivo della connessione TCP ricevuta dal reverse proxy.

Qui dice cazzate.
Quo avrebbe ragione ma impostarlo nel reverse proxy sovrascrive qualsiasi cosa un utente abbia mandato. Quindi in questo caso, non c’e’ quel rischio.
Qua ha ragione ma occhio perche’ se c’e’ un altro proxy tra chi fa la richiesta e il reverse proxy, ricevete l’ip del proxy.

Ripeto, occhio perche’ son tipo boh, 10 anni che non tocco apache :asd:

QUI toppa prorpio.

QUO ok ma bisogna precisare:

X-Forwarded-For è un HTTP header

Un qualsiasi client però è in grado di inviare:

X-Forwarded-For: 1.2.3.4

O

X-Forwarded-For: QUI-QUO-QUA

però il reverse proxy, NGINX in questo, caso azzera quello che riceve dal client (lo manipola) e ci pensa lui a riscrivere il valore

QUA da una mezza verità:

$remote_addr sarebbe l’IP sorgente della connessione TCP

non è falsificabile a livello HTTP, se escludiamo lo spoofing TCP/IP

l’affidabilità ce l’hai solo fino a NGINX (reverse proxy)

Se la chiamata è diretta si vede l’IP reale dell’utente
Se entrano in gioco una qualsiasi di queste variabili:
NAT
CDN
un qualsiasi proxy
altre tipologie di NAT etc

allora quello che si vede con $remote_addr non è l’IP dell’utente finale, ma dell’ultimo hop della chiamata :sisi:

2 Likes

Io avevo capito che e’ tutto apache, semplicemente hanno quel reverse proxy configurato. Comunque credo che quell’header alla fine funzioni alla stessa maniera sia per apache che nginx.

non lo so ho visto che ha messo nginx :look: come titolo cmq si piu o meno il parametro è quello

cmq è sempre una chiamata a livello HTTP, al posto del mio client metti apache come concetto

Si si, infatti son confuso :asd:

Nel titolo e’ nginx ma poi parla di apache :vface:

Premetto che io sono colui che sostiene che - escludendo spoofing a livello tcp - il valore é valido (faccio qua) e usabile.
Il reverse proxy é nginx, mentre il server web é apache.

Andando piu nel profondo, anche facessero spoofing a livello tcp e mettessero un ip nella classe 192.168.0.0/16 , alla peggio il pacchetto di ritorno resta nella rete (direi che al limite rischio un ddos)

Spero di avere chiarito

Magari summon anche @SkyLinx se vuole portare il suo autorevole contributo ne sarei davvero grato

Spe che rileggo

Ah adesso che hai editato e’ chiaro. Quindi c’e’ un nginx che accetta richieste e poi fa da reverse proxy su un apache che serve il sito stesso.

Boh, non cambia la risposta che abbiamo dato io e The End imho

Concordo

X-Forwarded-For Non lo setto IO client. Se non c’è proxy di mezzo, X-Forwarded-For non c’è proprio.

Tizio Client chiama Piero (nginx), e gli dice dammi sito Pippo (no X-Forwarded-For)

Piero si gira verso Franco (Apache) e gli dice dammi sito Pippo per tizio (X-Forwarded-For=$tizio)

Franco risponde a Piero, dicendogli ecco Pippo per tizio

Piero risponde a tizio dicendo eccoti Pippo

Il valore di $tizio è l’ip da cui arriva la chiamata di tizio verso Piero

In realtà qui quo e qua dicono cazzate non verificabili, topolino va deportato perchè è nero e zio paperone è negli epstein files

2 Likes

scusate, sbagliato topic

Topolino amico delle guardie

1 Like

Mi inserisco, secondo MDN x-forwarded-for non è proprio standard (come, se non ricordo male) tutti gli header x-*

Quindi non ha una semantica ben definita, secondo MDN fra l’altro potrebbe essere una lista di ip non un solo valore.

Quindi quello che può succedere è che al proxy nginx arrivi una richiesta con già l’ header settato a cui lui dovrebbe poi aggiungere il suo record

A proxy IP address. If a request goes through multiple proxies, the IP addresses of each successive proxy are listed. This means that the rightmost IP address is the IP address of the most recent proxy and the leftmost IP address is the address of the originating client (assuming well-behaved client and proxies).

Quindi è almeno in parte forgiabile

Quindi in realtà hanno torto tutti e tre, perché danno per scontato che ci siano 3 personaggi mentre invece ce ne possono essere N.

Questa cosa può essere vera se la direttiva impostata sul reverse proxy non è ben fatta.

Da come ho capito io se sul RP c’è il valore :

proxy_set_header X-Forwarded-For $remote_addr;

che non è questo

To simply append the incoming address to the header, but preserve the original values too, you can use this nginx config:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

allora il RP crea l’header X-Forwarded-For (a cui posso dare il nome che voglio) e gli mette SOLO l’ip sorgente

Anche perchè se non ci fosse il proxy, potrei usare tranquillamente
$S_SERVER[“REMOTE_ADDR”]

Non è standard solo perchè è parametrizzabile a piacere..

A me sembra però che la questione sia mal posta, si da per scontato che la connessione sia
Client > Pippo > Franco

Però né pippo né Franco possono essere sicuri che sia così, potrebbe esserci dietro una catena di proxy

Client > patagarru > ashenzello > kimtagarru > astrasashello > pippo > franco

L’unico IP su cui pippo ha visibilità è quello subito dietro di sé e su cui può garantire la provenienza.
Che è quello che dice @The.End , solo l’ultimo hop.

Se tutta la catena di proxy è onesta, tramite X-Forwarded-For puoi risalire all’indirizzo IP pubblico da cui è uscita la richiesta, che però non è nemmeno detto sia l’IP del client perchè poi ci sono i NAT.
Comunque non hai garanzia che sia onesta.

Ma infatti nella prima frase, QUA scrive

Il mio scopo finale è poter identificare se la richiesta arriva dall’interno della mia rete (quindi con un ip nella classe 192.168.0.0/16) oppure da fuori per far agire in modo diverso l’applicativo web

Ho capito come funziona insomma. The End e’ il figlio preferito.

2 Likes

Beh l’unica soluzione brutta ma sicura e semplice che mi viene in mente allora e’
due versioni dell’app, una per uso interno, una esterno.
Una la metti su una macchina/VM/Container/Triciclo accessibile solo via rete interna (o vpn se proprio) e una accessibile da interno ed esterno. cicciosuperplus.com per quella pubblica, internal.cicciosuperplus.com per quella interna. Avete qualcuno che si occupa della parte di infra? Mi vien da pensare di no se devi chiedere pareri su NGI :asd:

Devi andare giu’ di un livello insomma. A livello di webserver/app logic non credo tu possa avere una certezza ragionevole a giudicare da quanto questo punto sembra importante.

Il problema è che stiamo parlando di mediawiki e ho BISOGNO che il FQDN sia identico sia dall’interno che dall’esterno.

Io avevo proposto (cosa che già avevo fatto dove lavoravo prima) di mettere un entri nel nostro DNS che punta all’ip privato della macchina

in sostanza
DNS di Active directory con host pippo.example.com → 192.168.99.10
Dns pubblici pippo.example.com → 137.15.X.X

In questo modo se sei nella rete interna vai dalla rete privata, se sei da fuori risponde con ip pubblico.
Però a quanto pare è una cosa da non fare altrimenti esplode il mondo…

Diciamo che abbiamo idee moolto diverse.. ma molto molto..