PROXY Protocol 允许代理或负载均衡器在 TCP 连接开始处添加一个小而标准化的头部,把原始客户端的连接信息(包括其真实 IP 地址和端口)传递给后端服务器。
为什么需要 PROXY Protocol?它解决了什么问题
标准 TCP 代理的工作方式是代表客户端与后端服务器建立一条新连接。从后端服务器的角度看,连接来自代理的 IP 地址,而不是原始客户端的地址。这种行为掩盖了客户端的真实身份,带来若干问题:
- 日志记录: 服务器访问日志记录的是代理的 IP,无法把请求追溯到具体的客户端。
- 安全: 后端服务器上的 Web 应用防火墙(WAF)、限速器和访问控制列表(ACL)无法根据真实 IP 地址准确识别并拦截恶意客户端。
- 数据分析: 地理定向、个性化内容分发和滥用检测系统会丢失关键的客户端位置与行为数据。
- 调试: 无法看到原始客户端来源时,排查网络问题或特定客户端行为会复杂得多。
PROXY Protocol 提供了一种机制,让代理把客户端的连接细节(源 IP、源端口、目标 IP、目标端口)穿过代理层传递出去,从而使后端服务器能够正确识别原始客户端。
PROXY Protocol 如何工作
PROXY Protocol 工作在 OSI 模型的传输层(第 4 层)。当支持 PROXY Protocol 的代理或负载均衡器收到客户端连接时,它会与后端服务器建立一条新连接。在发送任何应用层数据(例如 HTTP 请求、数据库查询)之前,代理会先发送一个 PROXY Protocol 头部,其中包含客户端的连接信息。
要让 PROXY Protocol 正常工作,必须满足两个条件:
1. 代理或负载均衡器必须配置为发送 PROXY Protocol 头部。
2. 后端服务器必须配置为在处理任何应用层数据之前接收并解析 PROXY Protocol 头部。如果后端服务器不理解该头部,就会把它当作格式错误的应用数据,从而导致连接错误。
PROXY Protocol 版本
PROXY Protocol 主要有两个版本:v1 和 v2。
版本 1(v1)
PROXY Protocol v1 采用基于 ASCII、人类可读的格式,支持 TCP 上的 IPv4 和 IPv6。
格式:
PROXY <INET_PROTOCOL> <CLIENT_IP> <PROXY_IP> <CLIENT_PORT> <PROXY_PORT>\r\n
<INET_PROTOCOL>:IPv4 为TCP4,IPv6 为TCP6。<CLIENT_IP>:原始客户端的 IP 地址。<PROXY_IP>:代理的 IP 地址(后端看到的连接来源 IP)。<CLIENT_PORT>:原始客户端的源端口。<PROXY_PORT>:客户端所连接的代理上的目标端口。
示例(IPv4):
PROXY TCP4 192.168.1.1 10.0.0.1 52345 80\r\n
该头部表示一条来自客户端 192.168.1.1:52345 到代理的 TCPv4 连接,代理随后连接到后端 10.0.0.1:80。
v1 的局限:
* 仅限 TCP 连接。
* ASCII 开销使其效率略低。
* 不支持附加元数据。
版本 2(v2)
PROXY Protocol v2 是为效率和可扩展性设计的二进制格式。它支持 TCP 和 UDP 上的 IPv4、IPv6 以及 UNIX 套接字,并包含用于传输额外连接元数据的 Type-Length-Value(TLV)字段机制。
v2 的优势:
* 高效: 二进制格式减小了头部体积。
* 更广的协议支持: 支持 TCP、UDP 和 UNIX 套接字。
* IPv6 支持: 完整集成 IPv6。
* 可扩展性(TLV): 允许传递任意元数据,例如 SSL 连接信息、唯一连接 ID 或其他应用特定数据。
结构概览(简化):
v2 头部以 13 字节签名开始,随后是 1 字节的版本/命令字段、1 字节的协议字段、2 字节的地址长度字段,然后是可变长度的地址信息和可选的 TLV 字段。
原始二进制格式虽然复杂,但关键在于它能够高效地传输更多信息,并覆盖更广泛的协议。
PROXY Protocol 与 X-Forwarded-For(XFF)对比
PROXY Protocol 和 X-Forwarded-For(XFF)HTTP 头部都旨在把客户端 IP 信息穿过代理传递出去。但二者工作在不同的层,特性也不同。
| 特性 | PROXY Protocol | X-Forwarded-For(XFF)头部 |
|---|---|---|
| 工作层级 | 传输层(L4) | 应用层(L7,特指 HTTP) |
| 协议范围 | 任何基于 TCP/UDP 的协议(HTTP、FTP、SSH、SMTP 等) | 仅 HTTP/HTTPS |
| 机制 | 添加在原始 TCP/UDP 流前面的头部 | HTTP 请求头 |
| 客户端可控性 | 在第一个受信任代理之后无法被恶意客户端伪造 | 若第一个受信任代理处理不当,可被恶意客户端伪造 |
| 信息量 | 客户端 IP、客户端端口、代理 IP、代理端口(v2:TLV) | 客户端 IP(以及可能的前置代理 IP) |
| 开销 | 极小,固定长度(v1)或小体积二进制(v2) | 在 HTTP 头中增加一小段字符串 |
| 适用场景 | 任何需要真实客户端 IP 的 TCP/UDP 服务 | 需要真实客户端 IP 的 HTTP/HTTPS 服务 |
对于传递客户端 IP 信息,PROXY Protocol 提供了更稳健、更通用的方案,尤其适用于非 HTTP 服务,或需要在网络层强力防止客户端 IP 伪造的场景。
实施 PROXY Protocol
实施 PROXY Protocol 需要在发送端的代理/负载均衡器和接收端的后端服务器两侧都进行配置。
代理/负载均衡器配置(发送 PROXY Protocol)
HAProxy:
HAProxy 是常见的负载均衡选择,完整支持 PROXY Protocol。
frontend http_in
bind *:80
mode tcp
default_backend web_servers
backend web_servers
mode tcp
server s1 192.168.1.10:80 send-proxy-v2 # 发送 PROXY Protocol v2
server s2 192.168.1.11:80 send-proxy # 发送 PROXY Protocol v1
AWS Network Load Balancer(NLB):
为 NLB 配置 Target Group 时,可以启用 PROXY Protocol v2 支持。该设置作用于整个目标组,组内所有目标都必须配置为接受 PROXY Protocol。
Cloudflare Spectrum:
对于经由 Cloudflare Spectrum 代理的服务,可以启用 PROXY Protocol 支持。Cloudflare 会向您的源站服务器发送 PROXY Protocol v2 头部。
Nginx(Stream 模块——作为代理):
Nginx 的 ngx_stream_proxy_module 可以配置为发送 PROXY Protocol,主要在商业版本或特定构建中可用。
stream {
upstream backend_servers {
server 192.168.1.10:80;
server 192.168.1.11:80;
}
server {
listen 12345;
proxy_pass backend_servers;
proxy_protocol on; # Nginx 发送 PROXY Protocol v1
}
}
后端服务器配置(接收并解析 PROXY Protocol)
Nginx(作为后端 Web 服务器):
Nginx 可以配置为监听并解析 PROXY Protocol 头部。
http {
server {
listen 80 proxy_protocol; # 在 80 端口监听 PROXY Protocol v1/v2
listen 443 ssl proxy_protocol;
set_real_ip_from 10.0.0.0/8; # 信任来自您代理网络的 IP
real_ip_header proxy_protocol; # 使用 PROXY Protocol 头部中的 IP
location / {
root /var/www/html;
index index.html;
# 客户端的真实 IP 现在可通过 $remote_addr 获取
# $proxy_protocol_addr 可用于日志记录或特定逻辑
log_format custom_log '$proxy_protocol_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log custom_log;
}
}
}
在 Nginx 中,$remote_addr 会被设为代理的 IP,而 $proxy_protocol_addr 和 $proxy_protocol_port 变量则包含客户端的真实 IP 和端口。set_real_ip_from 与 real_ip_header proxy_protocol 指令可让 Nginx 正确地把客户端的真实 IP 填入 $remote_addr。
Apache HTTP Server:
Apache 可以使用 mod_remoteip 模块解析 PROXY Protocol 头部。
<IfModule mod_remoteip.c>
# 启用 PROXY Protocol 解析
RemoteIPProxyProtocol On
# 定义受信任的代理
RemoteIPTrustedProxy 10.0.0.0/8
RemoteIPTrustedProxy 192.168.0.0/16
# 配置日志格式以使用真实客户端 IP
LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
</IfModule>
启用 RemoteIPProxyProtocol On 后,Apache 会期待 PROXY Protocol 头部。此时 LogFormat 中的 %a 变量就会正确反映客户端的真实 IP。
自定义应用:
对于用 Python、Node.js、Go 或 Java 等语言编写的自定义应用,应用本身需要读取传入的 TCP 流,检查是否存在 PROXY Protocol 头部,解析它,然后再按应用自身的协议继续处理。
- 检测: 应用必须先从套接字读取若干字节(例如 100–200 字节),判断它们是否匹配 PROXY Protocol v1 签名(
PROXY)或 v2 签名。 - 解析: 如果检测到 PROXY Protocol 头部,应用解析它以提取客户端 IP、端口等信息。
- 继续: 解析完成后,应用按其原生协议处理套接字上的剩余数据。
- 无头部: 如果未找到 PROXY Protocol 头部,应用则认为连接直接来自客户端或来自不使用 PROXY Protocol 的代理,并直接处理数据。
许多网络库和框架为 PROXY Protocol 解析提供了中间件或内置支持。
使用 PROXY Protocol 的好处
- 准确的 IP 日志: 确保服务器访问日志、防火墙和监控系统记录真实的客户端 IP 地址。
- 更强的安全性: 让后端服务器上的安全工具(WAF、DDoS 缓解、限速器)能够基于客户端的真实身份执行策略。
- 更好的数据分析: 为数据分析、A/B 测试和个性化提供准确的地理与人群数据。
- 简化网络架构: 把客户端 IP 处理统一到传输层,减少对 XFF 这类应用特定头部的依赖。
- 广泛兼容: 适用于任何基于 TCP/UDP 的应用,而不只是 HTTP。
注意事项与最佳实践
- 信任边界: 只对您自己控制的受信任代理或负载均衡器启用 PROXY Protocol。如果不受信任的一方发送伪造的 PROXY Protocol 头部,您的后端服务器可能会被误导,误判客户端来源。
- 端到端兼容: 确保代理与后端服务器都正确配置为收发同一 PROXY Protocol 版本(v1 或 v2)。版本不匹配会导致连接失败。
- 性能: PROXY Protocol 头部带来的开销极小,尤其是 v2 的二进制格式。不过每条连接上的解析仍会增加一点点可忽略的处理成本。
- 监控: 监控后端服务器日志和网络流量,确认客户端 IP 被正确识别,且没有出现意外的 PROXY Protocol 错误。
