将 nftables 规则应用于 macvtap 接口

将 nftables 规则应用于 macvtap 接口

我正在尝试通过混杂设备和连接的 macvtap 在单独的地址上向开放世界公开 libvirt (qemu) 虚拟机,但同时保护本地网络免受监听,例如通过禁止 192.168.0.0/16(特殊地址、广播/网关/dns 除外)。但是,看起来我的规则并不完全适用于子 macvtap 设备,即使规则集中优先级最低的推荐元跟踪标志也只标记 enp88s0 数据包,而不标记 macvtap0 数据包。是否可以使用 nftables 拦截 macvtap 流量?如果可以,怎么做?

答案1

移动式真空泵接口,一种变体MACVLAN对于虚拟机,劫持发送到主机接口的帧,这些帧的目的地是虚拟机网卡的以太网 MAC 地址。同样,虚拟机发送的帧也会在主机的物理接口上向外注入。这通常也意味着虚拟机和主机无法直接通信(两者都会发送到外部而对方不会收到,除非使用 VEPA 模式的交换机,或者它们之间的流量被路由)。

由于虚拟机的流量既不是主机的路由堆栈:ip//ip6inetarp nftables家庭,也不是主办桥梁:bridge nftables家庭,在这些家庭中不会看到它(除inet ingress下文外)。

netdevingressegress

然而,仍有netdev家族及其ingressegress​​内核 >= 5.16)钩子可以看到此类流量。实际上,为了方便起见,该inet家族也有此类ingress钩子,仅限于 IPv4/IPv6(但没有等效egress钩子)。此处的ingress方向与实际接口相同:从“外部”到用户空间进程(QEMU)。egress是从用户空间进程到“外部”。

连接跟踪此级别将不可用:无法进行状态过滤。如果需要过滤两个方向(egress需要内核 >= 5.16 和最新nftables),或者过滤除IPv4/IPv6之外的其他协议(比如ARP),那么只有家庭netdev才能提供。

此外,这些钩子有些特殊:它们附加到一个或多个接口,并且定义链的语法反映了这一点:它必须包含关键字device(或devices =)和一个设备(或设备列表)。

因此,如果接口被命名macvtap0(但请参阅 Bonus 段落虚拟器集成),阻止 192.168.0.0/16 的规则集(除了 192.168.2.1)(并且不关心通过以太网 MAC 地址或 ARP 协议进行过滤,因为这只是一个例子)将如下所示(通常以太网层的过滤器的开始ether type ip是隐含的ip saddr和省略的):

table netdev filtermacvtap {
    chain filterin {
        type filter hook ingress device "macvtap0" priority filter; policy accept;
        ip saddr 192.168.2.1 accept
        ip saddr 192.168.0.0/16 drop

    }

    chain filterout {
        type filter hook egress device "macvtap0" priority filter; policy accept;
        ip daddr 192.168.2.1 accept
        ip daddr 192.168.0.0/16 drop
    }
}

如果使用的内核低于 5.16,则删除该filterout链(它可能没什么用)。

奖金:虚拟器一体化

这也意味着在创建 VM 的接口之前不能创建该规则集,因为设备引用必须事先存在。虚拟器提供钩子以帮助解决这个问题:

/etc/libvirt/hooks/qemu

[...]

第二个位置自 0.8.0 起可用,发生在 libvirt 完成标记所有资源但尚未启动客户机之后,称为:

/etc/libvirt/hooks/qemu guest_name start begin -

在...的帮助下xmlstarlet(该命令比 更难使用,xmllint但提供更好的输出,并且具有例如虚拟器的文档)可以从中检索动态创建的接口名称start begin 虚拟器钩子并将其用作适当的参数nftables规则集。

规则集文件/etc/filtermacvtap.nft

table netdev filtermacvtap        # for idempotence
delete table netdev filtermacvtap # for idempotence

table netdev filtermacvtap {
    chain filterin {
        type filter hook ingress device $intf priority filter; policy accept;
        ip saddr 192.168.2.1 accept
        ip saddr 192.168.0.0/16 drop

    }

    chain filterout {
        type filter hook egress device $intf priority filter; policy accept;
        ip daddr 192.168.2.1 accept
        ip daddr 192.168.0.0/16 drop
    }
}

完成后:

mkdir -p /etc/libvirt/hooks/qemu.d

并重新启动libvirtd;创建并使下面的文件可执行,该文件只会对名为 的 VM 触发myvm,并且仅在使用链接到 的 MACVTAP 接口时才有效enp88s0

/etc/libvirt/hooks/qemu.d/installfiltermacvtap.sh

#!/bin/sh

VM=myvm
LINKINTF=enp88s0

if [ "$1" = "$VM" ] && [ "$2" = start ] && [ "$3" = begin ]; then
    xmlstarlet select -t \
        -m 'domain/devices/interface[@type="direct"]' \
        -m "source[@dev='$LINKINTF']" \
        -m .. \
        -v 'target/@dev' \
    | {
        read intf
        nft -D intf="$intf" -f /etc/filtermacvtap.nft
    }
fi

enp88s0可以从中检索链接到实际接口的 MACVTAP 接口的名称虚拟器(VM 的 xmldump 可用作输入)并定义为$intf调用的参数nftables

清理工作留作一项练习(尽管当它们引用的接口消失时链也会消失,留下一个无害的空表)。

相关内容