具有多个 NIC 和公共 IP 地址的 iptables 源 NAT 不起作用

具有多个 NIC 和公共 IP 地址的 iptables 源 NAT 不起作用

我只是在多网卡和 IP 地址服务器上遇到了问题。我为 Cent OS 7 服务器配置了一个属于 eth1 的附加公共 IP 地址。当启用 iptables 源 NAT 时,只有附加 IP 地址无法使用。

在本文中,两个公网 IP 地址分别为 99.0.1.100 (eth0) 和 99.0.2.100 (eth1)。99.0.2.100 的子网掩码为 255.255.254.0,默认网关设置正确(99.0.1.1 对应 99.0.1.100,99.0.2.1 对应 99.0.2.100)。


以下是 的内容/etc/sysconfig/network-scripts/ifcfg-eth1。(HWADDR 已被审查)

DEVICE=eth1
TYPE=Ethernet
HWADDR=**:**:**:**:**:**
ONBOOT=yes
NM_CONTROLLED=no
BOOTPROTO=static
IPADDR=99.0.2.100
NETMASK=255.255.254.0

以下是 的输出ip route

default via 99.0.1.1 dev eth0 proto static metric 100
99.0.1.0/23 dev eth0 proto kernel scope link src 99.0.1.100 metric 100
99.0.2.0/23 dev eth1 proto kernel scope link src 99.0.2.100

以下是 的输出ip rule

0:      from all lookup local
32765:  from 99.0.2.100 lookup subroute-eth1
32766:  from all lookup main
32767:  from all lookup default

以下为 的内容/etc/sysconfig/network-scripts/route-eth1

default via 99.0.2.1 table subroute-eth1

以下为 的内容/etc/sysconfig/network-scripts/rule-eth1

from 99.0.2.100 table subroute-eth1

然后,以下请求已被正确发送,并且对于每个公共 IP 地址,以字符串形式响应正确的 IP 地址。

# curl --interface eth0 api.ipify.org  # outputs 99.0.1.100
# curl --interface eth1 api.ipify.org  # outputs 99.0.2.100

这里,我希望系统的每个用户使用不同的公网 IP 地址。因此应用了以下 iptables 源 NAT。其中 uid=1000 为 A,uid=1001 为 B。

# iptables -t nat -m owner --uid-owner 1000 -A POSTROUTING -j SNAT --to-source 99.0.1.100
# iptables -t nat -m owner --uid-owner 1001 -A POSTROUTING -j SNAT --to-source 99.0.2.100

在这种情况下,eth0工作正常,但eth1已超时。

# curl api.ipify.org  # outputs 99.0.1.100
# sudo -u A curl api.ipify.org  # outputs 99.0.1.100
# sudo -u B curl api.ipify.org  # **time out**

由于缺乏网络经验,我不知道哪里出了问题。如果有人有想法,请帮助我。请注意,在这种情况下,互联网仍然正常工作。ssh [email protected]


编辑:以下是输出ip route show table subroute-eth1

default via 99.0.2.1 dev eth1

答案1

uid 应该触发路由更改eth1以代替eth0nat/POSTROUTING的 SNAT(虽然仍然需要)来得太晚了,因为正如名称 POSTROUTING 所暗示的那样,它发生在路由决策之后:网络接口被选择并且不会再改变。

对于本地流量,规则必须在mangle/OUTPUT链中发生以改变路线,通过使用可以触发ip rule查找的标记:

# iptables -t mangle -A OUTPUT -m owner --uid-owner 1000 -j MARK --set-mark 1000
# iptables -t mangle -A OUTPUT -m owner --uid-owner 1001 -j MARK --set-mark 1001

然后通过 重新使用这些信息ip rule。甚至 1000 也必须考虑(以处理uid1000$ curl --interface eth1 http://api.ipify.org/不应使用条目 32765 的特定情况):

ip rule add fwmark 1000 lookup main
ip rule add fwmark 1001 lookup subroute-eth1

请注意,初始默认 IP 是之前选择的:仍然需要 SNAT 将其修复为新的正确 IP。既然已经有了规则,为什么还要使用此标记from 99.0.2.100 lookup subroute-eth1?同样,这是因为网络堆栈中各种评估的顺序,如下所述Netfilter 中的数据包流和通用网络示意图:只有发生改变mangle/OUTPUT才能触发重新路由检查如图所示,此时 IP 尚未更正为 99.0.2.100。

在反向路径中,一些网络元素无论如何都不会及时了解这些路由表,因此必须将松散模式设置eth1rp_过滤器否则返回数据包将被丢弃:

# echo 2 > /proc/sys/net/ipv4/conf/eth1/rp_filter

eth0处理上述 uid 1000 的具体情况也是一样的( uid1000$ curl --interface eth1 http://api.ipify.org/):

# echo 2 > /proc/sys/net/ipv4/conf/eth0/rp_filter

这样就应该大多可以工作。但实际上,一些数据包不属于 uid 1001:,TCP RST最后一个数据包ACK在进程结束后,或者对于 UDP,相关的 ICMP 错误... 仅由内核拥有。它们与 uid 1001 不匹配,将在错误的接口上发送(在这个错误的接口上使用错误的 IP),这将在连接结束时造成一些超时,更可能是在远程端而不是本地。这可以通过在kill -KILL客户端上执行 来看到,该客户端通过 与用户 1001 建立更改的连接eth1,并见证ACK通过eth0(而不是eth1)发送的源 99.0.2.100 的几个重试数据包,确认FIN在 上多次收到来自远程的数据包eth1

因此,所有这些都必须重新使用CONNMARK追踪流量由用户发起,而不是数据包由用户发起(一些解释这里)。nat无需更改,因为NEW无论如何只有状态中的第一个数据包会通过 iptables 规则,并且它已经在跟踪流量。让我们重新开始:

# iptables -t mangle -F
# iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
# iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark
# iptables -t mangle -A OUTPUT -m owner --uid-owner 1000 -j MARK --set-mark 1000
# iptables -t mangle -A OUTPUT -m owner --uid-owner 1001 -j MARK --set-mark 1001
# iptables -t mangle -A OUTPUT -j CONNMARK --save-mark

现在一切都应该正常运行。

最后说明:虽然您的设置中不需要它,但 eth1 的链接本地路由应该在表中重复subroute-eth1,否则eth1从 uid 1001 访问的 LAN 将产生奇怪的结果,因为规则 32765 在规则 32766 之前读取,因此永远不会定义链接本地路由,正如此命令的结果所示:

# ip route get 99.0.2.55 mark 1001
99.0.2.55 via 99.0.2.1 dev eth1 table subroute-eth1 src 99.0.2.100 mark 0x3e9 uid 0 \    cache

它可能会阻止访问网关以外的所有 LAN。

您应该添加(或根据需要放入系统配置文件中):

ip route add table subroute-eth1 99.0.2.0/23 dev eth1 scope link src 99.0.2.100

然后前一个ip route get命令将给出正确的结果:

99.0.2.55 dev eth1 table subroute-eth1 src 99.0.2.100 mark 0x3e9 uid 0 \    cache 

相关内容