La riscrittura https di Nginx trasforma POST in GET


17

Il mio server proxy funziona su ip A ed è così che le persone accedono al mio servizio web. La configurazione di nginx reindirizzerà a una macchina virtuale su ip B.

Per il server proxy su IP A, questo è disponibile nei miei siti

server {
        listen 443;
        ssl on;
        ssl_certificate nginx.pem;
        ssl_certificate_key nginx.key;

        client_max_body_size 200M;
        server_name localhost 127.0.0.1;
        server_name_in_redirect off;

        location / {
                proxy_pass http://10.10.0.59:80;
                proxy_redirect http://10.10.0.59:80/ /;

                proxy_set_header Host $http_host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

}

server {
        listen 80;
        rewrite     ^(.*)   https://$http_host$1 permanent;
        server_name localhost 127.0.0.1;
        server_name_in_redirect off;
        location / {
                proxy_pass http://10.10.0.59:80;
                proxy_redirect http://10.10.0.59:80/ /;
                proxy_set_header Host $http_host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

È proxy_redirectstato preso da come posso ottenere nginx per inoltrare le richieste HTTP POST tramite riscrittura?

Tutto ciò che colpisce l'IP pubblico colpirà 443 a causa della riscrittura. Internamente, stiamo inoltrando a 80 sulla macchina virtuale.

Ma quando eseguo uno script Python come quello qui sotto per testare la nostra configurazione

import requests

data = {'username': '....', 'password': '.....'}
url = 'http://IP_A/api/service/signup'

res  = requests.post(url, data=data, verify=False)
print res
print res.json
print res.status_code
print res.headers

Sto ottenendo un 405 Method Not Allowed. In nginx abbiamo scoperto che quando ha colpito il server interno, il nginx interno stava ottenendo una GETrichiesta, anche se nell'intestazione originale abbiamo fatto un POST(questo era mostrato nello script Python).

Quindi sembra che riscrivere abbia un problema. Qualche idea su come risolvere questo problema? Quando ho commentato la riscrittura, colpisce sicuramente 80, ed è andato a buon fine. Dal momento che riscrivere è stato in grado di parlare con il nostro server interno, quindi riscrivere se stesso non ha alcun problema. È solo la riscrittura rilasciata POSTa GET.

Grazie!

(Questo verrà anche chiesto sul forum Nginx perché si tratta di un blocco critico ...)

Risposte:


8

Non è Nginx, è il tuo browser.

Nota da RFC2616:

RFC 1945 e RFC 2068 specificano che al client non è consentito modificare il metodo sulla richiesta reindirizzata. Tuttavia, la maggior parte delle implementazioni degli user agent esistenti trattano 302 come se si trattasse di una risposta 303, eseguendo un GET sulla posizione [..]

Questo è vero per tutti i browser più diffusi e non c'è nulla che tu possa fare al riguardo.


@ c2h50h Capisco che le specifiche HTTP hanno dichiarato qualcosa di simile. Ma cosa posso fare in Nginx? Voglio dire questo è una messa a punto banale in cui la gente avanti 443 a una porta interna 80, ma possono ancora fare PUT, POST, DELETE, GET. Nella mia configurazione precedente non avevo questo proxy extra nella parte anteriore che serviva la folla. Ho avuto la stessa configurazione sullo stesso server interno (il nostro server di prova). Funziona benissimo.
CppLearner,

Niente. È lato client al 100%. Se un server Web, qualsiasi server Web, restituisce un reindirizzamento 301 o 302, il browser, sul lato client, sostituirà qualsiasi tipo di richiesta GET. Nessuna configurazione sul lato server o eventuali intestazioni http restituite non cambierà questo. È così per ragioni storiche (i primi browser si sono comportati in questo modo a causa di incomprensioni ed è diventato uno standard di fatto).
c2h5oh,

Beh, per uno non è proprio un browser. Bene, puoi dire che è un browser perché usa il protocollo HTTP ... va bene ... va bene. Ma poi di nuovo, sembra che ciò accada solo se stavo facendo questa configurazione a due livelli. Se dovessi mettere la stessa configurazione direttamente sulla macchina interna, ed eseguendo il test lì, non mi lamento. Ma poi di nuovo, come lo fanno le persone nella loro produzione? Suppongo che alcune persone stiano facendo qualcosa di simile, invertendo 443 con alcune VM che potrebbero essere in esecuzione solo 80. Se esiste una pratica migliore, mi piacerebbe impararla e sentirne parlare.
CppLearner,

1
Per browser intendevo client HTTP e con tutti i client popolari POSTdiventerà GETse è reindirizzato 301 o 302. Il POST rimarrà POST al reindirizzamento del proxy, ma non alla riscrittura.
c2h5oh,

1
RFC2616 di nuovo: If the 307 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.così la maggior parte dei browser visualizzerà un messaggio di avviso, poiché per gli altri client HTTP non riesco nemmeno a indovinare quale sarà il loro comportamento.
c2h5oh,

1

Ho scoperto che POST /api/brandsi stava trasformando GET /api/brandperché l'applicazione Web che stavo usando ( flask-restful) stava facendo una richiesta "non valida". Se ho usato POST /api/brand/(notare il finale /), ha avuto successo.


Stavo usando Postman per testare il login django rest-auth e stavo vedendo lo stesso problema descritto. La chiave era che avevo trascurato il "/" finale nella richiesta POST.
Steve L
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.