This guide adapts Opensnitch Nodes for Qubes OS from scratch. One offline "Server" qube running the Opensnitch GUI can filter the traffic of multiple "Node" qubes, the latter requiring only the Opensnitch daemon service. Communication is achieved by way of the qubes.ConnectTCP
service. The particular setup described below maintains compartmentalization at the expense of GUI effectiveness, so may not be the ideal setup for all use cases. It is nevertheless effective as an interactive filter.
an application firewall that allows for interactive outbound connections filtering. While the Opensnitch GUI is available for individual qubes, Opensnitch offers a server(GUI)/node(daemon) setup that is potentially better suited for filtering traffic originating from multiple qubes simultaneously.
If you only want a single GUI Server qube, it's worth considering the latest release of Opensnitch in a Fedora minimal standalone. We'll employ the Debian minimal template here.
in dom0 terminal:
$ sudo qubesctl --skip-dom0 --targets=debian-12-minimal --show-output state.sls update.qubes-vm
$ qvm-clone debian-12-minimal <DEBIAN-12-SERVER-TEMPLATE>
<ALLCAPS>
indicates one is free to choose, in this case the name of the template.
Newer releases are available on Github, including rpm packages, but we'll install the older version available in the Debian stable repos. Once installed, Opensnitch will run automatically on startup, so it's necessary to disable Opensnitch in the template and later enable it to run automatically in the Server and Node qubes.
in dom0 terminal:
$ qvm-run -u root --pass-io --no-gui <DEBIAN-12-SERVER-TEMPLATE> 'apt install opensnitch -y'
$ qvm-run -u root --pass-io --no-gui <DEBIAN-12-SERVER-TEMPLATE> 'systemctl stop opensnitch && systemctl disable opensnitch'
Restart the template. You should see a grey cloud icon appear in the system tray. A black cloud would indicate that Opensnitch is enabled, which is undesireable in the template.
In the template, modify Opensnitch daemon config files to listen on port 50051, rather than the default, which is to filter traffic originating from the Server qube itself. The configuration also defaults to iptables
, which should be changed to nftables
as of Qubes OS R4.2. These changes will be inherited by the GUI Server qube.
in <debian-12-server-template> root terminal:
# sed -i 's/iptables/nftables/g' /etc/opensnitchd/default-config.json
# sed -i 's/unix:\/\/\/tmp\/osui.sock/[::]:50051/g' /etc/opensnitchd/default-config.json
The GUI Server qube does not require network access, so assign it a null netvm
.
in dom0 terminal:
$ qvm-create --template <DEBIAN-12-SERVER-TEMPLATE> --label <COLOR> <GUI-SERVER-QUBE>
$ qvm-prefs <GUI-SERVER-QUBE> netvm ""
Since Opensnitch is disabled in the template, we have to reenable it in the app qube.
We also want the GUI to listen for the Node qubes. In the GUI config file, insert a line to specify a value for the variable server_address
under [global]
. In this case (below), we find that the end of the [global]
table is at line 10, so we insert it there (ymmv). This modification should tell the GUI to listen for Node qubes on port 50051.*
in <gui-server-qube> root terminal:
# echo "systemctl enable --now opensnitch" >> /rw/config/rc.local
# less -N /home/user/.config/opensnitch/settings.conf
# sed -i '10i server_address=[::]:50051' /home/user/.config/opensnitch/settings.conf
Despite our config change, the server address will still default to
unix:///tmp/osui.sock
, as hard coded in theopensnitch-ui
. A greyed out Opensnitch icon in the sys tray will result. It's possible that we have not specified theserver_address
variable correctly, but I was unable to find example (s) in Github. I'll happily update this guide if anyone can identify a working specification forserver_address
, but until I have a fix, the next step will at least get it working.
Modify the executable in the template (modification in the GUI Server qube will not persist).
in <debian-12-server-template> root terminal:
# sed -i 's/unix:\/\/\/tmp\/osui.sock/[::]:50051/g' /usr/bin/opensnitch-ui
The Opensnitch GUI Server should now behave as expected on reboot, so we just need to set up our upstream app qubes to act as Opensnitch nodes.
in dom0 terminal:
$ sudo qubesctl --skip-dom0 --targets=debian-12-xfce --show-output state.sls update.qubes-vm
$ qvm-clone debian-12-xfce <DEBIAN-12-NODE-TEMPLATE>
We can use the no-install-recommends
flag to install the Opensnitch daemon without the GUI and its dependencies.
in dom0 terminal:
$ qvm-run -u root --pass-io --no-gui <DEBIAN-12-NODE-TEMPLATE> 'apt install --no-install-recommends opensnitch -y'
$ qvm-run -u root --pass-io --no-gui <DEBIAN-12-NODE-TEMPLATE> 'systemctl stop opensnitch && systemctl disable opensnitch'
In the template, modify the Opensnitch daemon config files to use nftables
and to use the centralized GUI Server as the interactive filter. These changes will be inherited by the disposable template (or app qubes).
in <debian-12-node-template> root terminal:
# sed -i 's/iptables/nftables/g' /etc/opensnitchd/default-config.json
# sed -i 's/unix:\/\/\/tmp\/osui.sock/localhost:50051/g' /etc/opensnitchd/default-config.json
in dom0 terminal:
$ qvm-create --template <DEBIAN-12-NODE-TEMPLATE> --label <COLOR> <DVM-NODE-TEMPLATE>
$ qvm-prefs <DVM-NODE-TEMPLATE> template_for_dispvms True
$ qvm-prefs <DVM-NODE-TEMPLATE> netvm <NETVM>
$ qvm-features <DVM-NODE-TEMPLATE> appmenus-dispvm 1
Refreshing applications under the Applications tab of the Settings Manager will allow you to show selected applications in the <dvm-node-template>
app menu.
Allow the (disposable) Node qubes to communicate on port 50051 at startup. Note, the RPC service qubes.ConnectTCP
does not require networking between qubes.(2)
in <dvm-node-template> root terminal:
# echo "qvm-connect-tcp ::50051" >> /rw/config/rc.local
# echo "systemctl enable --now opensnitch" >> /rw/config/rc.local
<dvm-node-template>
.
Allow communication between the GUI Server and (disposable) Node qubes by tagging <dvm-node-template>
and establishing the minimal necessary qubes RPC policy.
in dom0 terminal:
$ qvm-tags <DVM-NODE-TEMPLATE> add snitch
$ echo "qubes.ConnectTCP +50051 @tag:snitch @default allow target=<GUI-SERVER-QUBE>" >> /etc/qubes/policy.d/30-user.policy
<gui-server-qube>
and filter traffic whenever a disposable Node qube is spawned from <dvm-node-template>
.
Rules generated in the GUI Server are saved and enforced locally in /etc/opensnitchd/rules/
of the disposable client Node. Hence, these rules will be lost when the disposable is closed. To preserve these rules, and lessen the human interaction with Opensnitch, they should be copied to <dvm-node-template>
. To make them available in the Node qubes, create a new directory in the disposable template where the JSON files can persist and have them automatically copied into the default rules path.(1)
in <dvm-node-template>
# mkdir -p /rw/config/opensnitchd/rules
# echo "sudo cp /rw/config/opensnitchd/rules/* /etc/opensnitchd/rules/" >> /rw/config/rc.local
(1)
Syncthing, or similar, might be helpful here. Opensnitch releases after v1.6.5 allow modification of the rules path via the config file, so one could simply use/rw/config/opensnitchd/rules/
. Since Debian Bookworm is on an older release, copying the rules into the default location will at least allow one to edit the rules in the GUI.
(2)
Use of thequbes.ConnectTCP
service in multiple Node qubes appears to confuse the GUI a bit. The GUI will only list the last node opened and if that disposable is closed, it will treat all nodes as offline, which causes the GUI to not display any new events. Nevertheless, the Opensnitch daemon(s) will continue to filter as expected.
(3)
The node for each rule will be listed as thelocalhost
. As a result, the rules created by any open Node qube will be applied to every other Node qube.
(4)
The rules are editable in the GUI, but within the GUI there will be some ambiguity about the identity of the Node qube that originated the rule and hence where any local changes will be applied. However, once saved, the changes will be reflected immediately in the JSON files.
(5)
Not sure if (4) matters in practice given (3).
While this setup is a good proof of concept, there appears to be some room for improvement, or at least alternative tradeoffs between GUI function and compartmentalization between the GUI Server qube and the Node qubes. GUI identification of nodes according to each qube's
actual IP address
, rather than lumping them all together as127.0.0.1
would be a significant improvement, but presumably at some cost... Any helpful feedback is appreciated.