Haproxy环境获取用户IP

网络资源 admin 2年前 (2023-02-20) 569次浏览 0个评论
haproxy工作在前端用户和后端的Server之间,作为”中间人”,haproxy会建立两个连接,一是用户端与haproxy建立一个连接,另一个是haproxy与后端的server建立一个连接。
所有proxy类服务的程序都会有一个相同的问题,就是处于proxy后端的server上不能够看到用户源IP地址,而只能看到haproxy的IP地址。
要解决这个问题,可以使用tproxy的方法,可以参考我之前的blog文档
http://blog.sina.com.cn/s/blog_704836f401011e17.html
但是这个方法有个比较明显的缺点就是比较难以经过防火墙,另外后端的server的默认网关必须是haproxy。
haproxy作者Willy Tarreau开发了一个”Proxy protocol”用来向后端Server传递用户源IP地址。
目前Proxy protocol有v1和v2两个版本,v1偏重人类可读,v2是二进制格式,易于程序处理。
proxy protocol有两种角色:sender和receiver 。sender在与receiver之间每个新连接建立成功以后,都会先发送一个带有PROXY header信息的的包。如果receiver没有正确配置,不能识别这个包则会丢弃,连接不能成功建立。
Proxy protocol v1的格式如下:

PROXY关键字+空格+协议栈+源IP+目的IP+源端口+目的端口+\r\n

Bash
PROXY TCP4 198.51.100.22 203.0.113.7 35646 80\r\n
这样,下一跳主机通过接受到的这些信息就可以正确处理用户的连接,就像用户是直接连接到自己一样。

通过以上分析,我们看到可以利用这个proxy protocol传递用户的源IP地址。具体的部署结构如下图所示:
    1. client经过防火墙,发送请求到处于DMZ区中的reverse-proxy
    2. Reverse-Proxy校验请求,经过防火墙,转发请求到后端LAN上的load-balancer上
    3. Load-balancer选择一个后端服务器,将请求转发到选定的Server上
    4. 后端Server处理用户请求,然后将结果返回给load-balancer
    5. Load-balancer转发处理结果给reverse-proxy
    6. reverse-proxy转发处理结果给client
因为haproxy对于后端只要求路由可达即可,所以load-balancer可以与reverse-proxy处于不同地理位置。这样就大大扩展了灵活性。
具体的配置非常简单,简单说一下:
[ reverse-proxy ]上,使用”send-proxy”关键字
server srv1 192.168.10.1 check send-proxy
[Load-balancer]上,在bind部分使用”accept-proxy”关键字
bind 192.168.1.1:80 accept-proxy
在bind后边使用了accept-proxy以后,haproxy会当作proxy protocol中的源IP真实存在一样,可以将这个源IP用与ACL,内容过滤,日志,透明代理等等。
后端的服务器上配置默认网关为load-balancer
[Load-balancer]上,配置使用source 0.0.0.0 usesrc clientip
backend bk_app
[…]
  source 0.0.0.0 usesrc clientip
  server srv1 192.168.11.1 check
 如上图所示,经过了3层haproxy的接力传递,最后在后端的web服务器上可以接收到最初的用户src ip地址。
目前已经有多个软件支持proxy protocol,例如: nginx,percona server
 
[haproxy和nginx配合的示例]:
1.在haproxy上使用send-proxy参数
server srv1 192.168.10.1 check send-proxy
2.nginx主配置文件/etc/nginx/nginx.conf中定义新的log格式
log_format elb_log ‘$proxy_protocol_addr – $remote_user [$time_local] ‘ ‘”$request” $status $body_bytes_sent “$http_referer” ‘ ‘”$http_user_agent”‘;
 $proxy_protocol_addr这个变量就是取过来的用户真实IP,这个变量可以用在很多地方。
3.在nginx站点配置中加入以下粗体部分:
server {
    listen 80 proxy_protocol;   # 加入proxy protocol指令
    root /usr/share/nginx/www;
    index index.html index.htm;
 
    # Make site accessible from http://localhost/
    server_name localhost;
    set_real_ip_from 172.31.0.0/20;  # 使用realip module, 172.31.0.0/20是haproxy内网地址段
    real_ip_header proxy_protocol;  # 增加proxy protocol指令
 
    access_log /var/log/nginx/elb-access.log elb_log;  # 使用之前定义的elb_log格式
 
    location / {
         try_files $uri $uri/ /index.html;
     }
}
4.nginx的access log中会记录如下:
96.251.49.3 – – [20/Mar/2014:03:50:47+0000] “GET / HTTP/1.1”  200 396 “-” “Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1897.2 Safari/537.36”
其中96.251.49.3就是用户的真实IP.
set_real_ip_from 和 real_ip_header对于使用proxy protocol不是必须的,但建议这样使用。
5.对于在HTTP协议中则相对简单,只需要在haproxy中配置两个option参数。
backend http
    mode http
    option http-server-close
    option forwardfor
forwardfor这个选项配置的header是X-Forwarded-For, X-Forwarded-for经过多级代理以后会有多个IP,格式是:X-Forwarded-For: client, proxy1, proxy2。我们通常只需要第一个,不过这个可是可以伪造的。
另外在实际使用中,有时候需要自己定义这个header的名字,在haproxy中操作也很简单,在配置文件中加入如下自定义header即可。
http-request set-header X-Client-IP %[src]
主要的CDN厂家,也会自己插入一个用户源IP的主机头,例如 :
网宿: Cdn-Src-Ip –> HTTP_CDN_SRC_IP
Akamai: Ture-Client-IP –> HTTP_TURE_CLIENT_IP
Cloudflare: CF-Connecting-IP –> HTTP_CF_CONNECTING_IP
根据 RFC 3875:
Meta-variables with names beginning with “HTTP_” contain values read from the client request header fields, if the protocol used is HTTP. The HTTP header field name is converted to upper case, has all occurrences of “-” replaced with “_” and has “HTTP_” prepended to give the meta-variable name.
http header中的变量都会转换成大写,”-“会被替换成下划线,并且在前边加入”HTTP_”前缀。
============================
[haproxy和percona server配合的示例]:
percona server自5.6.25-73.0这个版本以后,添加了proxy protocol支持,具体配置方法如下:
1. haproxy中配置send-proxy参数
2. percona server在 /etc/my.cnf中:
proxy_protocol_networks = haproxy_ip
bind_address=192.168.56.2 (具体的网卡接口名称,不能0.0.0.0)
为什么需要这样配置,详见参考文档7
=============================
[NetScaler和F5 BIGIP处理PROXY protocol的示例]:
NetScaler和F5内部没有内置支持proxy protocol,因此与proxy protocol配合时会有问题,可以通过如下方式去除proxy protocol相关信息。如果愿意,可以写更复杂的规则来处理。

VPS小白 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Haproxy环境获取用户IP
喜欢 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址