不得不说,Linux 开发者都是一帮偏执的“天才”程序员。曾经有人提出过 dnsmasq 强制将 67 端口绑定至 0.0.0.0(所有网络界面),但是几位“天才”非要固执地说,这是为了负载平衡。结果,由 dnsmasq 魔改的 pihole-FTL,真的就和 libvirt 依赖的 dnsmasq 冲突了……
依照前人的解决办法,依然无法让 pi-hole 正常工作。在参阅了 pi-hole docker 文档和另一位前人的笔记之后,终于找到了目前为止最好的解决方案。
背景
libvirt 安装好之后,会默认使用 NAT 模式将虚拟机连接到网络上。NAT 会生成一个网桥,并在网桥里面启用 dnsmasq。
解决办法就是完全停用 libvirt 生成的网桥,然后在 Linux 系统里手工建立一个网桥,并将虚拟机挂载到 Linux 网桥上。
完全关闭 libvirt 里面的默认网桥
virsh net-destroy default
virsh net-autostart --disable default
启动 pi-hole docker
sudo docker run -d \
--name pihole \
...
-e DNSMASQ_LISTENING=all \
-e IPv6=false \
...
--cap-add=NET_ADMIN \
--net=host \
pihole/pihole:latest
其中,DNSMASQ_LISTENING=all
可以在网络管理页面里面设置 Settings -> DNS -> Interface settings -> Permit all origins
建立系统网桥
这个步骤在不同的 Linux 发行版并没有统一的操作步骤,取决于 Linux 发行版采用哪种网络管理方式。
我使用的是 Ubuntu 24.04 LTS,采用的是 NetworkManager + Netplan 来管理网络。
首先,打开 Network Manager。
nm-connection-editor &
点击左下角的 + 号,然后设备类型选择 Bridge(网桥)。
在新建网桥的窗口中,填写网桥名称,然后点 Add 向网桥中添加网卡。
弹出的窗口里面选好物理网卡,点击保存。
切换到 IPv4 选项卡,设置 IP。在这里我使用了固定 IP,如果不需要固定 IP 的话,可以在第 2 步选择 DHCP,然后忽略第 3-5 步。
最后,切换到 IPv6 选项卡,这里我关闭了 IPv6。然后点击保存。
这样,网桥就建好了。
将虚拟机直接挂载到网桥
在虚拟机描述文件(domain XML)里面使用下面的方式定义网络连接。
<interface type='bridge'>
<mac address='52:54:00:3c:ac:6c'/>
<source bridge='br0'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</interface>
在启动虚拟机之后,libvirt 会自动将虚拟机网卡加入到网桥中。