具有 nftables 和 VLAN 的透明防火墙

Sev*_*gun 5 linux firewall vlan nftables

我想向您询问有关透明防火墙构建的最佳实践建议。

我有 2 段网络和带有 2 个 10G 接口的 CentOS 服务。我想过滤/监控/限制/丢弃网段之间的流量。流量已被标记。我应该取消流量过滤标记并将其标记回来,还是 nftable 可以处理标记的流量?

现在方案看起来像:

PCs--|                                         |--PCs
PCs--|--untag--[Switch]--tag--[Switch]--untag--|--PCs
PCs--|                                         |--PCs
Run Code Online (Sandbox Code Playgroud)

我想:

PCs--|                                                              |--PCs
PCs--|--untag--[Switch]--tag--**[Firewall]**--tag--[Switch]--untag--|--PCs
PCs--|                                                              |--PCs
Run Code Online (Sandbox Code Playgroud)

A.B*_*A.B 4

TL;DR:nftables 在桥接级别可以通过使用略有不同的规则来处理带标记或未标记的数据包。所有标记工作都可以通过 vlan 感知网桥在 Linux 端完成,因此无论 nftables 防火墙中做出什么选择,都不需要更改交换机上的配置。

在这些博客系列中可以找到许多有关测试 VLAN 的有趣文档(尤其是第四部分,即使一些信息可能不完全准确):

在未命名的 Linux 网络命名空间中享受 veth 设备、Linux 网桥和 VLAN 的乐趣I II III IV V VI VII VIII

让我们放置两个简约的防火墙模型(在网络命名空间中)。trunk100trunk200链接到两台交换机,从左侧计算机发送 VLAN 100 标记数据包,从右侧计算机发送 VLAN 200 标记数据包。请注意,此处明确允许 VLAN 标记出现在另一侧,方法是使用另一侧 VLAN id 创建子接口,或者直接将另一侧 VLAN id 添加到中继接口。

  1. vlan 子接口将未标记的数据包放入网桥中

    ip link add fw0 type bridge vlan_filtering 1
    ip link set fw0 up
    for trunk in 100 200; do
        for vlan in 100 200; do
            ip link add link trunk$trunk name trunk$trunk.$vlan type vlan id $vlan
            ip link set trunk$trunk.$vlan master fw0
            bridge vlan add vid $vlan pvid untagged dev trunk$trunk.$vlan
            bridge vlan del vid 1 dev trunk$trunk.$vlan
            ip link set trunk$trunk.$vlan up
        done
    done
    bridge vlan del vid 1 dev fw0 self
    
    Run Code Online (Sandbox Code Playgroud)

    对于这种情况,通过 trunk100 和 trunk200 到达的标记数据包将在每个 vlan 子接口中拆分,并且数据包未标记。网桥仍然在内部了解正在使用的 VLAN,并在源和目标上应用 VLAN 过滤。nft将添加自己的限制。传出数据包到达父中继接口后将被重新标记。

  2. 带标签的数据包直接进入网桥

    ip link add fw0 type bridge vlan_filtering 1
    ip link set fw0 up
    for trunk in 100 200; do
        ip link set trunk$trunk master fw0
        for vlan in 100 200; do
            bridge vlan add vid $vlan tagged dev trunk$trunk
        done
        bridge vlan del vid 1 dev trunk$trunk
        ip link set trunk$trunk up
    done
    bridge vlan del vid 1 dev fw0 self
    
    Run Code Online (Sandbox Code Playgroud)

对于这种更简单的情况,带标记的数据包会穿过网桥,同时保留其 VLAN 标记。

这是一个 nftables 规则集,显示了如何处理这两种情况。iifname在这里选择而不是iif因此同一组规则可以在两种情况下工作(不会因缺少接口而出现错误)。一般情况iif下应优先考虑。还有额外的计数器条目只是为了检查到底匹配或不匹配的内容(与nft list ruleset -a):

#!/usr/sbin/nft -f

flush ruleset

table bridge filter {
    chain input {
        type filter hook input priority -200; policy drop;
    }

    chain forward {
        type filter hook forward priority -200; policy drop;
        counter
        arp operation request counter
        arp operation reply counter
        vlan type arp arp operation request counter
        vlan type arp arp operation reply counter
        arp operation request counter accept
        arp operation reply counter accept
        vlan type arp arp operation request counter accept
        vlan type arp arp operation reply counter accept
        ip protocol icmp icmp type echo-request counter
        ip protocol icmp icmp type echo-reply counter
        vlan type ip icmp type echo-request counter
        vlan type ip icmp type echo-reply counter
        iifname trunk100.100 ip protocol icmp icmp type echo-request counter accept
        oifname trunk100.200 ip protocol icmp icmp type echo-reply counter accept
        vlan id 100 vlan type ip icmp type echo-request counter accept
        vlan id 200 vlan type ip icmp type echo-reply counter accept
    }

    chain output {
        type filter hook output priority 200; policy drop;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这些规则本来可以写得更详细。例子:

iifname "trunk100.100" ether type ip ip protocol icmp icmp type echo-request
Run Code Online (Sandbox Code Playgroud)

或者

ether type vlan vlan id 200 vlan type ip ip protocol icmp icmp type echo-reply
Run Code Online (Sandbox Code Playgroud)

当使用第一个设置时(通过子接口的未标记数据包),只有经典规则才会匹配。当使用第二个设置时,只有明确使用 vlan 的规则才会匹配。因此,这组双重规则(允许基本 ARP 解析并允许 VLAN 100 ping VLAN 200,但反之则不然)在两种情况下都有效。

当与 CentOS 的 nftables v0.6(未在 CentOS 内核上测试)或当前的 nftables v0.8.3 一起使用时,这组规则应该有效。

目前已知的限制:

自 v0.8.3 起,Nftables 无法像 ebtables/iptables 交互那样使用 conntrack。似乎有相关计划,请参阅此 PDF:使用 nftables 进行桥接过滤。所以这使得有状态的规则很难实施。

另请注意,nftables 仍然存在(自 0.8.3 起)显示问题:如果未使用任何选项,nft list ruleset -a将从“反编译”规则中删除。vlan例如,这两条规则:

nft add rule bridge filter forward ip protocol icmp counter
nft add rule bridge filter forward vlan type ip ip protocol icmp counter
Run Code Online (Sandbox Code Playgroud)

当使用nft list ruleset -a(v0.8.3) 显示回来时:

        ip protocol icmp counter packets 0 bytes 0 # handle 23
        ip protocol icmp counter packets 0 bytes 0 # handle 24
Run Code Online (Sandbox Code Playgroud)

只有这样nft --debug=netlink list ruleset -a才会转储字节码,很明显,这确实是两个不同的规则(这里的数据采用小端字节序):

bridge filter forward 23 22 
  [ payload load 2b @ link header + 12 => reg 1 ]
  [ cmp eq reg 1 0x00000008 ]
  [ payload load 1b @ network header + 9 => reg 1 ]
  [ cmp eq reg 1 0x00000001 ]
  [ counter pkts 0 bytes 0 ]

bridge filter forward 24 23 
  [ payload load 2b @ link header + 12 => reg 1 ]
  [ cmp eq reg 1 0x00000081 ]
  [ payload load 2b @ link header + 16 => reg 1 ]
  [ cmp eq reg 1 0x00000008 ]
  [ payload load 1b @ network header + 9 => reg 1 ]
  [ cmp eq reg 1 0x00000001 ]
  [ counter pkts 0 bytes 0 ]
Run Code Online (Sandbox Code Playgroud)

CentOS的v0.6(在内核4.15上测试)也有其自己不同的“反编译”显示问题:

ip protocol icmp icmp type echo-request
Run Code Online (Sandbox Code Playgroud)

显示为:

icmp type echo-request counter
Run Code Online (Sandbox Code Playgroud)

如果按照 v0.6 中的方式尝试,则会出现语法错误(但在 v0.8.3 中没问题)。