Host firewalls/VPNs: a solution

Hello! :smile:

So, one of the disadvantages of KVM as listed on the Wiki in Why use VirtualBox over KVM? is that it doesn’t work well with host firewalls or VPNs.

The root cause of this issue lies in how Libvirt’s Virtual Networking is implemented. The relevant detail here is that libvirt inserts iptables rules to make its virtual networks work, and these tend to clash with any existing rules.

Finally, after literally months of googling, learning and experimenting, I believe I’ve solved this problem! It’s not a particularly pleasant solution, but it works.

The solution

The basic idea is to manually set up the virtual networks, as documented in this guide:
Custom NAT-based network — libvirt Networking Handbook — Jamie Nguyen
It’s a bit out-of-date (a few steps are not required anymore), but the core idea remains the same.

The really tricky part was getting the specific firewall rules right. Debugging network issues without a proper understanding of Whonix’s network stack was hard enough; doing that while also trying to understand iptables for the first time was a challenge straight from hell :slight_smile:

I’ll list the steps required to get it working below. This will be a very minimal demonstration; I won’t implement any systemd services or scripts, I’ll just give you a set of commands that should make things work. Hopefully someone can polish this up and get it onto the Wiki - sorry, but after all the effort I put in here, I’m too tired to work on that as well :stuck_out_tongue:

Steps

I will use nftables for these steps, because it’s what I preferred to learn; in theory this can be adapted to iptables.

0. Destroy existing Whonix-* networks

Make sure you have a backup of Whonix_{internal,external}_network.xml (from the downloaded .tar.xz) for reference.

sudo virsh -c qemu:///system net-destroy Whonix-External
sudo virsh -c qemu:///system net-destroy Whonix-Internal
sudo virsh -c qemu:///system net-undefine Whonix-External
sudo virsh -c qemu:///system net-undefine Whonix-Internal
sudo nft flush ruleset # Clear all rules

1. Add the virtual bridges

sudo brctl addbr virbr1 # Whonix-External
sudo brctl addbr virbr2 # Whonix-Internal
sudo brctl set virbr1 on
sudo brctl set virbr2 on
sudo ip addr add 10.0.2.2/24 dev virbr1 # The address from Whonix_external_network.xml
sudo ip link set up virbr1
sudo ip link set up virbr2

2. Run a dnsmasq instance for virbr1 (Whonix-External)

sudo dnsmasq --except-interface=lo --interface=virbr1 --bind-dynamic --dhcp-range=10.0.2.2,10.0.2.254

3. Apply nftables rules

This is a template for a basic filter-by-default firewall; the goal is to demonstrate the minimum needed to make the virtual bridges work. You should be able to adapt this according to your needs.

Save the following rules in nftables.conf.


# delete table if exists
table inet filter
delete table inet filter

table inet filter {

    chain forward {
        type filter hook forward priority filter
        ###### change to 'accept' to accept everything #####
        policy drop

        ###### add your own rules here to allow types of traffic you want ######

        iifname "virbr1" accept comment "outgoing traffic from virbr1"
        oifname "virbr1" ct state established,related accept comment "established traffic to virbr1"
    }
}

table inet nat
delete table inet nat

table inet nat {

    chain postrouting {
        type nat hook postrouting priority srcnat
        policy accept

        ##### add any nat rules you already have here #####

        iifname virbr1 ip daddr { 224.0.0.0/24, 255.255.255.255/32 } return comment "no masquerading to reserved blocks"
        iifname virbr1 oifname != virbr1 masquerade comment "masquerade all outgoing traffic on virbr1"
    }
}

Now apply these rules with sudo nft -f nftables.conf.

At this stage you may also need to enable ipv4 forwarding if that isn’t already enabled:

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

4. Modify the VM configuration

Change the network source from ‘Whonix-External’ to the bridge ‘virbr1’, and from ‘Whonix-Internal’ to the bridge ‘virbr2’. You can do this by manually editing the XML (virsh edit Whonix-*), or via the virt-manager GUI.


And that’s it! Hopefully I didn’t forget anything.

Further information

Here’s how I figured out the nftables rules:

From this output, I was able to painstakingly assemble a sensible-seeming nftables configuration.


Anyway, I hope this is helpful! :smile:

Open topics this answers:

1 Like

hi

I want to set Mullvad VPN before Tor , and I need set VLESS protocol proxy

your topic can use to this ?

I first use whonix

If discussing this, please do not duplicate here and use:

So I can’t talk his topic in there ?

and that’s two things