一个可以以 QubesOS ProxyVM 运行的单内核(unikernel),用于替代 sys-firewall。
它使用 [mirage-qubes][] 库来实现 Qubes 协议。
更多细节请参见 [A Unikernel Firewall for QubesOS][]。
预编译的二进制文件可从 [releases page][] 获取。 安装说明请参见下面的 Deploy 部分。
注意:最可靠的构建方式是使用 Docker 或 Podman。 Fedora 42 对此支持良好,Debian 12 也可以,但你需要按照 [docker.com][debian-docker] 上的说明来安装 Docker (不要使用 Debian 自带的版本)。
创建一个新的 Fedora-42 AppVM(或复用现有的一个)。在该 Qube 的设置(Basic / Disk storage)中,将私有存储的最大大小从默认的 2048 MiB 增加到 8192 MiB。打开终端。
克隆此 Git 仓库并运行 build-with.sh 脚本,参数为 docker 或 podman(注意:在 Fedora 上,由于新的 SELinux 策略不允许将 Docker 镜像默认存放在主目录中,因此 chcon 调用是必需的):
mkdir /home/user/docker
sudo ln -s /home/user/docker /var/lib/docker
sudo chcon -Rt container_file_t /home/user/docker
sudo dnf install docker
sudo systemctl start docker
git clone https://github.com/mirage/qubes-mirage-firewall.git
cd qubes-mirage-firewall
sudo ./build-with.sh docker
或者
sudo systemctl start podman
git clone https://github.com/mirage/qubes-mirage-firewall.git
cd qubes-mirage-firewall
./build-with.sh podman
在我的笔记本上这大约需要 15 分钟(如果你再次运行会快很多)。 如果你的构建 VM 是 Standalone 类型,则开头的符号链接步骤不是必需的。它能为 Docker 提供更多磁盘空间,并避免在重启 Qube 时丢失 Docker 镜像缓存。 使用 Podman 时则不需要,因为容器默认存放在你的主目录中。
注意:目标文件存储在 _build 目录中,以加快增量构建。
如果你更改了依赖项,需要在重新构建之前删除该目录。
如果你希望在模板 VM 中安装 Docker 或 Podman 包以便重启后仍然保留,也是可以的,但防火墙本身的构建应在普通的 AppVM 中进行。
你也可以不借助该脚本进行构建,就像构建任何普通的 Mirage 单内核一样; 详情请参阅 Mirage 安装说明。
构建脚本会固定所使用的库的版本,确保你得到与发布版本完全相同的二进制文件。如果你不使用该脚本构建,则会改用最新版本进行构建(因此哈希值可能不匹配)。 不过,它应该仍然可以正常工作。
如果你想手动部署,只需在 domU 中下载 qubes-firewall.xen 和
qubes-firewall.sha256,并检查 .xen 文件具有相应的
哈希值。qubes-firewall.xen 就是单内核本身,应复制到
dom0 中 /var/lib/qubes/vm-kernels/mirage-firewall 目录下的 vmlinuz,例如
(如果 dev 是你构建它的 AppVM):
[tal@dom0 ~]$ mkdir -p /var/lib/qubes/vm-kernels/mirage-firewall/
[tal@dom0 ~]$ cd /var/lib/qubes/vm-kernels/mirage-firewall/
[tal@dom0 mirage-firewall]$ qvm-run -p dev 'cat mirage-firewall/qubes-firewall.xen' > vmlinuz
在 dom0 中运行此命令,使用你上面添加的 mirage-firewall 内核创建一个 mirage-firewall VM
qvm-create \
--property kernel=mirage-firewall \
--property kernelopts='' \
--property memory=32 \
--property maxmem=32 \
--property netvm=sys-net \
--property provides_network=True \
--property vcpus=1 \
--property virt_mode=pvh \
--property audiovm='' \
--label=green \
--class StandaloneVM \
mirage-firewall
qvm-features mirage-firewall qubes-firewall 1
qvm-features mirage-firewall no-default-kernelopts 1
qvm-features mirage-firewall skip-update 1
如果你熟悉如何在 Qubes 中运行 salt 状态,你也可以使用脚本 SaltScriptToDownloadAndInstallMirageFirewallInQubes.sls 自动在你的 Qubes OS 中部署最新版本的 mirage 防火墙。入门介绍可以在 这里 和 这里 找到。按照前一个链接中的说明,你可以在 dom0 中使用命令 sudo qubesctl --show-output state.apply SaltScriptToDownloadAndInstallMirageFirewallInQubes saltenv=user 运行该脚本。该脚本会检查集成服务器的校验和,并与 github releases 中提供的最新版本进行比较。如果用于下载 mirage 单内核的默认模板未预装 curl 和 tar 工具,则可能需要调整脚本中的 VM 模板。另外,不要忘记更改应使用该单内核的 VM,或调整“Qubes Global Settings”。
要从旧版本升级,只需用新版本覆盖 /var/lib/qubes/vm-kernels/mirage-firewall/vmlinuz,然后重启防火墙 VM。
你可以将 mirage-firewall 与你现有的 sys-firewall 一起运行,并可以通过 GUI 选择哪些 AppVM 使用哪个防火墙。
要将 AppVM 配置为使用它,请在该应用 VM 的 GUI 设置中,将其 NetVM 从 default (sys-firewall) 更改为 mirage-firewall。
你也可以通过在 dom0 中运行以下命令来配置它(将 my-app-vm 替换为 AppVM 的名称):
qvm-prefs --set my-app-vm netvm mirage-firewall
或者,你可以将 mirage-firewall 配置为默认的防火墙 VM。
注意,默认情况下 dom0 使用 sys-firewall 作为其“UpdateVM”(用于下载更新的代理)。 mirage-firewall 不能用于此目的,但任何 Linux VM 都可以胜任。 https://www.qubes-os.org/doc/software-update-dom0/ 中提到:
UpdateVM 的角色可以在 Qubes VM Manager 中分配给任何 VM,而且这种选择没有重大的安全影响。默认情况下, 此角色分配给 firewallvm。
OpenBSD 目前无法用作 netvm,因此如果你想将 BSD 作为你的 sys-net VM,你需要将其 netvm 设置为 qubes-mirage-firewall(更多信息请参见 https://github.com/mirage/qubes-mirage-firewall/issues/146)。
这意味着你将拥有 AppVMs -> qubes-mirage-firewall <- OpenBSD,其中箭头表示 netvm 属性设置。
在这种情况下,你必须告诉 qubes-mirage-firewall 哪个 AppVM 客户端应作为上行链路:
qvm-prefs --set mirage-firewall -- kernelopts '--ipv4=X.X.X.X --ipv4-gw=Y.Y.Y.Y'
其中 X.X.X.X 是 mirage-firewall 的 IP 地址,Y.Y.Y.Y 是你的 OpenBSD HVM 的 IP 地址。
此图展示了主要组件(每个框对应一个同名 .ml 源文件):
以太网帧来自客户端 qubes(例如 work 或 personal)或来自 sys-net。
Internet(IP)数据包被发送到 firewall,后者查询 NAT 表和来自 QubesDB 的规则,以决定如何处理该数据包。
如果应该转发,它使用 router 将其发送到选定的目的地。
client_net 监视 dom0 提供的 XenStore 数据库,
以了解何时需要添加或删除客户端。
启动过程:
config.ml 描述所使用的库和静态配置设置(NAT 表大小)。
mirage 工具使用它来生成 main.ml。main.ml 初始化由 config.ml 选择的驱动程序,
并调用 unikernel.ml 中的 start 函数。unikernel.ml 连接 Qubes 代理,设置网络组件,
然后等待关闭请求。在开发时,请使用 [test-mirage][] 脚本从你的开发 AppVM 部署单内核(qubes-firewall.xen)。
第一次需要多一些设置,但之后会快得多。例如:
[user@dev ~]$ test-mirage dist/qubes-firewall.xen mirage-firewall
Waiting for 'Ready'... OK
Uploading 'dist/qubes-firewall.xen' (7454880 bytes) to "mirage-test"
Waiting for 'Booting'... OK
Connecting to mirage-test console...
Solo5: Xen console: port 0x2, ring @0x00000000FEFFF000
| ___|
__| _ \ | _ \ __ \
\__ \ ( | | ( | ) |
____/\___/ _|\___/____/
Solo5: Bindings version v0.7.3
Solo5: Memory map: 32 MB addressable:
Solo5: reserved @ (0x0 - 0xfffff)
Solo5: text @ (0x100000 - 0x319fff)
Solo5: rodata @ (0x31a000 - 0x384fff)
Solo5: data @ (0x385000 - 0x53ffff)
Solo5: heap >= 0x540000 < stack < 0x2000000
2022-08-13 14:55:38 -00:00: INF [qubes.rexec] waiting for client...
2022-08-13 14:55:38 -00:00: INF [qubes.db] connecting to server...
2022-08-13 14:55:38 -00:00: INF [qubes.db] connected
2022-08-13 14:55:38 -00:00: INF [qubes.db] got update: "/mapped-ip/10.137.0.20/visible-ip" = "10.137.0.20"
2022-08-13 14:55:38 -00:00: INF [qubes.db] got update: "/mapped-ip/10.137.0.20/visible-gateway" = "10.137.0.23"
2022-08-13 14:55:38 -00:00: INF [qubes.rexec] client connected, using protocol version 3
2022-08-13 14:55:38 -00:00: INF [unikernel] QubesDB and qrexec agents connected in 0.041 s
2022-08-13 14:55:38 -00:00: INF [dao] Got network configuration from QubesDB:
NetVM IP on uplink network: 10.137.0.4
Our IP on uplink network: 10.137.0.23
Our IP on client networks: 10.137.0.23
DNS resolver: 10.139.1.1
DNS secondary resolver: 10.139.1.2
2022-08-13 14:55:38 -00:00: INF [net-xen frontend] connect 0
2022-08-13 14:55:38 -00:00: INF [net-xen frontend] create: id=0 domid=1
2022-08-13 14:55:38 -00:00: INF [net-xen frontend] sg:true gso_tcpv4:true rx_copy:true rx_flip:false smart_poll:false
2022-08-13 14:55:38 -00:00: INF [net-xen frontend] MAC: 00:16:3e:5e:6c:00
2022-08-13 14:55:38 -00:00: INF [ethernet] Connected Ethernet interface 00:16:3e:5e:6c:00
2022-08-13 14:55:38 -00:00: INF [ARP] Sending gratuitous ARP for 10.137.0.23 (00:16:3e:5e:6c:00)
2022-08-13 14:55:38 -00:00: INF [ARP] Sending gratuitous ARP for 10.137.0.23 (00:16:3e:5e:6c:00)
2022-08-13 14:55:38 -00:00: INF [udp] UDP layer connected on 10.137.0.23
2022-08-13 14:55:38 -00:00: INF [dao] Watching backend/vif
2022-08-13 14:55:38 -00:00: INF [memory_pressure] Writing meminfo: free 20MiB / 27MiB (72.68 %)
一个用于测试防火墙的单内核位于 test/ 子目录中。
要使用它,请运行 test.sh 并按照说明设置测试环境。
影响防火墙的安全公告请参见 标签为 "security" 的 issues。
参见 LICENSE.md