![使用 iptables 进行端口转发](https://linux22.com/image/646545/%E4%BD%BF%E7%94%A8%20iptables%20%E8%BF%9B%E8%A1%8C%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91.png)
我得到了lo
( 127.0.0.1
) 和eth0
( 172.17.0.8
)。我想将到达的数据包重定向127.0.0.1:80
到172.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
但当我这样做时,curl localhost:80
我没有得到任何回应,当我这样做时curl 172.17.42.1:80
它就起作用了。
答案1
当你尝试访问本地主机时,你的源地址是 127.0.0.1,目标地址也是。因此,数据包看起来如下所示:
| SRC | DST |
| 127.0.0.1 | 127.0.0.1 |
由于本地生成的数据包首先遍历 OUTPUT 链,因此您可以使用 DNAT 规则修改数据包:
iptables -t nat -A OUTPUT \
-d 127.0.0.1 \
-p tcp --dport 80 \
-j DNAT \
--to 172.17.42.1:80
经过OUTPUT链之后,数据包如下所示:
| SRC | DST |
| 127.0.0.1 | 172.17.42.1 |
因此,您看到的第一个错误是,即使此数据包被路由到源设备之外,目标也不知道如何正确返回它。因此,您还需要添加额外的 SNAT 规则:
iptables -t nat -A POSTROUTING \
-d 172.17.42.1 \
-p tcp --dport 80 \
-j SNAT \
--to-source <your_ip_addr>
现在,数据包看起来几乎正确,可以退出网络(源地址将是该设备的公共地址而不是本地主机地址)。
但是,这还不能解决你的痛苦。
本地机器生成的数据包的路由决策在两个地方做出。
- 在 OUTPUT 链之前
- 在 OUTPUT 链之后和 POSTROUTING 之前
在 POSTROUTING 链中重写源 IP 之前,将在第二阶段对此数据包做出决定...
因此,内核安全机制将丢弃数据包,因为默认情况下内核拒绝路由源地址为 127.0.0.1 的数据包。这意味着即使将 mangle 与 fwmark 结合使用也无法帮助我们。要实现此功能,需要启用 route_localnet。这是 per-iface 变量,允许使用 127/8 进行本地路由(默认值为 0 - 即禁用)。
sysctl -w net.ipv4.conf.all.route_localnet=1
您可以放心地将“all”更改为您情况下的传出接口名称。
现在,数据包将按照表中的先前规则进行路由,并且由于我们有 POSTROUTING SNAT,我们将修复 127.0.0.1 的源 IP 并重写它。
希望这可以帮助。
附言:抱歉,在进行 2-3 次编辑之前,这里是凌晨 5 点,我还没睡 :)
答案2
在直接进入防火墙规则之前,还应该执行简单的转发检查。就像在拆开硬件之前检查电源线是否插好一样。
跑步:
cat /proc/sys/net/ipv4/ip_forward
如果结果为零,则 IPv4 将不会转发。您需要启用此功能。
要立即并暂时打开它来验证行为:
echo 1 > /proc/sys/net/ipv4/ip_forward
上述操作会为机器打开它,但只是动态修改内核设置,不会被“保存”。
编辑 sysctl.conf 文件以进行适当的永久更改并确保以下设置:
net.ipv4.ip_forward = 1