这篇博文介绍了如何将 NGINX Open Source 和 NGINX Plus 配置为上游服务器流量的透明代理。 它解释了如何使用透明代理来欺骗数据包的源 IP 地址以实现 IP 透明度,以及如何为 UDP 流量实现称为直接服务器返回的负载平衡模式。
这篇文章中的信息适用于NGINX Open Source和NGINX Plus 。 为了简洁起见,我们仅参考 NGINX Plus。
编辑器——这是深入探讨 NGINX Plus R10 中新功能的系列博客文章的第五篇。
另外,请务必查看点播网络研讨会“ NGINX Plus R10 有什么新功能?”
NGINX Plus 作为第 7 层反向代理运行。 这使得 NGINX Plus 可以对其管理的网络请求应用许多优化和增强。
因此,上游(负载平衡)服务器观察到所有流量都来自 NGINX Plus 代理上的 IP 地址。 如果上游服务器需要确定真正的原始 IP 地址(远程客户端的 IP 地址),例如出于身份验证或日志记录目的,这将是一个挑战。
对于 HTTP 和 HTTPS 流量,有有效的解决方法,即使用X-Forwarded-For
HTTP 标头或PROXY 协议。 本文介绍了另外两种适用于 TCP 和 UDP 流量的方法:
方法 1 – IP 透明度 | 方法 2a – DSR(路由器 NAT) | 方法 2b – DSR(原点 NAT) | |
---|---|---|---|
支持的协议 | 基于 TCP 和基于 UDP | 仅基于 UDP | 仅基于 UDP |
通过上游服务器进行流量路由 | 所有出站流量均路由至 NGINX Plus 代理并终止 | 所有出站流量都通过中间 NGINX Plus 服务器路由 | 所有流量正常路由 |
表现 | 标准:出站流量在 NGINX Plus 代理上终止 | 更好:出站流量由中间 NGINX Plus 服务器转发 | 最佳:出口流量直接路由到远程客户端,绕过 NGINX Plus |
健康检查 | 默认为被动;支持主动 | 需要主动;无法进行被动 | 需要主动;无法进行被动 |
所需配置 | |||
NGINX Plus 代理上的 TCP 和 UDP | TCP:默认工作 UDP: 代理响应 1 |
TCP:不支持 UDP: 代理响应 0 |
TCP:不支持 UDP: 代理响应 0 |
数据包如何处理以及在哪里处理? | iptables 在 NGINX Plus 代理上终止 |
tc nat 在中间 NGINX Plus 服务器上进行重写 |
上游服务器上的tc nat 重写 |
NGINX Plus 代理上的网络配置 | iptables 捕获出口数据包 |
IP 转发和无状态 NAT | 没有任何 |
上游服务器上的网络配置 | 将 NGINX Plus 指定为默认路由 | 将 NGINX Plus 指定为默认路由 | 无状态 NAT |
部署和排除 IP 透明度和 DSR 故障非常复杂。 仅当标准反向代理操作模式不足以满足您的应用或服务需求时,才实施这些配置。
与简单转发数据包的交换机或路由器不同,NGINX Plus 作为第 7 层反向代理运行。 在这种操作模式下,NGINX Plus 管理单独的客户端和上游端 TCP 连接(用于 HTTP 和 TCP 流量)或 UDP 会话,以控制远程客户端和选定的上游服务器之间的通信。
这种标准反向代理操作模式的结果是上游服务器观察到 TCP 和 UDP 流量源自本地 NGINX Plus 代理。
第 7 层反向代理操作模式为 HTTP 和 TCP 流量带来了显著的性能提升和效率(包括 TCP 优化、缓冲和 HTTP 保活重用)。 如果上游服务器需要确定连接或会话的真实原始 IP 地址(用于身份验证和访问控制、速率限制和日志记录等目的),则会带来挑战。
对于某些协议,NGINX Plus 可以使用X-Forwarded-For
HTTP 标头或PROXY 协议向上游服务器提供原始 IP 地址。 这篇文章描述了另外两种方法,它们通过NGINX Plus Release 10 (R10)<.htmla>中引入的proxy_bind
指令的透明
参数实现。
IP 透明度的目的是隐藏反向代理的存在,以便原始服务器观察到 IP 数据包来自客户端的 IP 地址。 IP 透明度可与基于 TCP 和基于 UDP 的协议一起使用。
为了演示 IP 透明度,我们首先创建一个由四个 Web 服务器组成的负载平衡集群,这些服务器会响应一些简单的连接信息。
配置一个简单的 HTTP虚拟服务器,对一组上游服务器之间的流量进行负载平衡:
# 在 'http' 上下文中
服务器 {
listen 80;
location / {
proxy_pass http://http_upstreams;
}
}
上游 http_upstreams {
服务器 172.16.0.11;
服务器 172.16.0.12;
服务器 172.16.0.13;
服务器 172.16.0.14;
}
为了确认上游服务器观察到连接源自 NGINX Plus 负载均衡器,请在四个上游服务器(172.16.0.11 至 172.16.01.14)上分别配置一个 NGINX Plus Web 服务器,并使用一个简单的虚拟服务器返回有关连接的信息,例如:
# 在 'http' 上下文中
server {
listen 80;
location / {
return 200 "来自 $hostname 的 Hello。 您从 $remote_addr:$remote_port 连接到 $server_addr:$server_port\n";
}
}
当我们测试此配置时,上游报告连接源自本地 NGINX Plus IP 地址(172.16.0.1):
$ for i in {1..4}; do curl http://192.168.99.10 ; done来自 dev1 的问候。 您从 172.16.0.1:42723 连接到 172.16.0.11:80 来自 dev2 的问候。 您从 172.16.0.1:39117 连接到 172.16.0.12:80 来自 dev3 的问候。 您从 172.16.0.1:54545 连接到 172.16.0.13:80 来自 dev4 的问候。 您从 172.16.0.1:57020 连接到 172.16.0.14:80
NGINX Plus R10 及更高版本(以及 NGINX Open Source 1.11.0 及更高版本)可以欺骗上行流量的源地址。 将透明
参数包含在proxy_bind
指令中。 最常见的是,将源地址设置为远程客户端的地址:
proxy_bind $remote_addr 透明;
然而,这个简单的步骤带来了巨大的挑战,因为您需要确保正确处理到远程客户端的响应(传出)流量。 响应流量必须路由到 NGINX Plus,并且 NGINX Plus 必须终止上游 TCP 连接。 然后,NGINX Plus 通过客户端 TCP 连接将响应流量发送到远程客户端。
您需要对 NGINX Plus 负载均衡器和每个上游服务器进行几项配置更改:
在 NGINX Plus 负载均衡器上,将工作进程配置为以root
身份运行,以便它们可以将上游套接字绑定到任意地址。
在/etc/nginx/nginx.conf中的主(顶层)上下文中,包含用户
指令以将 NGINX Plus 工作进程的 ID 设置为root
:
# 在“主”上下文中
#“用户守护进程”是默认值;使用透明代理绑定更改为“用户 root”
用户 root;
在 NGINX Plus 负载均衡器上,确保每个连接都源自远程客户端地址。
将带有透明
参数的proxy_bind
指令添加到虚拟服务器的配置中:
# 在 'http' 上下文中
server {
listen 80;
location / {
proxy_bind $remote_addr transparent;
proxy_pass http://http_upstreams;
}
}
在NGINX Plus负载均衡器上,配置iptables
捕获上游服务器的返回数据包并投递给NGINX Plus。
在此示例中,我们运行iptables
和ip
rule
命令来捕获来自 IP 范围 172.16.0.0/28 所代表的服务器的端口 80 上的所有 TCP 流量:
# ip rule add fwmark 1 lookup 100 # ip route add local 0.0.0.0/0 dev lo table 100 # iptables -t mangle -A PREROUTING -p tcp -s 172.16.0.0/28 --sport 80 -j MARK --set-xmark 0x1/0xffffffff
运行以下命令来列出( -L
) iptables
mangle 表中的当前配置:
# iptables -t mangle -L链 PREROUTING (策略接受) 目标协议选择源目标 MARK tcp -- 172.16.0.0/28 任意位置 tcp spt:http MARK 设置 0x1 链 INPUT (策略接受) 目标协议选择源目标 链 FORWARD (策略接受) 目标协议选择源目标 链 OUTPUT (策略接受) 目标协议选择源目标 链 POSTROUTING (策略接受) 目标协议选择源目标
在上游服务器上,配置路由,以便所有返回流量都转发到 NGINX Plus。
在每个上游服务器上,删除任何预先存在的默认路由,并将默认路由配置为 NGINX Plus 负载均衡器/反向代理的 IP 地址。 请注意,此 IP 地址必须与上游服务器的某个接口位于同一子网。
#路由删除默认网关 10.0.2.2 #路由添加默认网关 172.16.0.1
检查路由表是否合理:
# route -n内核 IP 路由表 目标网关 Genmask 标志 度量标准 引用使用 Iface 0.0.0.0 172.16.0.1 0.0.0.0 UG 0 0 0 eth2 10.0.2.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 172.16.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth2 192.168.56.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
如果您的上游服务器需要能够连接到外部服务器,您还需要配置新的 NGINX Plus 网关来转发和伪装流量 - 请参阅下面的启用上游以访问外部服务器。
您现在可以通过向 NGINX Plus 发送请求来测试配置。 确保您发送的请求来自不能从上游服务器直接路由的远程 IP 地址:
$ for i in {1..4}; do curl http://192.168.99.10 ; done来自 dev1 的问候。 您从 192.168.99.1:60729 连接到 172.16.0.11:80 来自 dev2 的问候。 您从 192.168.99.1:43070 连接到 172.16.0.12:80 来自 dev3 的问候。 您从 192.168.99.1:45749 连接到 172.16.0.13:80 来自 dev4 的问候。 您从 192.168.99.1:46609 连接到 172.16.0.14:80
观察发现,连接现在似乎源自远程客户端的 IP 地址(192.168.99.1),而不是来自 NGINX Plus 负载均衡器本地的地址。
如果配置不起作用,请参阅下面的故障排除。
iptables
规则标记这些数据包,然后路由在本地传送它们。最终结果是,从上游服务器的角度来看,连接似乎直接来自远程客户端。
直接服务器返回 (DSR) 是 IP 透明度概念的扩展。 在 IP 透明度中,上游服务器接收看似来自远程客户端的数据包。 通过 DSR,上游服务器还可以直接响应远程客户端;返回数据包完全绕过负载均衡器。
DSR 可以带来一点性能优势,因为它减少了负载均衡器的负载,但它也存在一些限制:
DSR 对于 TCP 协议的用途有限,并且无论如何 NGINX Plus 的反向代理架构都不能应用于 DSR/TCP。
UDP 协议更加简单,没有 TCP 的任何连接语义。 您可以配置 NGINX Plus 以支持 DNS 等 UDP 协议的 DSR,这可以带来性能优势。 具体来说,DSR 意味着 NGINX Plus 不需要保持 UDP 套接字打开以等待响应数据包(这提高了可扩展性),并且响应数据包可以完全绕过 NGINX Plus 的第 7 层处理(这减少了延迟)。
对于 UDP 流量,IP 透明度配置和 DSR 配置之间有三个区别:
proxy_bind
端口配置)时必须欺骗远程客户端 IP 地址和端口。proxy_responses
0
指示)。此外,必须配置 NGINX Plus 以对上游服务器执行主动健康检查。 NGINX Plus 不能依靠其通常的被动检查来验证服务器是否健康,因为 NGINX Plus 不会观察服务器发送的响应数据包。
为了演示 DSR,首先创建一个由四台 DNS 服务器组成的负载平衡集群,这些服务器使用不同的 IP 地址来响应对名称www.example.com的查找。
配置一个简单的反向代理配置,在 DNS 服务器之间进行负载平衡:
# 在 'stream' 上下文中
server {
listen 53 udp;
proxy_responses 1;
proxy_timeout 1s;
proxy_pass dns_upstreams;
}
upstream dns_upstreams {
server 172.16.0.11:53;
server 172.16.0.12:53;
server 172.16.0.13:53;
server 172.16.0.14:53;
}
proxy_responses
和proxy_timeout
指令实现了基本的健康检查。 如果上游服务器在 1 秒内没有发送 1 个响应,NGINX Plus 会假定该服务器已发生故障并重试 DNS 请求。
配置每个 DNS 服务器以使用自己的 IP 地址来响应对www.example.com的查找:
$TTL 604800
@ IN SOA ns1.example.com.admin.example.com。 (
2 ; 串行
604800 ; 刷新
86400 ; 重试
2419200 ; 过期
604800 );负缓存 TTL
example.com。 IN NS ns1.example.com。
ns1 IN A 172.16.0.11
www IN A 172.16.0.11
测试表明 NGINX Plus 正在 DNS 服务器之间对请求进行负载均衡:
$ for i in {1..4} ; do dig +short @192.168.99.10 www.example.com ; 完成
172.16.0.11
172.16.0.12
172.16.0.13
172.16.0.14
NGINX Plus R10 及更高版本(以及 NGINX Open Source 1.11.2 及更高版本)可以欺骗上行流量的源地址和端口。 将透明
参数添加到proxy_bind
指令中:
proxy_bind $remote_addr:$remote_port 透明;
这使得上游服务器能够观察完整的源 IP 地址,因此它可以构建直接发送到远程客户端的响应数据报。
上游服务器生成具有正确 IP 目的地的响应(“出口”)数据包,但使用其本地 IP 地址作为源地址。 源地址需要重写为客户端原来连接的 NGINX Plus 负载均衡器的 IP 地址和端口。
有两种方法可用:
这两种方法都使用您通过tc
命令配置的无状态 NAT 功能。 如果上游服务器直接连接到互联网(这种拓扑结构意味着返回数据包不会通过您可以控制的中间路由器发送),那么您必须选择原始 NAT 方法。
响应数据包不会传送到 NGINX Plus,因此您需要禁用在创建标准 UDP 反向代理服务中配置的健康检查:修改proxy_responses
指令并禁用proxy_timeout
指令。 现在 NGINX Plus 不再等待响应,并且当没有收到响应时也不会断定上游服务器已发生故障。 禁用此检查还允许 NGINX Plus 立即重用套接字资源。
还在proxy_bind
指令的第一个参数中包含$remote_addr
和$remote_port
变量,以便 NGINX Plus 在发送到上游服务器的数据报中保留原始源地址和源端口:
# 在 'stream' 上下文中
server {
listen 53 udp;
proxy_bind $remote_addr:$remote_port transparent;
proxy_responses 0;
#proxy_timeout 1s;
}
您可以在单个中间路由器上重写传出数据包。 例如,如果上游服务器位于 NGINX Plus 负载均衡器后面的私有网络上,则可以使用负载均衡器作为默认路由并在转发数据包时重写它们。
配置每个上游服务器以通过 NGINX Plus 服务器路由所有传出流量:
#路由添加默认网关nginx-ip 地址
配置 NGINX Plus 服务器以转发 IP 流量:
# sysctl -w net.ipv4.ip_forward=1
配置 NGINX Plus 服务器以执行无状态 NAT 重写:
# tc qdisc add dev eth0 root handle 10: htb # tc filter add dev eth0 parent 10: 协议 ip prio 10 u32 match ip src 172.16.0.11 match ip sport 53 action nat egress 172.16.0.11 192.168.99.10 # tc filter add dev eth0 parent 10: 协议 ip prio 10 u32 match ip src 172.16.0.12 match ip sport 53 action nat egress 172.16.0.12 192.168.99.10 # tc filter add dev eth0 parent 10: 协议 ip prio 10 u32 match ip src 172.16.0.13 match ip sport 53 action nat egress 172.16.0.13 192.168.99.10 # tc filter add dev eth0 parent 10: 协议 ip prio 10 u32 匹配 ip src 172.16.0.14 匹配 ip sport 53 动作 nat egress 172.16.0.14 192.168.99.10
确保选择每个上游服务器的适当的出口接口和适当的 IP 地址。
有关无状态 NAT 的更多信息,请参阅tc
nat
手册页。 根据您的配置,您可能能够通过对src
和egress
old
参数使用 CIDR 掩码将tc
filter
命令减少为单个命令。
要显示当前tc
过滤器
配置,请运行以下命令:
# tc filter 显示 dev eth0
如果您能够在上游服务器上配置网络,尤其是当它们直接连接到 Internet 时,则可以使用以下配置。 它必须应用于每个上游服务器。
配置每个上游服务器执行无状态 NAT 重写:
# tc qdisc add dev eth0 root handle 10: htb # tc filter add dev eth0 parent 10: protocol ip prio 10 u32 match ip src 172.16.0.11 match ip sport 53 action nat egress 172.16.0.11 192.168.99.10
确保在每个上游选择适当的接口和 IP 地址。
要测试配置,请将 DNS 请求发送到 NGINX Plus 负载均衡器并验证它们是否在上游服务器之间进行负载平衡。
DSR 没有直接可见的效果。 如果你使用了proxy_responses
,那么你可以确信它正在工作0
指令配置 NGINX Plus 不期望响应数据包,但您的 DNS 客户端会收到负载平衡的响应。 您可以使用tcpdump
进一步观察数据包流,如下面故障排除中所述。
最终结果是响应数据包绕过 NGINX Plus 中的第 7 层处理并直接到达远程客户端。
如果 IP 透明度或 DSR 未按预期工作,请按照以下建议调查可能的原因:
root 身份
运行验证 NGINX Plus 工作进程是否配置为以root 身份
运行。 如果没有,当 NGINX Plus 尝试将套接字绑定到非本地地址时,您会在错误日志中看到类似以下内容的错误消息:
setsockopt(IP_TRANSPARENT) 失败(1: 操作不允许)连接到上游时,客户端: 192.168.99.1,服务器:,请求: “GET / HTTP/1.1”,上游:“http://172.16.0.11:80/”,主机: “192.168.99.10”
ping
进行测试验证您可以从 NGINX Plus 代理 ping 客户端和服务器。 除非您首先创建必要的路由配置并配置 NGINX Plus 中介来转发数据包,否则上游服务器无法 ping 远程客户端 IP 地址。
确保远程客户端使用的 IP 子网不直接连接到上游服务器。 如果是这样,则可能会出现两个问题:
tcpdump
在建立配置和测试每个中间步骤时,请在每台服务器上持续运行tcpdump
,以验证每个阶段的数据包是否被发送到正确的端点并由正确的端点接收:
$ sudo tcpdump -i any -n tcp 端口 80
使用下面列出的检查来调查任何异常行为。
仔细检查每台服务器上的路由表,特别注意上游服务器:
# route -n内核 IP 路由表 目标网关 Genmask 标志 度量标准 引用使用 Iface 0.0.0.0 172.16.0.1 0.0.0.0 UG 0 0 0 eth2 10.0.2.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 172.16.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth2 192.168.56.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
有哪些意想不到的路线? 您能否确认流中的所有数据包都将被路由到正确的目的地? 回想一下,在iptables
和路由器 NAT 配置中,所有出站数据包都必须通过中间 NGINX Plus 代理进行路由。
如果数据包意外丢失( tcpdump
显示它们由一台机器发送但未被另一台机器接收),则反向路径过滤可能是潜在的无声罪魁祸首。 要暂时禁用反向路径过滤,请运行以下命令:
#对于 /proc/sys/net/ipv4/conf/*/rp_filter 中的 f;执行 echo 0 > $f;完成
如果您的上游服务器位于私有网络上并使用 NGINX Plus(或其他服务器)作为其默认网关,您可能希望配置网关以允许上游服务器访问外部(Internet)主机。
您需要启用IP 转发,以便网关可以转发来自上游服务器的数据包;IP 转发通常默认是禁用的。 如果服务器没有可路由的 IP 地址(它们使用私有地址,例如 172.16.0.0/24),您还需要在网关服务器的外部接口上配置IP 伪装:
# sysctl -w net.ipv4.ip_forward=1 # iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
验证您是否可以从内部上游服务器 ping 外部服务器:
root@dev1:~# ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8) 56(84)字节数据。
来自 8.8.8.8 的 64 字节:icmp_seq=1 ttl=61 时间=6.72 毫秒 来自 8.8.8.8 的 64 字节:icmp_seq=2 ttl=61 时间=5.49 毫秒^C
要显示当前的转发、路由和iptables
nat
配置,请运行以下三个命令:
# sysctl net.ipv4.ip_forward #路由 -n # iptables -t nat -L
IP 透明度或直接服务器返回的配置很复杂,其他中间网络设备可能会通过丢弃或重写数据包来影响部署。 如果您需要帮助,NGINX 的专业服务团队随时准备为您提供帮助。
使用 NGINX Plus 亲自探索 IP 透明度和 DSR – 立即开始30 天免费试用或联系我们讨论您的用例。
“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”