
Minimale Unikernel-Firewall für QubesOS, die Netzwerkverkehr filtert, NAT implementiert und über Qubes DB und qrexec kommuniziert.
Ein Unikernel, der als QubesOS-ProxyVM ausgeführt werden kann und sys-firewall ersetzt. Er verwendet die mirage-qubes-Bibliothek, um die Qubes-Protokolle zu implementieren.
Siehe Ein Unikernel-Firewall für QubesOS für weitere Details.
Vorgefertigte Binärdateien sind auf der Releases-Seite verfügbar. Installationsanweisungen finden Sie im Abschnitt Deploy weiter unten.
Hinweis: Der zuverlässigste Weg zum Bauen ist die Verwendung von Docker oder Podman. Fedora 42 funktioniert hierfür gut, Debian 12 ebenfalls, aber du musst den Anweisungen auf docker.com folgen, um Docker zu erhalten (verwende nicht die Debian-Version).
Erstelle eine neue Fedora-42-AppVM (oder verwende eine vorhandene erneut). Erhöhe in den Einstellungen der Qube (Basic / Disk storage) die maximale Größe des privaten Speichers von den standardmäßigen 2048 MiB auf 8192 MiB. Öffne ein Terminal.
Klein dieses Git-Repository und führe das Skript build-with.sh mit docker oder podman als Argument aus (Hinweis: Der chcon-Aufruf ist auf Fedora mit neuen SELinux-Richtlinien obligatorisch, die es nicht standardmäßig erlauben, die Docker-Images im Home-Verzeichnis zu behalten):
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
Oder
sudo systemctl start podman
git clone https://github.com/mirage/qubes-mirage-firewall.git
cd qubes-mirage-firewall
./build-with.sh podman
Das dauerte auf meinem Laptop etwa 15 Minuten (beim erneuten Ausführen wird es viel schneller gehen). Der Symlink-Schritt am Anfang ist nicht erforderlich, wenn deine Build-VM standalone ist. Er gibt Docker mehr Speicherplatz und verhindert, dass der Docker-Image-Cache beim Neustart der Qube verloren geht. Bei Podman ist er nicht erforderlich, da die Container standardmäßig in deinem Home-Verzeichnis liegen.
Hinweis: Die Objektdateien werden im Verzeichnis _build gespeichert, um inkrementelle Builds zu beschleunigen.
Wenn du die Abhängigkeiten änderst, musst du dieses Verzeichnis vor dem erneuten Erstellen löschen.
Es ist in Ordnung, das Docker- oder Podman-Paket in einer Template-VM zu installieren, wenn du möchtest, dass es nach einem Neustart erhalten bleibt, aber der Build der Firewall selbst sollte in einer regulären AppVM durchgeführt werden.
Du kannst auch ohne dieses Skript bauen, wie bei jedem normalen Mirage-Unikernel; Einzelheiten findest du in den Mirage-Installationsanweisungen.
Das Build-Skript legt die Versionen der verwendeten Bibliotheken fest und stellt sicher, dass du exakt dieselbe Binärdatei erhältst, die im Release enthalten ist. Wenn du ohne das Skript baust, wird stattdessen gegen die neuesten Versionen gebaut (und der Hash wird daher wahrscheinlich nicht übereinstimmen). Es sollte aber trotzdem einwandfrei funktionieren.
Wenn du manuell bereitstellen möchtest, musst du nur qubes-firewall.xen und qubes-firewall.sha256 in die domU herunterladen und prüfen, ob die .xen-Datei einen entsprechenden Hashwert besitzt. qubes-firewall.xen ist der Unikernel selbst und sollte in /var/lib/qubes/vm-kernels/mirage-firewall in dom0 nach vmlinuz kopiert werden, z. B. (wenn dev die AppVM ist, in der du ihn erstellt hast):
[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
Führe diesen Befehl in dom0 aus, um eine mirage-firewall-VM mit dem oben hinzugefügten mirage-firewall-Kernel zu erstellen:
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
Wenn du weißt, wie man Salt-States in Qubes ausführt, kannst du auch das Skript SaltScriptToDownloadAndInstallMirageFirewallInQubes.sls verwenden, um die neueste Version der Mirage-Firewall automatisch in deinem Qubes OS bereitzustellen. Eine Einführung findest du hier und hier. Wenn du den Anweisungen des ersten Links folgst, kannst du das Skript in dom0 mit dem Befehl sudo qubesctl --show-output state.apply SaltScriptToDownloadAndInstallMirageFirewallInQubes saltenv=user ausführen. Das Skript prüft die Prüfsumme vom Integrationsserver und vergleicht sie mit der neuesten Version, die in den GitHub-Releases bereitgestellt wird. Möglicherweise ist es notwendig, die VM-Templates im Skript anzupassen, die zum Herunterladen des Mirage-Unikernels verwendet werden, falls deine Standard-Templates die Werkzeuge curl und tar nicht standardmäßig installiert haben. Vergiss außerdem nicht, die VMs zu ändern, in denen der Unikernel verwendet werden soll, oder die „Qubes Global Settings“ anzupassen.
Um von einer früheren Version zu aktualisieren, überschreibe einfach /var/lib/qubes/vm-kernels/mirage-firewall/vmlinuz mit der neuen Version und starte die Firewall-VM neu.
Du kannst mirage-firewall neben deiner vorhandenen sys-firewall ausführen und über die GUI auswählen, welche AppVMs welche Firewall verwenden.
Um eine AppVM für die Verwendung zu konfigurieren, gehe in der GUI zu den Einstellungen der AppVM und ändere ihre NetVM von default (sys-firewall) auf mirage-firewall.
Du kannst es auch konfigurieren, indem du diesen Befehl in dom0 ausführst (ersetze my-app-vm durch den Namen der AppVM):
qvm-prefs --set my-app-vm netvm mirage-firewall
Alternativ kannst du mirage-firewall als deine Standard-Firewall-VM konfigurieren.
Beachte, dass dom0 standardmäßig sys-firewall als „UpdateVM“ verwendet (einen Proxy zum Herunterladen von Updates). mirage-firewall kann dafür nicht verwendet werden, aber jede Linux-VM sollte geeignet sein. https://www.qubes-os.org/doc/software-update-dom0/ sagt:
Die Rolle der UpdateVM kann jeder VM im Qubes-VM-Manager zugewiesen werden, und diese Wahl hat keine signifikanten Sicherheitsauswirkungen. Standardmäßig wird diese Rolle der FirewallVM zugewiesen.
OpenBSD kann derzeit nicht als NetVM verwendet werden. Wenn du also ein BSD als deine sys-net-VM verwenden möchtest, musst du dessen NetVM auf qubes-mirage-firewall setzen (weitere Informationen unter https://github.com/mirage/qubes-mirage-firewall/issues/146). Das bedeutet, du wirst AppVMs -> qubes-mirage-firewall <- OpenBSD haben, wobei der Pfeil für die NetVM-Eigenschaft steht.
In diesem Fall musst du qubes-mirage-firewall mitteilen, welcher AppVM-Client als Uplink verwendet werden soll:
qvm-prefs --set mirage-firewall -- kernelopts '--ipv4=X.X.X.X --ipv4-gw=Y.Y.Y.Y'
wobei X.X.X.X die IP-Adresse für mirage-firewall und Y.Y.Y.Y die IP-Adresse deiner OpenBSD-HVM ist.
Dieses Diagramm zeigt die Hauptkomponenten (jede Box entspricht einer Quelldatei .ml mit demselben Namen):
Ethernet-Frames kommen von Client-Qubes (wie work oder personal) oder von sys-net.
Internet-IP-Pakete werden an firewall gesendet, das die NAT-Tabelle und die Regeln aus der QubesDB konsultiert, um zu entscheiden, was mit dem Paket geschehen soll.
Wenn es weitergesendet werden soll, verwendet es router, um es an das gewählte Ziel zu senden.
client_net überwacht die von dom0 bereitgestellte XenStore-Datenbank, um herauszufinden, wann Clients hinzugefügt oder entfernt werden müssen.
Der Bootvorgang:
config.ml beschreibt die verwendeten Bibliotheken und statischen Konfigurationseinstellungen (NAT-Tabellengröße).
Das mirage-Werkzeug verwendet dies, um main.ml zu generieren.main.ml initialisiert die von config.ml ausgewählten Treiber
und ruft die Funktion start in unikernel.ml auf.unikernel.ml verbindet die Qubes-Agenten, richtet die Netzwerkkomponenten ein
und wartet dann auf eine Herunterfahranforderung.Verwende für die Entwicklung die test-mirage-Skripte, um den Unikernel (qubes-firewall.xen) aus deiner Entwicklungs-AppVM bereitzustellen.
Das erfordert beim ersten Mal etwas mehr Einrichtung, ist danach aber viel schneller. z. B.
[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 %)
Im Unterverzeichnis test/ ist ein Unikernel verfügbar, der die Firewall testet. Um ihn zu verwenden, führe test.sh aus und folge den Anweisungen, um die Testumgebung einzurichten.
Sicherheitshinweise, die die Firewall betreffen, findest du unter Issues mit dem Tag „security“.
Siehe LICENSE.md