Firewall-up more of your qubes with likeafox's Dynamic Firewall VM

If you’ve configured firewall settings in QubesOS, you’ve probably bumped into its limitations. Many sites aren’t usable with the firewall because they pull resources from too many domains, or the domains don’t resolve to the same IPs reliably (they might use round-robin DNS, or resolve differently depending on what region your VPN is connecting from, or who-knows-what-else). Sometimes we’re forced to turn off the firewall to keep the network usable.

But is there a way to compromise between firewall-on (unreliable usability, high security) and firewall-off (full usability, no security)? A firewall that dynamically updates its rules (ex. by fetching rules from the internet) provides a “better than nothing” approach in cases where a strict firewall is unviable.

Installation

Create a new App qube. Set its network connection to sys-net, and check “Provides network access”. These instructions assume the qube is named “dynamic-firewall” and assume using a Debian 12 template. Fedora should be fine too but has not been tested as much.

Before starting up the new qube, open up the template and make sure required packages are installed:

sudo apt update
sudo apt install qubes-core-admin-client git
sudo shutdown now

Then in the dynamic-firewall qube

sudo su -l
cd /rw
git clone https://github.com/likeafox/dynamic-firewall.git
echo "/rw/dynamic-firewall/install.sh" >> /rw/config/rc.local
./dynamic-firewall/install.sh

Pay attention to the output of the final ./dynamic-firewall/install.sh, as it should print out something like “installed OK” at the end. If not, you might have a problem.

Finally, dynamic-firewall will need RPC permissions to query clients, so in dom0 make the file /etc/qubes/policy.d/50-dynamic-firewall.policy with these contents:

admin.vm.CurrentState  * dynamic-firewall  @tag:dynamic-firewall-user  allow target=@adminvm
admin.vm.property.List * dynamic-firewall  @tag:dynamic-firewall-user  allow target=@adminvm
admin.vm.property.Get  * dynamic-firewall  @tag:dynamic-firewall-user  allow target=@adminvm
admin.vm.firewall.Get  * dynamic-firewall  @tag:dynamic-firewall-user  allow target=@adminvm

(If you named the qube something other than “dynamic-firewall” then modify this file accordingly)

Adding clients

Say you want to attach your qube named “programming” to dynamic-firewall.

  1. In dom0, add the tag that will allow dynamic-firewall to query it
qvm-tags programming add dynamic-firewall-user
  1. Change programming’s netvm
qvm-prefs programming netvm dynamic-firewall
  1. In, dynamic-firewall, modify /rw/dynamic-firewall/clients file, adding the line:
programming  github

Your programming qube will now be able to connect to all github servers.

Clients also retain all firewall rules you configured in the Qube Manager’s firewall tab. So you can mix allowing site profiles (in the clients file) with allowing specific IPs/domains (in Qubes firewall settings).

Full documentation of the clients file

  • Lines are ignored if they are empty, contain only whitespace, or begin with #.
  • The first word of a line is the qube name.
  • Subsequent words specify site profiles that are enabled for that qube.
  • Words can be separated by an arbitrary amount of space

Example:

# qube-name       profiles
programming       github  pypi
studying          wikipedia

This configures dynamic-firewall for two client qubes. programming has access to github and pypi, while studying has access to wikipedia.

Site profiles

A site profile is the entire dynamic ruleset that comprises full access to a particular website/service.

At time of writing there are only 4 site profiles (and 2 aliases) that are able to be specified:

fastly
github
google
pypi
wikipedia
youtube

You can add more by editing sites.py. The file is meant to be easy to modify if you have the requisite programming knowledge.

programming note: refresh functions return a list of addresses in either plain or cidr format (ex. ["10.12.0.2", "192.168.0.0/24"]), or False if the address list should not be updated.

Questions for improvements

For now it only supports IPv4. What do you think, is there a compelling reason to add IPv6 support right now?

Is there a way to make RPC policy management easier? Afaik, there is no way to specify something like @netvm-client as a class in a policy file (that would be very nice), and so we need to use tags instead.

1 Like

Looks like it just parses public IP data from these services for the firewall.

That’s overly complicated, not generally applicable and raises privacy concerns as it will contact these providers.

Just do DNS pinning and you’re done for all sites without privacy concerns.

I posted that solution years ago on github [1] and also mentioned it on qubes-issues [2].

It was not adopted though as the Qubes OS devs don’t like the increased attack surface of a local DNS server/cache, which is understandable. I guess one could put it inside a dedicated VM, but oh well.

[1] GitHub - 3hhh/qubes-dns: DNS VM helper scripts
[2] Idea: dynamic firewall · Issue #5225 · QubesOS/qubes-issues · GitHub

It’s not generally applicable, because it’s not meant to be. It’s not a replacement for the Qubes firewall. What it is, is what it says: A way to get more of your qubes behind a firewall when you’d otherwise have them unfirewalled.

Many of my qubes use the Qubes firewall only. And then for instance I have a qube attached to dynamic-firewall that I use solely to access my google account, which lets me go on youtube, and be able to see and contribute to google docs that people share with me.

That’s what happens in the 4 examples provided but it need not be limited to that. The point is that it’s flexible. You could have a site profile that downloads a webpage and parses it for certain resources (scripts, images) and adds their url domains to the fw. You could have a site profile that is entirely static but containing a hundred entries, and so is way more convenient to add/remove for a client than doing it from Qubes interface.

That’s neat. I’d made a note for myself to add similar functionality to my dynamic firewall.

snoop on client DNS requests, check if that domain should be allowed by any profile/fw rule. If so, add it to a client-dns chain of our firewall. That way, variable dns resolution will never pose a problem for the firewall (as long as the client doesn’t go oob using firefox dns-over-https feature, which can’t be intercepted)

But to be clear, this approach != done for all sites. It only helps in cases where you know the full list of domain names in advance, the list doesn’t change, and the client doesn’t go out-of-band to lookup dns.

I think yours works by just adding resolved addresses to the firewall vm’s hosts file? Is that what the Qubes team considers to be a DNS cache, that they didn’t like?

On privacy:

I see that as a valid opinion, but for some reason fetching IP lists for servers you’re planning to make DNS/HTTPS requests to anyway doesn’t bother me very much (isn’t that essentially what DNS is about anyway?). It can have a privacy benefit too, since a qube never has to generate unencrypted DNS queries for sub-sites within the allowed IP ranges.

You could also say the dynamic-firewall has more information than sys-firewall and so a compromise of dynamic-firewall, which is also more likely to be compromised, has the potential to leak more information. Not a lot more, but it’s there. That would be fair too.

So while I get that there is some privacy concern, I don’t think it is anything egregious, and we have to recognize every approach has its trade-offs, which can be well worth it.

I would rather use a web proxy (running in the firewall VM) - if i would want to limit the VMs internet access…

Yes, proxies are another option. In particular SOCKS proxies were discussed by the Qubes devs.

They have the advantage of being able to allow single websites in contast to thousands per IP (or magnitudes of that if you allow the entire google range) at the cost of an even bigger attack surface (I don’t think there’s a corporate proxy provider out there which didn’t have a RCE in their product). They also tend to require explicit client support.