如何从 haproxy docker 容器内的请求中获取客户端 IP?

How to get client IP from request inside haproxy docker container?

我正在尝试从我的 nodejs 服务器中的请求 objects 中获取客户端的 IP 地址。

我的技术架构是: 我 运行 两个 docker 容器。一个用于 haproxy,另一个用于 nodejs,它使用 expressjs 框架。所有传入流量首先由我用于代理和负载平衡的 haproxy 接收。 Haproxy根据配置文件中的ACLs将请求转发到合适的后端。

我尝试在我的 nodejs 中访问 x-forwarded-for 请求 header,但它只返回 docker 网络网关接口 172.17.0.1.

的 IP

转到 haproxy 配置并在 defaults 块中使用 option forwardfor header X-Client-IP 还将 x-client-ip header 设置为 docker 网络网关接口ip。调试日志也记录了相同的 ip。

这就是问题所在。由于 haproxy 在容器内 运行ning 它认为 docker 网络网关接口是客户端。

如何将实际客户端的 IP 获取到容器内的 haproxy,以便它可以将其转发给 nodejs?

这是我的 haproxy 配置文件:

global
    debug
    maxconn 4096

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
    timeout http-keep-alive 50000ms
    option http-keep-alive
    option http-server-close
    option forwardfor header X-Client-IP

frontend http-in
    bind *:80
    acl is_api hdr_end(host) -i api.3dphy-dev.com

    use_backend api if is_api

    default_backend default

backend default
    server s0 "${DOCKER_INTERFACE_IP}:3000"

backend api
    balance leastconn
    option httpclose
    option forwardfor
    server s1 "${DOCKER_INTERFACE_IP}:17884"

我 运行 我的 haproxy 容器使用:

docker run -d --name haproxy_1 -p 80:80 -e DOCKER_INTERFACE_IP=`ifconfig docker0 | grep -oP 'inet addr:\K\S+'` -v $(pwd)/config/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro haproxy:1.6

注意:我没有使用任何防火墙。另外,请随时对我的配置提出任何改进建议。 Keep-alive 也被证明是一个问题。

这不可能是 haproxy 的工作方式,因为当您在启动时没有连接主机时它会一直抛出错误,因为它需要完全解析地址。我已经尝试了很多解决方法(也许是可能的),但我放弃了并用 docker-compose

做了这个 运行

我 post 编写了一个 运行ning 示例,可能会在 post 之前提供帮助。

要点是 link 具有实际已经存在的主机的容器。这是由 docker linking 完成的。

docker-compose.yml

api1:
  build: .
  dockerfile: ./Dockerfile
  ports:
    - 3955
  links:
    - mongo
    - redis
  environment:
    - REDIS_HOST=redis
    - MONGO_HOST=mongo
    - IS_TEST=true
  command: "node app.js"

api2:
  build: .
  dockerfile: ./Dockerfile
  ports:
    - 3955
  links:
    - mongo
    - redis
  environment:
    - REDIS_HOST=redis
    - MONGO_HOST=mongo
    - IS_TEST=true
  command: "node app.js"

mongo:
  image: mongo
  ports:
    - "27017:27017"
  command: "--smallfiles --logpath=/dev/null"

redis:
   image: redis
   ports:
     - "6379:6379"

haproxy:
  image: haproxy:1.5
  volumes:
     - ./cluster:/usr/local/etc/haproxy/
  links:
    - "api1"
    - "api2"
  ports:
    - 80:80
    - 70:70
  expose:
    - "80"
    - "70"

haproxy.cfg

global
  log 127.0.0.1 local0
  log 127.0.0.1 local1 notice

defaults
  log global
  mode http
  option httplog
  option dontlognull
  timeout connect 5000
  timeout client 10000
  timeout server 10000

listen stats :70
  stats enable
  stats uri /

frontend balancer
  bind 0.0.0.0:80
  mode http
  default_backend aj_backends

backend aj_backends
  mode http
  balance roundrobin
  option forwardfor
  http-request set-header X-Forwarded-Port %[dst_port]
  http-request add-header X-Forwarded-Proto https if { ssl_fc }
  option httpchk HEAD / HTTP/1.1\r\nHost:localhost
  default-server inter 3s fall 5
  server api1 api1:3955
  server api2 api2:3955

通过 docker 论坛搜索后终于找到了解决方案。

解决方案分为两步。

首先,我需要将 haproxy 配置更新为:

global
    debug
    maxconn 4096

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
    timeout http-keep-alive 50000ms
    option http-keep-alive
    option http-server-close

frontend http-in
    bind *:80
    option forwardfor
    acl is_site hdr_end(host) -i surenderthakran-dev.com

    use_backend site if is_site

    default_backend default

backend default
    server s0 "${DOCKER_INTERFACE_IP}:3000"

backend site
    balance leastconn
    option httpclose
    option forwardfor
    server s1 "${DOCKER_INTERFACE_IP}:17884"

注意在 frontend http-in 块中添加了 option forwardfor。这告诉 haproxy 的前端部分将客户端 IP 添加到请求 header.

其次,docker 运行命令应该更新为:

docker run -d --name haproxy_1 -p 80:80 -e DOCKER_INTERFACE_IP=`ifconfig docker0 | grep -oP 'inet addr:\K\S+'` -v $(pwd)/config/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro --net=host haproxy:1.6

注意在 docker 运行 命令中添加了 --net=host 选项。它告诉 docker 启动新容器并使用与主机相同的网卡。

现在原始客户端 IP 添加到请求 header 并且可以在请求转发到的任何应用程序中的 x-forwarded-for 请求 header 中访问。