Nftables DNAT 似乎不起作用

Nftables DNAT 似乎不起作用

我正在尝试使用 nftables 在新的 centos 8 上设置 DNAT。这个实用程序(和 centos 8)对我来说很新,我已经使用 iptables(centos 到 6)很久了。

我的猜测是,我没有正确设置 DNAT 以使其生效,但我可能只是没有正确使用这些工具。或者两者兼而有之。

无论如何,如果有关系的话,这里是我之前就同一盒子上的一些路由问题提出的一个问题:多个互联网连接,错误的 NIC 端口上的传入数据包(入站路由问题?)(问题是 ARP 通量,已解决)。

下面是我当前设置的草图,蓝线标记了我想要发生的事情(预期的“路径”)

在此处输入图片描述

基本上,数据包从互联网进入,经过接口 2(ens2),通过本地接口(ens5,本地 IP 192.168.1.10)进行 DNAT 到 192.168.1.2。(一旦此操作成功,ens 3 和 ens 4 将进行相同的设置,并进入同一 LAN 上的几个不同的虚拟机)

我已经验证数据包通过正确的接口进入(预期的 nft 日志触发),但是 conntrack -E没有显示任何内容。

此外,centos 6 框(实际目标,192.168.1.2)上的 iptables 日志没有显示任何内容(很久以前设置的相同日志在我上次检查时,也就是几个月前,显示了预期的输出,所以理论上那个框应该没问题)

这是我现在的 nftables 脚本,其中 IP/IF 已翻译以匹配草图。

table ip nat {
    chain PREROUTING {
            type nat hook prerouting priority -100; policy accept;
            iif "ens2" goto PREROUTING_RDS2
            iif "ens3" goto PREROUTING_RDS3
    }

    chain PREROUTING_RDS2 {
            tcp dport { http, https } log prefix "RDS2_dnat-3 "
            tcp dport { http, https } dnat to IP_6
    }

    chain PREROUTING_RDS3 {
            tcp dport { http, https } log prefix "RDS3_dnat-3 "
            tcp dport { http, https } dnat to IP_6
    }
}

table inet filter {
    chain INPUT {
            type filter hook input priority 0; policy drop;
            #
            iif "lo" accept
            #
            # allow ping
            ip protocol icmp icmp type echo-request limit rate 1/second log prefix "PING "
            ip protocol icmp icmp type echo-request limit rate 1/second accept
            # following is required and must be BEFORE the ct state established otherwise the ping flooding will not be stopped
            ip protocol icmp drop
            #
            ct state established,related accept
            ct status dnat accept
            #
            iifname "ens5" goto INPUT_LOCAL
            #
            # now we drop the rest
            ct state invalid log prefix "INPUT_STATE_INVALID_DROP: "
            ct state invalid drop
            log prefix "INPUT_FINAL_REJECT: "
            reject with icmpx type admin-prohibited
    }

    chain FILTER {
            type filter hook forward priority 50; policy drop;
            iif "ens2" goto FILTER_RDS2
            iif "ens3" goto FILTER_RDS3
    }

    chain INPUT_LOCAL {
            tcp dport ssh goto INPUT_LOCAL_ssh
    }

    chain INPUT_LOCAL_ssh {
            ip saddr IP_MY_PC accept
    }

    chain FILTER_RDS2 {
            oifname "ens5" ip daddr IP_6 tcp dport { http, https } accept
    }

    chain FILTER_RDS3 {
            oifname "ens5" ip daddr IP_6 tcp dport { http, https } accept
    }
}

先感谢您。

答案1

事实上,如果不仔细研究,这个问题很难回答。先前的问答解决初始设置centos8。解决方案变得非常复杂。考虑到必须为此实施的配置类型,每个接口一个 IP 可能不值得,多个接口位于同一个 LAN 上,而不是所有 IP 都位于同一个接口上,尤其是考虑到它处于虚拟环境中:不会有任何加速。对配置的任何更改都必须反映在下面的所有命令中,因此正确管理它将很困难。


centos8路由器

由于为了解决同一 LAN 中的多个接口问题,需要额外的路由表,因此在本问答中centos8充当路由器,必须将更多路由条目从主表复制到附加路由表:

# ip route add 192.168.1.0/24 dev ens5 table 1001 src 192.168.1.10 
# ip route add 192.168.1.0/24 dev ens5 table 1002 src 192.168.1.10 
# ip route add 192.168.1.0/24 dev ens5 table 1003 src 192.168.1.10 
# ip route add 192.168.1.0/24 dev ens5 table 1004 src 192.168.1.10 

否则,ens1ens2ens3或者ens4基因通过ens5将失败反向路径过滤器因为没有路可以穿过ens5在那些桌子上。

当然这还不够:回复数据包中没有任何信息(例如:从centos6) 关于使用了哪个接口以及应该以相反的方式重用哪个接口。因此,必须使用 netfilter 的 conntrack 逐个流记住此信息。在 nftables 规则中,删除整个ip nat表:

# nft delete table ip nat

并用这个新表替换它ip markandnat

# nft -f - << 'EOF'
table ip markandnat {
        map iif2mark {
                type iface_index : mark;
                elements = {
                        ens1 : 101,
                        ens2 : 102,
                        ens3 : 103,
                        ens4 : 104
                }
        }

        map mark2daddr {
                type mark : ipv4_addr;
                elements = {
                        102 : 192.168.1.2,
                        103 : 192.168.1.2, # same IP, as per OP's config
                        104 : 192.168.1.4  # some other VM
                }
        }
        chain premark {
                type filter hook prerouting priority -150; policy accept;
                meta mark set ct mark meta mark != 0 return
                meta mark set iif map @iif2mark meta mark != 0 ct mark set meta mark
        }

        chain prenat {
                type nat hook prerouting priority -100; policy accept;
                tcp dport { http, https } dnat to meta mark map @mark2daddr
        }
}
EOF

这将映射接口 => 标记 => dnat 目的地,同时将标记保存为 conntrack 的标记(请参阅末尾的链接关于康马克使用)。现在,通过添加以下规则,此标记将可用并由路由堆栈使用,以指向相同的附加路由表:

# ip rule add pref 11001 fwmark 101 table 1001
# ip rule add pref 11002 fwmark 102 table 1002
# ip rule add pref 11003 fwmark 103 table 1003
# ip rule add pref 11004 fwmark 104 table 1004

但还有一部分没有说明:再次介绍反向路径过滤器。当使用标记时,反向路径过滤器不会使用标记更改的新路线重新检查,并且通常会检查失败。实际上有一个未记录的功能,在 2009/2010 年的内核 2.6.33/2.6.32.8 中添加,恰好解决了这个问题,而不需要使用松散反向路径模式:src_valid_mark

# sysctl -w net.ipv4.conf.ens1.src_valid_mark=1
# sysctl -w net.ipv4.conf.ens2.src_valid_mark=1
# sysctl -w net.ipv4.conf.ens3.src_valid_mark=1
# sysctl -w net.ipv4.conf.ens4.src_valid_mark=1

centos6服务器

如果您想暂时使用备用网关,即使这又会增加复杂性,并且可能带来无法预见的微妙副作用,也可以通过标记来实现。由于它是 CentOS 6,nftables不可用,因此iptables将会被使用。

我会认为centos6VM 在(唯一)接口上有 IP 192.168.1.2/24eth0,默认网关为 192.168.1.1。让我们为备用网关 192.168.1.10 添加新的路由表和规则:

# ip route add table 10 default via 192.168.1.10
# ip rule add fwmark 10 lookup 10

放在iptables规则(这里只有曼格尔需要表格):

# iptables-restore << 'EOF'
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -j CONNMARK --restore-mark
-A PREROUTING -m mark ! --mark 0 -j RETURN
-A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -j MARK --set-mark 10
-A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j MARK --set-mark 10
-A PREROUTING -m mark ! --mark 0 -j CONNMARK --save-mark
-A OUTPUT -m connmark ! --mark 0 -j CONNMARK --restore-mark
COMMIT
EOF

现在,端口 80 或 443 上收到的任何流都将标记传入数据包及其回复。路由堆栈将使用此标记将传入和回复的网关更改为 192.168.1.10(mangle/输出触发重新路由检查,参见下面的第二个链接)。

在这种情况下似乎不需要使用src_valid_mark,但只需设置它或设置rp_filter=2如果它不起作用。此设置不允许同时接收基因通过 192.168.1.1 编辑流量。


一些链接:

答案2

从评论来看,最直接、最重要的遗漏是关闭 IP 转发。只需:

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

并检查DNAT数据包是否已经到达IP6。

第二个问题是非对称路由。经过 DNAT 处理的数据包通过 192.168.1.10 (IP5) 到达 IP6,在那里它们被修改(目标地址被更改)。返回数据包将通过 LAN 上的默认网关(182.168.1.1),并且在路由到连接源时不会被修改。它们可能会保留其 RFC1918 地址,或者在 192.168.1.1 上被 SNAT 为其他地址,并且永远不会匹配其目的地上的任何连接,并且可能会被丢弃。

编辑:

因此,为了解决 FORWARD 链,我会将其重写为以下内容(在我看来更简单):

table inet filter {
:
    chain FORWARD {
            type filter hook forward priority 0; policy drop;
            ct state established,related accept
            ct status dnat accept
    }
:
}

相关内容