iptable 中 nat 和端口转发的奇怪行为

iptable 中 nat 和端口转发的奇怪行为

我有几个专用服务器托管在几个数据中心,我想迁移邮件(pop3映射简体中文:以及它们的 TLS/SSL 变体)服务从一台服务器迁移到另一台服务器。

为此,我打算暂时安装一个NAT将新服务器的路由到旧服务器以处理 DNS 传播时间。

所以我定义了以下内容IP表规则:

iptables -t nat -A PREROUTING -p tcp -m tcp --dport 25  -j DNAT --to-destination <my_remote_ip>:8025
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 110 -j DNAT --to-destination <my_remote_ip>
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 143 -j DNAT --to-destination <my_remote_ip>
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 465 -j DNAT --to-destination <my_remote_ip>:8465
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 587 -j DNAT --to-destination <my_remote_ip>:8587
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 993 -j DNAT --to-destination <my_remote_ip>
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 995 -j DNAT --to-destination <my_remote_ip>
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 110  -j SNAT --to-source <my_local_ip>
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 143  -j SNAT --to-source <my_local_ip>
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 993  -j SNAT --to-source <my_local_ip>
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 995  -j SNAT --to-source <my_local_ip>
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 8025 -j SNAT --to-source <my_local_ip>:25
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 8465 -j SNAT --to-source <my_local_ip>:465
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 8587 -j SNAT --to-source <my_local_ip>:587

iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 110  -j ACCEPT
iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 143  -j ACCEPT
iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 993  -j ACCEPT
iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 995  -j ACCEPT
iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 8025 -j ACCEPT
iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 8465 -j ACCEPT
iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 8587 -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 110  -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 143  -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 993  -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 995  -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 8025 -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 8465 -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 8587 -j ACCEPT

(实际上简化了,事实上这对于 IPv4 和 IPV6 是重复的,在某些服务器上,接口可能与 eth0 不同……当然,我弄乱了实际的 IP 地址)

您可能会注意到,邮件服务仅通过 NAT,但 SMTP 相关服务也通过端口翻译,与目标服务器上的反向转换或这些端口的特定监听相关联。

这样做的理由很充分:我的托管服务提供商会监控传出的 SMTP 连接,以便检测和阻止托管在其托管的所有服务器上的垃圾邮件。但如果我将传入连接转发到 SMTP 端口到我的另一台服务器,传入垃圾邮件变成发送垃圾邮件 (从数据中心的角度来看)在它有机会被过滤之前(在目标服务器上),结果就是我的托管服务提供商立即阻止了 NAT 服务器。

因此我还必须转换端口号才能转发这些连接。

传入数据包和传出(经过 NAT 的)数据包使用相同的接口(因为这些服务器只有一个网络接口)

实际上这或多或少是可行的,除了端口转发连接(只有这些,端口 110、143 等没有问题)有一个奇怪的行为:我第一次使用它们时它们可以工作,但如果我立即断开连接并重新连接,转发将不再起作用,我必须等待大约 1 到 3 分钟才能再次连接。

这似乎与 IP 地址有关,而与端口号无关:即使之前的连接是在端口 110(pop3,而不是端口转发),我也必须等待相同的延迟才能连接到端口 25。

我已经在几台服务器上验证过,它们都是Linux Debian Wheezy杰西或者拉紧,并且IPv4IPv6 (除了无法进行 IPv6 NAT 的 Wheezy)………是的,我知道 Wheezy 现在已经老了,这就是我要迁移的原因。

所有 IP 地址都是完全静态的。

是的,我已经设置了/proc/sys/net/ipv4/ip_forward(以及 IPv6 等效版本)1

我用远程登录用于测试连接,然后我使用以下方法检查转发tcpdump。通过后者,我可以检查转发是否确实没有完成,并且不是阻止传入连接的目标服务器。

请有人能帮我找出这个 1-3 分钟阻塞延迟的原因以及如何解决它?

银杏

答案1

您的问题与 Linux 连接跟踪器的特殊性有关。

快速回答:您无法在配置中避免这种延迟。避免此问题的唯一方法是使用-j SNAT不带端口号指定的--to-source选项。

还有一个小技巧,对您帮助不大——使用j SNAT端口范围而不是单个端口号。这样您就可以建立多个连接。同时连接的数量是范围内的端口数。规则如下:

iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 8025 -j SNAT --to-source <my_local_ip>:10025-10125

如果您想要了解详细信息,我可以扩展答案。

更新

要了解详细信息,您应该有一些背景知识。"Linux kernel networking: implementation and theory"由 Rami Rosen 撰写。您主要需要本书中的“第 9 章。Netfilter。连接跟踪器”。

当数据包通过您的 Linux 主机时,Linux 连接跟踪器 ( conntrack) 会分析它们并将有关数据包流的信息存储到表 ( conntrack table) 中。每个数据包流都显示为 conntrack 条目。conntrack 使用元组来识别数据包流。元组由以下部分组成:L3(两端的 IP 地址和 L4 协议数量)和L4(对于 TCP,这是两端的端口号信息)流的信息。

conntrack 针对每个 L4 协议都有一些模块来跟踪传输协议特定的连接状态。TCP conntrack 部分实现了 TCP 有限状态机。

在实验室(内核 4.14)中,这个 TCP conntrack 部分的行为有些奇怪。让我们在简单环境中演示一下。

客户端 ( 192.0.2.2) 连接到 Linux 主机 ( 192.0.2.1:22),该主机将此连接转发到其他主机 ( 192.0.2.6:22)。Linux 主机也使用 SNAT 规则,就像在您的设置中一样。

tcpdump 输出:

14:47:32.036809 IP 192.0.2.2.40079 > 192.0.2.1.22: Flags [S], seq 2159011818, win 29200, length 0
14:47:32.037346 IP 192.0.2.1.22 > 192.0.2.2.40079: Flags [S.], seq 960236935, ack 2159011819, win 28960, options [mss 1460,sackOK,TS val 1415987649 ecr 3003498128,nop,wscale 5], length 0
14:47:32.037683 IP 192.0.2.2.40079 > 192.0.2.1.22: Flags [.], ack 1, win 913, options [nop,nop,TS val 3003498129 ecr 1415987649], length 0
14:47:32.041407 IP 192.0.2.1.22 > 192.0.2.2.40079: Flags [P.], seq 1:22, ack 1, win 905, options [nop,nop,TS val 1415987653 ecr 3003498129], length 21
14:47:32.041806 IP 192.0.2.2.40079 > 192.0.2.1.22: Flags [.], ack 22, win 913, options [nop,nop,TS val 3003498133 ecr 1415987653], length 0
14:47:35.826919 IP 192.0.2.2.40079 > 192.0.2.1.22: Flags [F.], seq 1, ack 22, win 913, options [nop,nop,TS val 3003501918 ecr 1415987653], length 0
14:47:35.827996 IP 192.0.2.1.22 > 192.0.2.2.40079: Flags [F.], seq 22, ack 2, win 905, options [nop,nop,TS val 1415991440 ecr 3003501918], length 0
14:47:35.828386 IP 192.0.2.2.40079 > 192.0.2.1.22: Flags [.], ack 23, win 913, options [nop,nop,TS val 3003501919 ecr 1415991440], length 0

在 Linux 主机上的 conntrack 中我看到以下内容:

ipv4     2 tcp      6 431999 ESTABLISHED src=192.0.2.2 dst=192.0.2.1 sport=40079 dport=22 src=192.0.2.6 dst=192.0.2.5 sport=22 dport=22 [ASSURED] mark=0 zone=0 use=2
ipv4     2 tcp      6 431998 ESTABLISHED src=192.0.2.2 dst=192.0.2.1 sport=40079 dport=22 src=192.0.2.6 dst=192.0.2.5 sport=22 dport=22 [ASSURED] mark=0 zone=0 use=2
ipv4     2 tcp      6 431997 ESTABLISHED src=192.0.2.2 dst=192.0.2.1 sport=40079 dport=22 src=192.0.2.6 dst=192.0.2.5 sport=22 dport=22 [ASSURED] mark=0 zone=0 use=2
ipv4     2 tcp      6 431996 ESTABLISHED src=192.0.2.2 dst=192.0.2.1 sport=40079 dport=22 src=192.0.2.6 dst=192.0.2.5 sport=22 dport=22 [ASSURED] mark=0 zone=0 use=2
ipv4     2 tcp      6 119 TIME_WAIT src=192.0.2.2 dst=192.0.2.1 sport=40079 dport=22 src=192.0.2.6 dst=192.0.2.5 sport=22 dport=22 [ASSURED] mark=0 zone=0 use=2
...
ipv4     2 tcp      6 0 TIME_WAIT src=192.0.2.2 dst=192.0.2.1 sport=40079 dport=22 src=192.0.2.6 dst=192.0.2.5 sport=22 dport=22 [ASSURED] mark=0 zone=0 use=2

如您所见,尽管连接已正确关闭,但关联的 conntrack 条目仍与状态一起显示在 conntrack 表中TIME_WAIT。而且,由于我们只有一个可能的 SNAT 端口,该端口已经很忙,因此新的连接尝试会失败。为什么我们不再使用这个端口?因为系统无法区分192.0.2.6当前状态流TIME_WAIT和新状态流的回复数据包。

TIME_WAIT我还没有弄清楚为什么 conntrack 将连接设置为该状态而不是销毁它。

相关内容