「HAProxy」- 在TCP模式下,从Haproxy向Nginx传递客户端IP地址

  CREATE BY JENKINSBOT
原文链接:「HAProxy」- 在TCP模式下,从Haproxy向Nginx传递客户端IP地址
文章分类:「Web_Service_Stack:Nginx_and_Tengine:x.Miscellanies:rewrite_the_IP_address_of_HAProxy」
文章标识:「aed34a0e」

典型场景

客户端Client访问对外开放的HAProxy服务,然后由HAProxy负责负载均衡,来向后端的Nginx服务转发流量。如图:

存在的问题

从Nginx获取到的IP地址为HAProxy的IP地址,而不是客户端Client的IP地址。

需要解决该问题。

系统环境

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-proxysend-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通过。

参考文献