78 votes

nginx : Enregistrer la demande/réponse complète avec tous les en-têtes ?

Nous avons un serveur d'applications qui se bloque parfois. Nous pensons que cela est dû à une mauvaise requête d'un client.

Nginx peut-il enregistrer la requête/réponse complète (comme les captures de fiddler) dans des fichiers, afin que nous puissions voir les requêtes qui ont été envoyées avant le blocage ?

(Nous devons probablement éviter pcap et cette approche et faire tout cela dans nginx)

Si nginx n'est pas le bon outil pour cela, qu'est-ce qui pourrait l'être (à part un analyseur de réseau) ?

2 votes

mitmproxy en mode proxy inverse devrait faire ce que vous cherchez.

2 votes

@VivekThomas c'est une question de nginx.... nous utilisons déjà nginx et n'allons pas changer.

3 votes

@samsmith Vieille question, mais peut-être que cela aidera quelqu'un d'autre : vous ne doivent abandonner nginx. Selon les circonstances, vous pouvez simplement réacheminer temporairement nginx vers un autre port, afin de permettre à mitmproxy d'intercepter le trafic et de prendre en charge le débogage. Ensuite, une fois le débogage terminé, vous pouvez simplement rediriger nginx vers le port d'origine et arrêter mitmproxy.

65voto

berto Points 221

Pour obtenir le corps de la requête envoyé par les visiteurs, utilisez client_body_in_file_only on; et enregistrer le fichier "temporaire" dans lequel il a été écrit dans les journaux en ajoutant la variable $request_body_file au format du journal. Les fichiers "temporaires" seront situés dans le répertoire client_temp par défaut.

Vous pouvez enregistrer les en-têtes de requête $http_<header> aussi et a envoyé des en-têtes avec $sent_http_<header> .

Si vous avez le corps de la requête et les en-têtes, vous devriez pouvoir la rejouer et obtenir la réponse de votre visiteur.

Aussi quelque chose comme gor doit être envisagé afin de pouvoir rejouer le trafic sur un autre environnement où vous pouvez laisser nginx écrire ces fichiers temporaires sans causer de problèmes d'entrées-sorties en production (nginx ne les purgera pas avec la fonction on c'est pourquoi il n'est pas si "temporaire" dans ce cas).

0 votes

Xavier, Merci ! Les travaux ! Maintenant que j'ai les fichiers, comment les rejouer ?

0 votes

NVM RE Replay. curl fonctionne très bien !

0 votes

Cela ne fonctionne pas pour moi pour les en-têtes (nginx 1.8.0). J'obtiens le message corps d'une requête mais pas les en-têtes. Les fichiers ne sont enregistrés que pour les requêtes POST (c'est-à-dire les requêtes dont le corps contient quelque chose).

23voto

Colin Kershaw Points 155

mitmproxy semble être le bon outil pour faire ce que vous demandez.

mitmproxy est un proxy man-in-the-middle interactif, compatible SSL pour HTTP avec une interface console.

mitmdump est la version en ligne de commande de mitmproxy. Pensez à tcpdump pour HTTP.

Caractéristiques

  • Intercepter les demandes et les réponses HTTP et les modifier à la volée.
  • Enregistrez des conversations HTTP complètes pour les rejouer et les analyser ultérieurement.
  • Reproduire le côté client d'une conversation HTTP. Rejouer les réponses HTTP d'un serveur précédemment enregistré.
  • Mode proxy inversé pour transférer le trafic vers un serveur spécifié.
  • Mode proxy transparent sur OSX et Linux.
  • Apporter des modifications scripturales au trafic HTTP en utilisant Python.
  • Les certificats SSL pour l'interception sont générés à la volée.

Le mode proxy inverse vous permet de capturer la demande et la réponse comme le fait Fiddler.

0 votes

Dans le contexte Linux, socat vaut également la peine d'être explorée pour le MITM.

2voto

Sérgio Carvalho Points 604

Je cherchais une réponse à cette question, et j'ai trouvé ceci : https://tarunlalwani.com/post/request-capturing-nginx-lua/

En gros, il utilise un script lua script pour rassembler et enregistrer tous les en-têtes :

ngx.log(ngx.ERR, "REQUEST capturing started")
json = require("json")

function getval(v, def)
  if v == nil then
     return def
  end
  return v
end

local data = {request={}, response={}}

local req = data["request"]
local resp = data["response"]
req["host"] = ngx.var.host
req["uri"] = ngx.var.uri
req["headers"] = ngx.req.get_headers()
req["time"] = ngx.req.start_time()
req["method"] = ngx.req.get_method()
req["get_args"] = ngx.req.get_uri_args()

req["post_args"] = ngx.req.get_post_args()
req["body"] = ngx.var.request_body

content_type = getval(ngx.var.CONTENT_TYPE, "")

resp["headers"] = ngx.resp.get_headers()
resp["status"] = ngx.status
resp["duration"] = ngx.var.upstream_response_time
resp["time"] = ngx.now()
resp["body"] = ngx.var.response_body

ngx.log(ngx.CRIT, json.encode(data));

Puis, faites-y référence à partir de la configuration de nginx :

location / {
    proxy_pass http://127.0.0.1:8081/;
    log_by_lua_file lua/request_logger.lua;
}

Pour recueillir le corps, un bloc est nécessaire dans nginx main.conf :

#we must declare variables first, we cannot create vars in lua
set $response_body '';

body_filter_by_lua_block {
    -- arg[1] contains a chunk of response content
    local resp_body = string.sub(ngx.arg[1], 1, 1000)
    ngx.ctx.buffered = string.sub((ngx.ctx.buffered or "") .. resp_body, 1, 1000)
    -- arg[2] is true if this is the last chunk
    if ngx.arg[2] then
      ngx.var.response_body = ngx.ctx.buffered
    end
}

Le post dont le lien figure ci-dessus détaille beaucoup mieux cette question.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X