使用 iptables 进行端口转发

svo*_*l13 1 networking iptables port-forwarding linux-networking dnat

我得到了lo127.0.0.1)和eth0172.17.0.8)。我想将登陆的数据包重定向127.0.0.1:80172.17.42.1:80(从 路由eth0)。

我试过

iptables -t nat -A OUTPUT -p tcp --dport 80 -d 127.0.0.1 -j DNAT --to 172.17.42.1:80
Run Code Online (Sandbox Code Playgroud)

但是当我这样做时我没有curl localhost:80得到任何回应,当我这样做时curl 172.17.42.1:80它就起作用了。

Jak*_*sic 8

当您尝试访问 localhost 时,您的源地址是 127.0.0.1,您的目标地址也是。所以,数据包看起来像这样:

 | SRC        |  DST       |
 | 127.0.0.1  |  127.0.0.1 |
Run Code Online (Sandbox Code Playgroud)

由于本地生成的数据包首先遍历 OUTPUT 链,因此您可以使用 DNAT 规则修改数据包:

iptables -t nat -A OUTPUT \
         -d 127.0.0.1 \
         -p tcp --dport 80 \
         -j DNAT \
         --to 172.17.42.1:80
Run Code Online (Sandbox Code Playgroud)

在 OUTPUT 链之后,数据包如下所示:

 | SRC        |  DST        |
 | 127.0.0.1  |  172.17.42.1 |
Run Code Online (Sandbox Code Playgroud)

所以,你看到的第一个错误是,即使这个数据包被路由到源设备之​​外,目的地也不知道如何正确返回它。因此,您还需要添加额外的 SNAT 规则:

iptables -t nat -A POSTROUTING \
         -d 172.17.42.1 \
         -p tcp --dport 80 \
         -j SNAT \
         --to-source <your_ip_addr>
Run Code Online (Sandbox Code Playgroud)

现在,在网络上退出的数据包看起来非常正确(源地址将是该设备的公共地址,而不是本地主机地址)。

但是,这还不能解决你的痛苦。

在本地机器上生成的数据包的路由决策在两个地方进行。

  • 在 OUTPUT 链之前
  • 在 OUTPUT 链之后和 POSTROUTING 之前

这个数据包的决定将在第二阶段做出,在源 IP 在 POSTROUTING 链中重写之前......

因此,内核安全机制将丢弃数据包,因为默认情况下内核拒绝路由 src 为 127.0.0.1 的数据包。这意味着即使 mangle 加上 fwmark 在这里也无济于事。要使其工作,需要启用 route_localnet。这是 per-iface 变量,它允许将 127/8 用于本地路由目的(默认值为 0 - 也称为禁用)。

sysctl -w net.ipv4.conf.all.route_localnet=1
Run Code Online (Sandbox Code Playgroud)

在您的情况下,您可以安全地更改传出接口名称的“全部”。

现在,数据包将按照表中之前的规则进行路由,并且由于我们有 POSTROUTING SNAT,我们将修复 127.0.0.1 的源 ip 并重写它。

希望这可以帮助。

附注。抱歉,在 2-3 次编辑之前,现在是凌晨 5 点,我还醒着:)