Mozilla VPN as Proxy VM

Mozilla VPN as Proxy VM

Running current Qubes 4.1.1, all updates installed.

I try to use Mozilla VPN in ProxyVM qube. Under the hood Mozilla VPN uses MullatVPN and WireGuard, so I followed this guide: Using Mullvad VPN in Qubes and created the VM vpn-mozilla

I used a Ubuntu Jammy 22.04 template (taken from qubes.3isec.org/Templates/), created a Standalone Template (changes are persistent) with options set like the tutorial shows.

I then installed MozillaVPN following this: How to install Mozilla VPN on a Linux computer | Mozilla VPN Help

To authorize the user I replaced /usr/bin/qvm-open-in-dvm tempoarily with a script that contained:

firefox "${@}"

Authorize the user with:

mozillavpn login

The browser opens, you can log in and authorize the VPN client. So far so good, I can configure and start up the VPN using the mozillavpn CLI or UI. The nm-applet of vpn-mozilla shows a moz0 VPN configuration of type WireGuard.

A browser or terminal in vpn-mozilla can ping the internet (8.8.8.8) and is routed through the VPN. The virtual DNS address used by eth0 in vpn-mozilla cannot be reached after Mozilla VPN is activated:

user@vpn-mozilla:~$ ping -c 1 10.139.1.1
PING 10.139.1.1 (10.139.1.1) 56(84) bytes of data.

--- 10.139.1.1 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

Setting DNS to 8.8.8.8 for device eth0 does fix this issue:

user@vpn-mozilla:~$ nmcli d modify eth0 ipv4.dns 8.8.8.8
Connection successfully reapplied to device 'eth0'.

user@vpn-mozilla:~$ host linux.org
linux.org has address 188.114.96.7
linux.org has address 188.114.97.7
linux.org has IPv6 address 2a06:98c1:3121::7
linux.org has IPv6 address 2a06:98c1:3120::7
linux.org mail is handled by 10 mx1.improvmx.com.
linux.org mail is handled by 20 mx2.improvmx.com.

Problem: AppVMs using vpn-mozilla as NetVM does not use the Mozilla VPN

A AppVM using vpn-mozilla as net VM has normal internet access, but the traffic is not routed through the moz0 VPN tunnel (as local network traffic does in vpn-mozilla). Wireshark told me that the network traffic enters the vpn-mozilla VM via vifX:Y interface and is send to the internet directly via eth0. But local traffic in vpn-mozilla is poperly routed through the moz0 tunnel.

I guess there is not much missing, but my knowledge about the NetVM routing concept in QubesOS as well as about WireGuard and how it applies the routings is limited. I want to route all traffic comming from vif* interfaces into moz0 and not to eth0. Would be great if someone could give me some advice how to do so.

Thanks,
Sebastian

  • wireguard routing table
user@vpn-mozilla:~$ ip route list table 51820
0.0.0.0/5 dev moz0 
8.0.0.0/7 dev moz0 
10.64.0.1 dev moz0 
10.124.0.0/20 dev moz0 
11.0.0.0/8 dev moz0 
12.0.0.0/6 dev moz0 
16.0.0.0/4 dev moz0 
32.0.0.0/3 dev moz0 
64.0.0.0/2 dev moz0 
128.0.0.0/3 dev moz0 
160.0.0.0/5 dev moz0 
168.0.0.0/6 dev moz0 
172.0.0.0/12 dev moz0 
172.32.0.0/11 dev moz0 
172.64.0.0/10 dev moz0 
172.128.0.0/9 dev moz0 
173.0.0.0/8 dev moz0 
174.0.0.0/7 dev moz0 
176.0.0.0/4 dev moz0 
192.0.0.0/9 dev moz0 
192.128.0.0/11 dev moz0 
192.160.0.0/13 dev moz0 
192.169.0.0/16 dev moz0 
192.170.0.0/15 dev moz0 
192.172.0.0/14 dev moz0 
192.176.0.0/12 dev moz0 
192.192.0.0/10 dev moz0 
193.0.0.0/8 dev moz0 
194.0.0.0/7 dev moz0 
196.0.0.0/6 dev moz0 
200.0.0.0/5 dev moz0 
208.0.0.0/4 dev moz0 
240.0.0.0/4 dev moz0 
  • routing table
user@vpn-mozilla:~$ route 
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         10.138.38.125   0.0.0.0         UG    100    0        0 eth0
10.137.0.8      0.0.0.0         255.255.255.255 UH    32743  0        0 vif9.0
10.137.0.15     0.0.0.0         255.255.255.255 UH    32744  0        0 vif8.0
10.138.38.125   0.0.0.0         255.255.255.255 UH    100    0        0 eth0

user@vpn-mozilla:~$ ip rule list
0:	from all lookup local
32762:	from all to 91.193.5.34 lookup main
32763:	from all to 34.107.221.82 lookup main
32764:	from all lookup main suppress_prefixlength 0
32765:	not from all fwmark 0xca6c lookup 51820
32766:	from all lookup main
32767:	from all lookup default
2 Likes

After some digging I got it to work by changing a rule in mozvpn-preroute filter hook:

root@vpn-mozilla:/home/user# nft -a list table inet mozvpn-inet
table inet mozvpn-inet { # handle 7
	chain mozvpn-mangle { # handle 1
		type route hook output priority raw; policy accept;
	}

	chain mozvpn-nat { # handle 2
		type nat hook postrouting priority srcnat; policy accept;
	}

	chain mozvpn-conntrack { # handle 3
		type route hook output priority -201; policy accept;
		meta mark 0x0000ca6c ct zone set 40524 # handle 8
	}

	chain mozvpn-preroute { # handle 4
		type filter hook prerouting priority raw; policy accept;
		iifname != "vif*" iifname != "moz0" meta iiftype != loopback meta mark set 0x0000ca6c ct zone set 40524 # handle 10
	}
}

At # handle 10 I have added iifname != "vif*" to the condition:

# find the handle number of rule that contains "mark set 0x0000ca6c"
nft -a list table inet mozvpn-inet
# then delete that rule
nft delete rule inet mozvpn-inet mozvpn-preroute handle HANDLE_IDX
# and add this extended rule
nft -a add rule inet mozvpn-inet mozvpn-preroute iifname != vif* meta iifname != moz0 meta iiftype != loopback meta mark set 0x0000ca6c ct zone set 40524

This avoids that packages from vif* interfaces are directly routed to eth0 by getting marked with fwmark 0x0000ca6c (packages with fwmark 0x0000ca6c are routed to eth0, anything else goes through moz0)

I put everything together as NetworkManager dispatcher script (/etc/NetworkManager/dispatcher.d/99-mozilla-vpn.sh). It is triggered when eth0 gets up, it starts moz0 and applies the needed fixes:

#!/bin/bash
#
# Run as root. Init mozilla VPN when eth0 is up.
# Fix things for NetVM when moz0 is up
#

set -e

if [ "$1" = "eth0" -a "$2" = "up" ]; then
	# enable vpn state
	su - user -c "mozillavpn deactivate && mozillavpn activate"
fi
if [ "$1" = "moz0" -a "$2" = "up" ]; then
	# fix DNS using nmcli
	su - user -c "nmcli d modify eth0 ipv4.dns 8.8.8.8"
	# get handle id of nftable rule
	HANDLE_IDX=$(sudo nft -a list table inet mozvpn-inet | grep "mark set 0x0000ca6c" | sed "s/.*handle \(.*\)/\1/g")
	# remove the rule
	nft delete rule inet mozvpn-inet mozvpn-preroute handle ${HANDLE_IDX}
	# replace it with new rule
	nft -a add rule inet mozvpn-inet mozvpn-preroute iifname != vif* meta iifname != moz0 meta iiftype != loopback meta mark set 0x0000ca6c ct zone set 40524
fi

Works for now, but nothing production stable. I also uncommented this lines in /rw/config/qubes-firewall-user-script

# Block forwarding of connections through upstream network device
# (in case the vpn tunnel breaks)
iptables -I FORWARD -o eth0 -j DROP
iptables -I FORWARD -i eth0 -j DROP
ip6tables -I FORWARD -o eth0 -j DROP
ip6tables -I FORWARD -i eth0 -j DROP

TODO: The script uses 8.8.8.8 as DNS since I need a proper way to get the DNS servers mozillaVPN

4 Likes

Can I use a Firefox VPN extension?

Thanks for the write up it was very helpful. Were you able to persist the login? I am prompted to login after I restart the host.

I used a Standalone VM that persist the root file system. I don’t know where the login data is stored. If you figure that out you can use bind-dirs to persist the folder

I found my issue described in Mozilla VPN Github. Looks like its been recently fixed.