La réécriture https Nginx transforme POST en GET

17

Mon serveur proxy fonctionne sur ip A et c'est ainsi que les gens accèdent à mon service Web. La configuration nginx redirigera vers une machine virtuelle sur ip B.

Pour le serveur proxy sur IP A, je l'ai dans mes sites disponibles

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

Le a proxy_redirectété tiré de comment puis-je obtenir nginx pour transférer les demandes HTTP POST via la réécriture?

Tout ce qui frappe l'IP publique atteindra 443 en raison de la réécriture. En interne, nous transmettons à 80 sur la machine virtuelle.

Mais quand je lance un script python tel que celui ci-dessous pour tester notre configuration

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

Je reçois un 405 Method Not Allowed. Dans nginx, nous avons constaté que lorsqu'il atteignait le serveur interne, le nginx interne recevait une GETdemande, même si dans l'en-tête d'origine, nous avons fait une POST(cela a été montré dans le script Python).

Il semble donc que la réécriture ait un problème. Une idée de comment résoudre ce problème? Quand j'ai commenté la réécriture, elle atteint 80 à coup sûr, et elle est passée. Puisque la réécriture a pu parler à notre serveur interne, la réécriture elle-même n'a donc aucun problème. Il est juste la réécriture a chuté POSTà GET.

Je vous remercie!

(Cela sera également demandé sur le forum Nginx car il s'agit d'un bloqueur critique ...)

CppLearner
la source

Réponses:

8

Ce n'est pas Nginx, c'est votre navigateur.

Note de RFC2616:

Les RFC 1945 et RFC 2068 spécifient que le client n'est pas autorisé à modifier la méthode sur la demande redirigée. Cependant, la plupart des implémentations d'agent utilisateur existantes traitent 302 comme s'il s'agissait d'une réponse 303, effectuant un GET sur l'emplacement [..]

Cela est vrai pour tous les navigateurs populaires et vous ne pouvez rien y faire.

c2h5oh
la source
@ c2h50h Je comprends que la spécification HTTP a déclaré quelque chose de similaire comme ça. Mais que puis-je faire dans Nginx? Je veux dire est une configuration trivial où les gens vers l' avant 443 à un port 80 interne, mais ils peuvent le faire encore PUT, POST, DELETE, GET. Dans ma configuration précédente, je n'avais pas ce proxy supplémentaire à l'avant pour servir la foule. J'avais la même configuration sur le même serveur interne (notre serveur de test). Ça marche bien.
CppLearner
Rien. C'est 100% côté client. Si un serveur Web, tout serveur Web, renvoie une redirection 301 ou 302, le navigateur, côté client, remplacera tout type de demande GET. Aucune configuration côté serveur ou aucun en-tête http retourné ne changera cela. C'est comme ça pour des raisons historiques (les premiers navigateurs se sont comportés de cette façon en raison d'un malentendu et c'est devenu une norme de facto).
c2h5oh
Eh bien, ce n'est pas vraiment un navigateur. Eh bien, vous pouvez dire que c'est un navigateur car il utilise le protocole HTTP .. ok .. c'est très bien. Mais là encore, il semble que cela ne se produise que si je faisais cette configuration à deux couches. Si je devais mettre la même configuration directement sur la machine interne, et y exécuter le test, cela ne se plaindra pas. Mais là encore, comment les gens le font-ils dans leur production? Je suppose que certaines personnes font la même chose, inverser 443 sur une machine virtuelle qui exécute 80 uniquement. S'il existe une meilleure pratique, j'aimerais l'apprendre et en entendre parler.
CppLearner
1
Par navigateur, je voulais dire client HTTP et avec tous les clients populaires POSTle deviendra GETs'il est redirigé 301 ou 302. POST restera POST sur la redirection du proxy, mais pas sur la réécriture.
c2h5oh
1
RFC2616 à nouveau: la 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.plupart des navigateurs afficheront donc un message d'avertissement, comme pour les autres clients HTTP, je ne peux même pas deviner quel sera leur comportement.
c2h5oh
1

J'ai découvert qu'il POST /api/brandétait en train d'être transformé GET /api/brandcar l'application Web que j'utilisais ( flask-restful) faisait une demande «non valide». Si j'ai utilisé POST /api/brand/(remarquez la fin /), cela a réussi.

gaozhidf
la source
J'utilisais Postman pour tester la connexion django rest-auth et je voyais le même problème décrit. La clé était que j'avais négligé le «/» de fin dans la demande POST.
Steve L