使用 iptables 的 DNAT 仅适用于 eth0 上的传入流量

使用 iptables 的 DNAT 仅适用于 eth0 上的传入流量

我有一台机器,它有两个接口,具有不同的可路由 IPv4 地址。为了在正确的接口上返回流量,我使用了这个答案和评论,并且它可以正常工作:我可以使用任一 IP 地址 ssh 进入机器。(我不知道这是否有任何影响,但可能会有。)

现在我添加了以下 iptables 规则用于反向代理/DNAT使用这个答案。我的启动脚本现在如下所示:

# eth0 is automatically brought up and gets 192.168.1.2 as IP

sudo ip link set eth1 up
sudo ip addr add 192.168.2.2/24 dev eth1
sudo ip rule add from 192.168.2.2 table frometh1
sudo ip route add default via 192.168.2.1 dev eth1 table frometh1

sudo iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j DNAT --to-destination 10.0.0.1:443
sudo iptables -t nat -A PREROUTING -i eth1 -p tcp -m tcp --dport 443 -j DNAT --to-destination 10.0.0.1:443
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE

echo 1 > /proc/sys/net/ipv4/ip_forward

最初,-i eth*省略了 ,DNAT 规则只是一个命令。MASQUERADE 规则也是一个命令,只需将其替换-o eth*-d 10.0.0.1。我将它们拆分,以尝试使接口明确,以防 iptables 隐式地使用其中一个接口。

当我尝试访问时192.168.1.2:443,流量按预期转发和伪装。PREROUTING 表中正确的 DNAT 规则的命中计数器递增,正确的 MASQUERADE 规则也递增。我可以成功建立 TCP 连接。

尝试访问时192.168.2.2:443,我可以看到接口上的传入流量,并且 PREROUTING 表中正确 DNAT 规则的命中计数器会递增。但没有传出数据包,并且 POSTROUTING 中的两个 MASQUERADE 规则均未递增。TCP SYN 从未到达远程系统 ( 10.0.0.1)。

10.0.0.1请注意,转发是通过 eth0 还是 eth1 进行并不重要,通过 eth0 或 eth1将流量转发到目标()都可以。

为什么 iptables 规则只对 eth0 起作用,而不对 eth1 起作用?

编辑:了解用户空间中的代理是否运行良好可能是相关的:

mkfifo /tmp/fifo
sudo nc -kvlp 443 </tmp/fifo | nc 10.0.0.1 443 >/tmp/fifo

允许交通双向流动,正如本博客所述。此解决方案的问题是远程端只能看到一个持久的 TCP 连接。它应该看到每个客户端一个连接并同时处理多个客户端。

答案1

首先,让我们绘制您的网络拓扑。

网络图

我认为这是反向路径过滤器的副作用,因为数据包在路由决策步骤中被丢弃。

检查命令的输出nstat -az 'TcpExtIPReversePathFilter'。它很可能显示非零计数器,这些计数器在检查时会增加。

使用命令检查路由ip route get 10.0.0.1 from <external-host-ip> iif eth1。我认为它会显示类似以下内容invalid cross-device link

rp_filter您可以在命令输出中看到接口的当前状态ip netconf show

禁用rp_filter或最好将其设置为loose模式。这rp_filter可防止下游网络中的 IP 地址欺骗,这在 DDoS 放大攻击中被广泛使用。在您的情况下,调整不会增加新的风险,因为您的 Linux 主机不是连接网络的主要网关。

这不是您设置中的最后一个问题。rp_filter如果存在,请修复该问题,然后我会扩展答案以使您的配置正常工作。

更新

现在,当rp_filter问题解决后,让我们尝试使配置按预期工作。

让我们从描述发生了什么以及出了什么问题开始。

  1. 客户端主机将数据包发送到ext2.IP。原始数据包如下所示

C.IP:<someport> -> <ext2.IP>:443

  1. 数据包到达GW2并通过端口转发后将如下所示,并将被发送到 Linux 主机。

C.IP:<someport> -> 192.168.2.2:443

  1. 当数据包到达 Linux 主机时,DNAT 规则将目标再次重写为10.0.0.1:443。现在我们有以下形式的数据包:

C.IP:<someport> -> 10.0.0.1:443

  1. 只有在目的地重写后,Linux 才会查找路由(路由决策)。由于数据包中的源地址仍然是原始的,因此不会涉及您的附加路由规则,并且数据包将通过主路由表进行路由(我假设将GW1使用)。我们将对其进行改进,因为问题的根本原因就在于此。

  2. 数据包被发送到GW1直通eth0接口。源地址将被规则重写-o eth0 -j MASQUERADE。数据包将转换为:

192.168.1.2:<otherport> -> 10.0.0.1:443与 的目标 MAC 地址GW1

  1. 将此GW1数据包转发到外部网络并进行额外的源地址重写。

<ext1.IP>:<otherport2> -> 10.0.0.1:443

  1. 远程系统接收数据包,处理它并使用带有标志的 tcp 数据报对其进行应答SYN-ACK。答案将如下所示:

10.0.0.1:443 -> <ext1.IP>:<otherport2>

  1. 收到GW1答复后,对目标地址进行反向地址转换并将答复进一步发送给Linux主机:

10.0.0.1:443 -> 192.168.1.2:<otherport>

  1. 最后,Linux 主机收到SYN-ACK答案,也进行反向地址转换,并尝试查找路由以进一步转发答案。但在这一步之后,出现了问题,但这只是这一步出现问题的结果4。为了更好地理解需要检查防火墙规则集。

10.0.0.1:443 -> <C.IP>:<someport>

解决方案

主要目标是通过接收数据包的同一接口将数据包路由回去。为此,我们将使用简单的路由规则。

  1. 创建两个附加路由表(每个上行链路通道一个)
ip route add 192.168.1.0/24 dev eth0 table 10
ip route add 0/0 via 192.168.1.1 dev eth0 table 10

ip route add 192.168.2.0/24 dev eth1 table 11
ip route add 0/0 via 192.168.2.1 dev eth1 table 11
  1. 创建按输入接口匹配的路由规则:
ip rule add iif eth0 lookup 10 pref 1010
ip rule add iif eth1 lookup 11 pref 1011

所有在特定接口上接收到的数据包都将通过有限的路由表进行路由,其中​​只有单个接口的路由。这是最简单的解决方案,但它消除了 Linux 主机在接口之间转发数据包的能力(从eth0eth1,反之亦然)。如果这不适合您,还有另一种方法,但更复杂。如果您需要,我会描述它。

相关内容