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

Luc*_*Luc 2 iptables linux-networking dnat

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

现在我使用此答案添加了以下用于反向代理/DNAT 的 iptables 规则。我的启动脚本现在看起来像:

# 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
Run Code Online (Sandbox Code Playgroud)

最初,-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)。

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

为什么 iptables 规则仅适用于 eth0 而不适用于 eth1?

编辑:了解用户空间中的代理工作正常可能是相关的:

mkfifo /tmp/fifo
sudo nc -kvlp 443 </tmp/fifo | nc 10.0.0.1 443 >/tmp/fifo
Run Code Online (Sandbox Code Playgroud)

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

Ant*_*lov 5

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

网络图

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

检查命令的输出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. 数据包被发送到GW1througheth0接口。源地址将被-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. 远程系统接收数据包,处理它并使用带有 flags 的 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
Run Code Online (Sandbox Code Playgroud)
  1. 创建按输入接口匹配的路由规则:
ip rule add iif eth0 lookup 10 pref 1010
ip rule add iif eth1 lookup 11 pref 1011
Run Code Online (Sandbox Code Playgroud)

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