Docker compose 搭建的测试环境下,没办法拿到真实的 Client IP 吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
kenshin912
V2EX    Docker

Docker compose 搭建的测试环境下,没办法拿到真实的 Client IP 吗?

  •  
  •   kenshin912 2021-01-13 13:24:08 +08:00 3324 次点击
    这是一个创建于 1734 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有一台阿里云的服务器,我用 docker compose 起了一套测试环境,需要获取用户真实 IP,Google 了挺久一直没解决,所以跑 V2EX 来问问大佬们。

    Web Server 是 Openresty,后端是 PHP

    docker-compose 文件部分如下:

    version: '3.8' services: openresty: image: ${OPENRESTY_IMAGE} container_name: openresty build: context: ./service/openresty ports: - "80:80" - "443:443" volumes: - ${SOURCE_DIR}:/home/wwwroot/:rw - ${OPENRESTY_SSL_DIR}:/etc/nginx/ssl:rw - ${OPENRESTY_CONF_DIR}:/etc/nginx/conf.d/:rw - ${OPENRESTY_CONF_FILE}:/usr/local/openresty/nginx/conf/nginx.conf:ro - ${OPENRESTY_LUA_DIR}:/usr/local/openresty/nginx/lua/:rw - ${OPENRESTY_LOG_DIR}:/home/wwwlogs/openresty/:rw restart: always networks: - frontend depends_on: - php php: image: ${PHP_IMAGE} container_name: php build: context: ./service/php volumes: - ${SOURCE_DIR}:/home/wwwroot/:rw - ${PHP_LOG_DIR}:/var/log/php - ${DATA_DIR}/composer:/tmp/composer #- ${PHP_CONF_FILE}:/usr/local/etc/php/php.ini #- ${PHP_FPM_CONF_FILE}:/usr/local/etc/php-fpm.d/www.conf #- ${SUPERVISOR_CONF_FILE}:/etc/supervisord.conf #- ${SUPERVISOR_CONF_DIR}:/etc/supervisor/conf.d restart: always networks: - frontend - backend depends_on: - mysql 

    遇到的情况就是,不管是 Openresty 的 log , 还是程序打印出来的 $_SERVER['REMOTE_ADDR'] 都是 docker 网关的地址,拿不到真实的用户 IP,就很尴尬...

    有没有大佬能提点一下,谢谢啦

    21 条回复    2021-01-14 13:42:02 +08:00
    oott123
        1
    oott123  
       2021-01-13 13:26:34 +08:00
    你说的“docker 网关”是什么东西?
    kenshin912
        2
    kenshin912  
    OP
       2021-01-13 13:29:42 +08:00
    @oott123 #1 就是 docker inspect ${Container} 看到的 Gateway

    就这里了
    我这边看到的 Client IP 一直是这个 Gateway 的地址

    ```
    "NetworkID": "e9d63ecbe9c731c56c4279be95fe1f04501ec9adb0f7b42ffd91bfc5f157664b",
    "EndpointID": "491a05c9061f5464ec29f17bd28feda572ab3db5723f3f078a250747a8c1fe57",
    "Gateway": "172.19.0.1",
    "IPAddress": "172.19.0.3",
    "IPPrefixLen": 16,
    "IPv6Gateway": "",
    "GlobalIPv6Address": "",
    "GlobalIPv6PrefixLen": 0,
    "MacAddress": "02:42:ac:13:00:03",
    "DriverOpts": null
    ```
    SingeeKing
        3
    SingeeKing  
    PRO
       2021-01-13 13:30:23 +08:00
    如果服务很简单可以直接 --net=host

    如果不太方便,那么几乎没有办法;可以考虑在外层负载均衡 /CDN 将用户的 IP 写入 Headers 然后 openresty 配置信任这个自己写的 Headers
    kenshin912
        4
    kenshin912  
    OP
       2021-01-13 13:38:14 +08:00
    @SingeeKing #3 感谢
    --net=host 请问是加在哪里呢?
    我之前 Google 到的解决方案中有建议将 openresty 的网络修改为 host 模式,我在 openresty 的 ports 那里定义过但是似乎不起作用,如果定义为 networks_mode: host,则 openresty 无法启动,因为 openresty 的配置文件中定义了 fastcgi_pass php:9000 , openresty 不在 frontend 网络中,找不到 php:9000 ...

    网络上还有办法就是安装 traefik,然后手动追加一个 X-Real-IP 到 headers 里面,看了下挺麻烦心态也有点崩,所以只好跑 V2EX 来问问各位大佬们有没有什么办法了
    SingeeKing
        5
    SingeeKing  
    PRO
       2021-01-13 13:42:44 +08:00
    @kenshin912 #4 你这种情形就不适合配置 network host 了…… 只能是前置追加了
    kenshin912
        6
    kenshin912  
    OP
       2021-01-13 13:57:21 +08:00
    @SingeeKing #5 好吧,谢谢大佬,研究一下 traefik 的用法去...
    shynome
        7
    shynome  
       2021-01-13 15:30:37 +08:00 via Android
    #ports 写法改成下面这样就可以了
    ports:
    - { mode: host, protocol: tcp, target: 80, published: 80 }
    #要注意的是只能有一个这样的服务不然会端口冲突
    deploy: &deploy
    replicas: 1
    shynome
        8
    shynome  
       2021-01-13 15:32:20 +08:00 via Android
    deploy: &deploy
    replicas: 1
    #可能要加上下面这个才行
    endpoint_mode: dnsrr
    also24
        9
    also24  
       2021-01-13 15:36:54 +08:00
    简单点直接套娃吧,外面套个 proxy_protocol

    https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/
    Symo
        10
    Symo  
       2021-01-13 15:38:33 +08:00
    X-Real-IP 还是个比较常用的办法, 阿里云自己的 SLB 就是用这个 header 来识别 client ip 的, 不然 vps 拿到的全是 slb 的内网 ip
    kenshin912
        11
    kenshin912  
    OP
       2021-01-13 16:17:37 +08:00 via iPhone
    @shynome 好的,谢谢大佬我去试试看
    kenshin912
        12
    kenshin912  
    OP
       2021-01-13 16:18:56 +08:00 via iPhone
    @shynome 我没有启动 Swarm,直接 docker compose up -d 启动的,也需要设定副本数量嘛
    shynome
        13
    shynome  
       2021-01-13 16:46:56 +08:00 via Android
    @kenshin912 那不用设定副本数量
    kenshin912
        14
    kenshin912  
    OP
       2021-01-13 17:18:30 +08:00
    @shynome #13 大佬,我已经修改了 docker-compose.yaml

    ```
    build:
    context: ./service/openresty
    ports:
    - { mode: host, protocol: tcp, target: 80, published: 80 }
    - { mode: host, protocol: tcp, target: 443, published: 443 }
    ```

    但是没有任何效果
    endpoint_mode: dnsrr 这个我添加后是无法启动的,提示:
    Unsupported config option for services.openresty: 'endpoint_mode'
    shynome
        15
    shynome  
       2021-01-13 20:07:50 +08:00   1
    @kenshin912 干, docker-compose 起的都是可以直接拿到客户端的地址的, openresty 这里就可以拿到客户端的地址, 转发到 php 这里的话就只能拿到 openresty 的地址, 如果用的是 docker swarm 才会拿不到客户端的地址, 所以你到底用的是啥? 前面还有层代理?
    kenshin912
        16
    kenshin912  
    OP
       2021-01-13 21:19:05 +08:00
    @shynome #15 我真的用的是 docker compose 起的服务啊
    openresty 的 log 也都只能拿到 172.19.0.1 这种 Gateway 的地址,我一直很纳闷。
    之前我在 Docker Swarm 里面遇到过这种问题,不过当时没来得及解决。
    现在我只是拿 docker-compose 起了个测试环境而已,还是这种问题,我也好郁闷。

    系统环境是 Cent OS 8.2,docker 版本是 Docker version 19.03.13, build 4484c46d9d
    muzuiget
        17
    muzuiget  
       2021-01-13 22:13:25 +08:00   1
    我觉得根本不是 docker 的问题喔,一般来说 $_SERVER['REMOTE_ADDR'] 就是 TCP 层面发起端的 IP 。如果不是你想要的用户 IP,那么数据传递时中间肯定有个节点再起另一个新 TCP 链接到你的 web 服务器上,就像使用代理一样。除非代理以某种方式告知,否则你搞不到原始 IP 的。

    我看你用的是 openresty,也就是 nginx, 在折腾其它方案之前,先看看是不是有 X-Forwarded-For 这个 HTTP header,或者先打印出所有 X- 开头的 headers 看看。
    kenshin912
        18
    kenshin912  
    OP
       2021-01-13 23:02:38 +08:00
    @muzuiget #17 打印过 $_SERVER , 整个看了一圈确实是来自于 Docker 的 Gateway 。
    不过刚才 15 楼提醒了我,docker-compose 起的是可以直接拿到客户端 IP 的不应该有问题。
    我登录了一台阿里云 HK 的节点,观察了这台服务器上同样是 docker-compose 起的 openresty,log 里面的 IP 是正常的没有问题。
    结合之前我操作 firewall-cmd --zOne=public --remove-masquerade 以后,log 里面的 IP 就正常了,但是 openresty 会找不到 php:9000 来看,应该还是服务器配置或者防火墙有问题。

    所以,你开始的结论应该是正确的,不是 docker 的问题。
    只是我现在还是不知道到底哪里出了问题,只好再去一个个排查一下。
    privil
        19
    privil  
       2021-01-13 23:26:20 +08:00   1
    基于 iptables,会转发 nat 一次的,所以你拿到的地址都是 docker gateway 的
    kenshin912
        20
    kenshin912  
    OP
       2021-01-13 23:52:17 +08:00
    @privil 我关闭转发后 openresty 的 log 显示拿到了正确的 Client IP,但是访问站点却抛出了 502,看 error_log 显示 no route to host 172.19.0.2 之类的错误
    但是还是无法解决我的问题,毕竟站点挂了。
    kenshin912
        21
    kenshin912  
    OP
       2021-01-14 13:42:02 +08:00
    @shynome #15
    @muzuiget #17
    @privil #19

    感谢各位的帮助,问题已经解决。
    参考链接:
    https://stackoverflow.com/questions/47537954/how-to-make-docker-container-see-real-user-ip
    https://stackoverflow.com/a/61263768

    首先将 openresty 所在 network 对应的网卡接口调整到 internal 区域
    执行完这个命令后重启防火墙, 访问 openresty 会报 no route to host 的错误
    接着执行后三条命令即可。

    firewall-cmd --zOne=internal --change-interface=br-e9d63ecbe9c7 --permanent
    firewall-cmd --reload
    sysctl net.bridge.bridge-nf-call-iptables=0
    sysctl net.bridge.bridge-nf-call-arptables=0
    sysctl net.bridge.bridge-nf-call-ip6tables=0
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3692 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 05:06 PVG 13:06 LAX 22:06 JFK 0:06
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86