文章分类:「Web_Application_Server,_LB_and_HA:Nginx,_Tengine,_OpenResty:3.Problem_Solving_(how-to):Real_IP_Address:rewrite_the_IP_address_of_HAProxy」
文章标识:「d4ceed25」
问题描述
客户端 Client 访问对外开放的 HAProxy 服务,然后由 HAProxy 负责负载均衡,来向后端的 Nginx 服务转发流量。如图:
但是,从 Nginx 获取到的 IP 地址为 HAProxy 的IP地址,而不是客户端 Client 的真实 IP 地址。
该笔记将记录:在 HAProxy + Nginx 中,如何获取客户端的真实网络地址(Real IP Address)
系统环境
CentOS 7.0,三台机器:172.31.255.112(Clint);172.31.255.13(HAProxy);172.31.255.125(Nginx-00);
Nginx 1.14.0
HAProxy 1.5.18,在TCP模式下的负载均衡。当然也可以使用HTTP模式,但是效率低啊,我们要用TCP模式。(“使用HTTP效率低”完全是基于“TCP是四层的,而HTTP是七层的”进行的推断)
解决方案
官方已经给出解决方案,参考 Accepting the PROXY Protocol 页面。下面会简单的解释该过程,并标记出需要注意的地方。
在HAProxy的TCP模式下,如果要向Nginx发送客户端的IP地址,它们之间要使用「PROXY协议」。使用PROXY协议后,NGINX才可以从HTTP,SSL,HTTP/2,SPDY,WebSocket,TCP中得知原始IP地址。可以这么理解PROXY协议:HAProxy修改了TCP报文的信息,追加了客户端的IP地址等信息,然后在转发到Nginx。使用PROXY协议没有创建新的连接。
注意,这里讨论的是HTTP协议。对于HTTPS协议,在配置上会有少许的不同,后面会有所提及,而官方文章「Accepting the PROXY Protocol」中也有说明。
下面先介绍HAProxy的注意事项,然后再介绍Nginx的注意事项。
修改HAProxy配置
为了演示问题,这里的HAProxy简单配置。
因为要使用PROXY协议,需要在server中追加send-proxy配置:
listen nginx mode tcp bind *:80 balance roundrobin server nginx-01 172.31.255.125:80 send-proxy weight 1 maxconn 10000 check inter 10s
注意,「PROXY协议」由两个版本,对应的HAProxy中的配置为send-proxy与send-proxy-v2指令,这是用于HTTP协议的。如果是HTTPS协议要使用send-proxy-v2-ssl或者send-proxy-v2-ssl-cn指令,具体的含义参照「官方文档」中的说明。
通过「PROXY协议」传递给Nginx的信息是客户端IP地址、代理服务器IP地址、两个端口号。接下来就是配置Nginx服务接受PROXY协议。
修改Nginx配置
目前,HAProxy已经将客户端的IP地址通过PROXY协议发送给Nginx服务了。接下来就是在Nginx中获取客户端的IP地址,有两种办法:
1. 在Nginx中,使用''$proxy_protocol_addr''和''$proxy_protocol_port''变量捕获原始客户端IP地址和端口。''$remote_addr''和''$remote_port''变量捕获HAProxy的IP地址和端口。 2. 在Nginx中,启用RealIP模块。启用RealIP模块后:Nginx会重写''$remote_addr''和''$remote_port''变量的值,用客户端IP地址和端口替换负载均衡的IP地址和端口;而''$realip_remote_addr''和''$realip_remote_port''变量保留负载均衡的IP地址和端口;''$proxy_protocol_addr''和''$proxy_protocol_port''变量保留原始客户端IP地址和端口。
这对Nginx也有所要求:
* 接受PROXY协议v2,需要NGINX 1.13.11及更高版本,或NGINX Plus R16及更高版本; * 接受HTTP的PROXY协议,需要NGINX 1.5.12及更高版本,或NGINX Plus R3及更高版本;(在nginx-1.5.12的http中,指令listen首次支持proxy_protocol) * 接受TCP的PROXY协议,需要NGINX 1.11.4及更高版本,或NGINX Plus R11及更高版本;(在nginx-1.11.4的stream中,指令listen首次支持proxy_protocol) * 对于TCP客户端PROXY协议支持,需要NGINX 1.9.3及更高版本,或NGINX Plus R7及更高版本;(在nginx-1.9.2中,加入了proxy_protocol指令,用于请求PROXY服务) * 如果使用RealIP模块,则需要进行安装,默认没有安装该模块。
在Nginx中进行如下配置:
http { #... server { set_real_ip_from 192.168.1.0/24; real_ip_header proxy_protocol; listen 80 proxy_protocol; #... } }
有一点需要说明,指令set_real_ip_from的参数为负载均衡的IP地址或网段。该参数标记出负载均衡的网段,然后Nginx会进行递归排除,递归排除之后剩下的IP地址就是客户端的IP地址。这个地方不要配置错了。
配置测试
如果配置正确,并且使用了RealIP模块,如果后端是PHP,那$_SERVER[‘REMOTE_ADDR’]的地址为客户端的地址。
杂记
haproxy-1.5.18 + nginx-1.14.0 => 测试 HTTP 通过
haproxy-1.5.18 + tengine-2.1.2(nginx 1.6.2) => 测试 HTTP 通过
参考文献
- forward client’s IP address to Nginx from Haproxy in tcp mode
- Using Proxy Protocol with Nginx
- Haproxy documentation
- Send PROXY protocol header from HAProxy
- How to setup HAProxy as Load Balancer for Nginx on CentOS 7