nginx + Gunicorn 部署 Flask 应用,获取不了 real ip,是哪里的坑?

2015-11-15 22:01:45 +08:00
 rogwan

用 nginx+Gunicorn 部署 Flask 应用,调整了 nginx 的配置很多次,重新编译安装 nginx ,也添加了 ngx_http_realip_module ,但是在 Flask 应用里获取到用户 ip 始终是 127.0.0.1.
* 如果前端去掉 nginx+Gunicorn, 直接跑 Flask 自带的 wsgi ,是可以成功获取用户的访问 ip 。

nginx 配置如下:

/usr/local/nginx/sbin/nginx -V

nginx version: nginx/1.8.0
built by gcc 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
configure arguments: --prefix=/usr/local/nginx --with-pcre=/usr/src/pcre-8.36 --with-http_realip_module

nginx.conf

server {
listen 80;
server_name mydomain.com;

location / {
    proxy_pass http://127.0.0.1:8000;
    # set_real_ip_from 127.0.0.1;
    # real_ip_header X-Forwarded-For;
    # real_ip_recursive on;
    proxy_redirect     off;
    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-Host $server_name;
}

}

4660 次点击
所在节点    Python
11 条回复
humiaozuzu
2015-11-15 22:07:03 +08:00
werkzeug 获取 ip 的默认是你 nginx 的 ip 了,也就是说不是真实的 ip ,下面的代码可以解决问题

from werkzeug.contrib.fixers import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)
lins05
2015-11-15 22:43:39 +08:00
改成 `proxy_set_header X-Forwarded-For $remote_addr` 试试
zhuangzhuang1988
2015-11-15 22:53:04 +08:00
先直接 在 flask 中 把传入的 env 都打印出来先..
rogwan
2015-11-16 09:12:45 +08:00
@lins05 最开始就是这样配置的,无效;改为 X-Real-IP $remote_addr 也不行。
@humiaozuzu 还是取的本地 ip...
先让 Gunicorn 裸跑着吧,这样直接获取用户的 ip 是 OK 的(把前端的 nginx 去掉了),还不清楚 nginx 和 gunicorn 的配合哪儿出问题了
julyclyde
2015-11-16 12:30:41 +08:00
nginx 配置如下只是 nginx 负责任的传递了这个信息在 header 里
至于是否读那个 header ,那是 python 这边的事
xiangace
2015-11-16 13:34:54 +08:00
headers.environ.get("HTTP_X_Forwarded_For", headers.get('HTTP_X_REAL_IP', request.remote_addr))
rogwan
2015-11-16 19:24:31 +08:00
@xiangace 兄弟,你试过这样能生效吗?怎么我用你说的方法,不行啊...
我原来的写法是:
from flask import request
def get_real_ip():
ip = request.remote_addr
这样直接用 Flask 自带的 wsgi 跑,或用 Gunicorn 跑 mydomain.com:80 端口,都是可以成功获取用户真实 ip 的,但 Gunicorn 前面再套一层 nginx 就不行的。
xiangace
2015-11-17 10:25:23 +08:00
@rogwan
抱歉, nginx 还需要加上:
proxy_pass_header Server;
julyclyde
2015-11-17 14:37:46 +08:00
@rogwan 你先搞明白 X-forwarded-for 的原理再说吧。你现在这个认知层次,给你说这些你也听不懂
rogwan
2015-11-18 07:33:17 +08:00
@julyclyde 看了一下 Gunicorn 的核心开发者 Starefossen 今年 5 月做了这样一个说明:
#633 removed functionality which made Gunicron update REMOTE_ADDR to what a trusted upstream client sent in the X-Forwared-For header. This was a violation of RFC 3875 CGI Version 1.1 which states the following:

4.1.8. REMOTE_ADDR

The REMOTE_ADDR variable MUST be set to the network address of the
client sending the request to the server.

Because of this change, if your Gunicorn server is behind any proxy you will only get the proxy's IP address as the REMOTE_ADDR.

I think there should be some sort of mention of this pitfall in the documentation along with a suggested good workaround (if there exists any). I'll see if I get the time to submit a PR at the end of this weekend.

===========
应用程序是不能通过 Gunicorn (版本阶段应该是 Guniconr19.0+吧)来取 REMOTE_ADDR 了(这个值是空的),而 X-forwarded_for 的 client ip 值容易被修改和伪造(实际测试中发现,通过微信内置浏览器访问的话,这个值会取不到)。
julyclyde
2015-11-18 10:54:31 +08:00
@rogwan 是这样的。但如果你两个都不信,就没有其它渠道取到这个值了

另外你的微信浏览器实际测试对此事没有任何意义

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/236393

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX