我正在尝试通过混杂设备和连接的 macvtap 在单独的地址上向开放世界公开 libvirt (qemu) 虚拟机,但同时保护本地网络免受监听,例如通过禁止 192.168.0.0/16(特殊地址、广播/网关/dns 除外)。但是,看起来我的规则并不完全适用于子 macvtap 设备,即使规则集中优先级最低的推荐元跟踪标志也只标记 enp88s0 数据包,而不标记 macvtap0 数据包。是否可以使用 nftables 拦截 macvtap 流量?如果可以,怎么做?
答案1
这移动式真空泵接口,一种变体MACVLAN对于虚拟机,劫持发送到主机接口的帧,这些帧的目的地是虚拟机网卡的以太网 MAC 地址。同样,虚拟机发送的帧也会在主机的物理接口上向外注入。这通常也意味着虚拟机和主机无法直接通信(两者都会发送到外部而对方不会收到,除非使用 VEPA 模式的交换机,或者它们之间的流量被路由)。
由于虚拟机的流量既不是主机的路由堆栈:ip
//ip6
或inet
arp
nftables家庭,也不是主办桥梁:bridge
nftables家庭,在这些家庭中不会看到它(除inet ingress
下文外)。
netdev
,ingress
和egress
然而,仍有netdev
家族及其ingress
(egress
内核 >= 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。
清理工作留作一项练习(尽管当它们引用的接口消失时链也会消失,留下一个无害的空表)。