V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
rogwan
V2EX  ›  Python

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

  •  
  •   rogwan · 2015-11-15 22:01:45 +08:00 · 4434 次点击
    这是一个创建于 3084 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    }

    11 条回复    2015-11-18 10:54:31 +08:00
    humiaozuzu
        1
    humiaozuzu  
       2015-11-15 22:07:03 +08:00   ❤️ 2
    werkzeug 获取 ip 的默认是你 nginx 的 ip 了,也就是说不是真实的 ip ,下面的代码可以解决问题

    from werkzeug.contrib.fixers import ProxyFix
    app.wsgi_app = ProxyFix(app.wsgi_app)
    lins05
        2
    lins05  
       2015-11-15 22:43:39 +08:00
    改成 `proxy_set_header X-Forwarded-For $remote_addr` 试试
    zhuangzhuang1988
        3
    zhuangzhuang1988  
       2015-11-15 22:53:04 +08:00
    先直接 在 flask 中 把传入的 env 都打印出来先..
    rogwan
        4
    rogwan  
    OP
       2015-11-16 09:12:45 +08:00
    @lins05 最开始就是这样配置的,无效;改为 X-Real-IP $remote_addr 也不行。
    @humiaozuzu 还是取的本地 ip...
    先让 Gunicorn 裸跑着吧,这样直接获取用户的 ip 是 OK 的(把前端的 nginx 去掉了),还不清楚 nginx 和 gunicorn 的配合哪儿出问题了
    julyclyde
        5
    julyclyde  
       2015-11-16 12:30:41 +08:00
    nginx 配置如下只是 nginx 负责任的传递了这个信息在 header 里
    至于是否读那个 header ,那是 python 这边的事
    xiangace
        6
    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
        7
    rogwan  
    OP
       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
        8
    xiangace  
       2015-11-17 10:25:23 +08:00
    @rogwan
    抱歉, nginx 还需要加上:
    proxy_pass_header Server;
    julyclyde
        9
    julyclyde  
       2015-11-17 14:37:46 +08:00
    @rogwan 你先搞明白 X-forwarded-for 的原理再说吧。你现在这个认知层次,给你说这些你也听不懂
    rogwan
        10
    rogwan  
    OP
       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
        11
    julyclyde  
       2015-11-18 10:54:31 +08:00
    @rogwan 是这样的。但如果你两个都不信,就没有其它渠道取到这个值了

    另外你的微信浏览器实际测试对此事没有任何意义
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3793 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 42ms · UTC 00:52 · PVG 08:52 · LAX 17:52 · JFK 20:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.