Mullvad VPN App 4.3 (and 4.2) setup guide

If i select that option then what are the cons for me and does that mean that Dom0 and the whole system i.e. everything will then use sys-VPN and never the ISP clear Internet?

This does exactly what it says. Updates will be downloaded through the configured qube, so if you use your mullvad vpn qube here, it will always go through the mullvad qube.

However, all appvms are checking for updates and ping qubes os servers, including sys-net and sys-firewall by default, which are not covered by any vpn.

do you think i should bother, is there any threat model for me to be concerned about with the current set up on Dom0 updating direct from the ISP, what info exactly does the ISP get from it as i dont like the ISP knowing anything if possible ideally as untrusted?

I don’t know, you seemed concerned updates were not done through the VPN.

but as im a newbie, im not sure if i should be concerned about it really :thinking:

If you are concerned that someone monitoring your activity can
trivially identify you as a Qubes user, then you should be concerned
about this.

I never presume to speak for the Qubes team.
When I comment in the Forum I speak for myself.

Can someone explain why I need to enable VPN Settings > Local network sharing?

For reference, here is the quote:

Open the Mullvad VPN app. Go to Settings → VPN settings and toggle Local network sharing. Due to some strange interaction between qubes services and Mullvad VPN, certain apps will get internet connections while others do not if this toggle is not enabled. This toggle will not actually allow AppVMs connected to the ProxyVM to connect to the local network.

Another reference here in forum.

That “some strange interaction” makes me slightly uncomfortable :sweat_smile: .

I don’t understand it …

1. nft list table inet mullvad in proxyVM shows, that basically all private IP ranges are added with accept to output, input, and forward. Why would that be needed for AppVM → ProxyVM → sys-firewall > sys-net > Inet, given that my destination is some site on the internet?

2. I am also not sure of

This toggle will not actually allow AppVMs connected to the ProxyVM to connect to the local network.

Typing ip route show table all shows, there is a new, top-most wg0-mullvad default route with enabled WG, which is used for all destination IPs. You can confirm by ip route get 192.168.1.1, which outputs wg0-mullvad .

But this actually does not block access via firewall rules to your LAN from appVMs using Mullvad netVM (rather routes private IP to WG). With some Mullvad app route misconfiguration in worst case, a compromised VPN app would be able to have have LAN network access, I guess? Hence IMO it definitely makes sense to block forwarding to eth0 in every case, or better block private IP ranges in Mullvad proxyVM settings via qvm-firewall.

It also would be nice to get rid of this “Local network sharing” setting at all which clutters firewall rules and in theory globally accepts LAN.

Any clarification appreciated!

After a bit evaluation, I believe this is normal routing behavior, not "some strange interaction”.

Any client qube connecting to Mullvad VPN proxyVM/netVM is assigned an 10.138.x.x IP by Qubes. Mullvad’s kill switch blocks all private IP ranges, unless “Local network sharing” is ticked. Therefore client connections are simply rejected, as the default reject/drop in that added Mullvad table/ forward chain always wins over the default accept rules of Qubes.

To summarize, enabling “Local network sharing” is needed to add an explicit accept rule for client qube network traffic and should be fine.

Hi, I’ve followed the guide through completely, but have been stuck on the following issue: the rc.local does not work on boot, because of the denied permissions. In particular, when I run ./rc.local by hand:
sudo: unable to execute /usr/sbin/nft: Permission denied.
This line is repeated the second time.

The script is executable. When I run /usr/local/bin/mullvad-dns.sh, there is no problem with the dns on the dependent appVM, however it does not happen automatically on boot.

This is weird and should not happen. Can you share the content of your rc.local script? Which version of fedora are you using?

#!/bin/sh

# This script will be executed at every VM startup, you can place your own
# custom commands here. This includes overriding some configuration in /etc,
# starting services etc.
#
# Executable scripts located in /rw/config/rc.local.d with the extension
# '.rc' are executed immediately before this rc.local.
# Example:
#  /rw/config/rc.local.d/custom.rc
#
# Example for overriding the whole CUPS configuration:
#  rm -rf /etc/cups
#  ln -s /rw/config/cups /etc/cups
#  systemctl --no-block restart cups
/usr/local/bin/mullvad-dns.sh &

I use the fedora-42-xfce

The actual output of the ./rc.local is:

Mullvad is on
sudo: unable to execute /usr/sbin/nft: Permission denied
sudo: unable to execute /usr/sbin/nft: Permission denied

The content of the script itself:

#! /usr/bin/env bash

update_dns() {
	# mullvad_on: 0 -> off, 1 -> on
	mullvad_on=$([[ $(grep -v -c "nameserver \+10.139" /etc/resolv.conf) -gt 0 ]] && echo 1 || echo 0)

	if [[ $mullvad_on -eq 1 ]]; then

		echo "Mullvad is on"

		# get the mullvad dns ip address. First one is used if there is more than one.
		mullvad_dns_ip=$(grep "nameserver" < /etc/resolv.conf| awk '{print $2}' | head -n 1)

		# delete all the lines defined in dnat-dns
		sudo nft flush chain ip qubes dnat-dns

		# forward all dns requests to mullvad dns servers
		sudo nft add rule ip qubes dnat-dns meta l4proto { tcp, udp } ip daddr { 10.139.1.1, 10.139.1.2 } th dport 53 dnat to "$mullvad_dns_ip"

	else

		echo "Mullvad is off"

		# get qubes nameserver ip addresses
		nameserver_ips=$(grep "nameserver" < /etc/resolv.conf| awk '{print $2}')

		# delete all the lines defined in dnat-dns
		sudo nft flush chain ip qubes dnat-dns

		# add rule to forward dns requests to qubes nameservers
		for ip in $nameserver_ips; do
			sudo nft add rule ip qubes dnat-dns ip daddr "$ip" udp dport 53 dnat to "$ip"
			sudo nft add rule ip qubes dnat-dns ip daddr "$ip" tcp dport 53 dnat to "$ip"
		done

	fi
}

update_dns
# check for /etc/resolv.conf content change
inotifywait -m -q -e close_write /etc/resolv.conf | while read -r;
do
	update_dns
done

Which user are you using when running the rc.local script?

I use the “user” user, with sudo ./rc.local it works fine, I also did make the script executable like in the setup guide

Newbie here: thanks for this guide: it seems to work perfectly. Just one observation and question:

Am I correct in thinking that with this setup, Qubes OS updates will still use the sys-net/sys-firewall connection to the network and therefore by-pass MullvadVPN?

If true, what setup options are there to force these updates through a VPN?

Or may be it’s safer to connect to the public network using a mobile phone running the VPN and sharing that connection with the laptop?

QubesOS updates functions always use the (in global config) given solution. Means: If you’ve choosen sys-firewall (for update VM) there, all updates went via sys-firewall < sys-net … If you choose sys-whonix (my suggestion), all updates went via sys-whonix < sys-net …

I only would use sys-mullvad/or other vpn qubes for personal use.

I am failing at the first step. Standalone fedora42, added qubes firewall service.

sudo dnf config-manager addrepo --from-repofile=https://repository.mullvad.net/rpm/stable/mullvad.repo
sudo dnf install mullvad-vpn
 https://repository.mullvad.net/rpm/sta ???% |   0.0   B/s |   0.0   B |  00m00s
>>> Curl error (35): SSL connect error for https://repository.mullvad.net/rpm/st
>>> Curl error (35): SSL connect error for https://repository.mullvad.net/rpm/st
>>> Curl error (35): SSL connect error for https://repository.mullvad.net/rpm/st
>>> Curl error (35): SSL connect error for https://repository.mullvad.net/rpm/st
>>> Curl error (35): SSL connect error for https://repository.mullvad.net/rpm/st
Failed to download repository configuration file "https://repository.mullvad.net/rpm/stable/mullvad.repo": Failed to download files
Updating and loading repositories:
Repositories loaded.

If you go to the mullvad config generator and download the linux version of the servers you want to use you can add them to your sys-net qube as VPN profiles so ALL of your traffic will go out through the VPN, I made a post detailing how to add mullvad as a networking qube but if you follow along after the point where you generate the config files and treat the sys-net qube as the qube you are trying to route mullvad through it works the same, as a matter of fact you can even set up a mullvad qube like I detailed and have qubes you set to use your mullvadVPN qube as its networking qube to have their own traffic routed to specific servers after its routed through your sys-net mullvad vpn, ie privateQube → mullvadVPN qube (vpn set to Sweden) → sys-firewall → sys-net (vpn set to germany) → internet → Germany server → Sweden server

Its a first in last out system, so the first VPN you hit is the place you end up, you can verify this by disabling the vpns and refreshing mullvads connection check page to see which vpn that specific qube is using. You should also use that page (or dns leak test) to ensure you don’t have a leaky DNS, firefox by default leaks your DNS by using public DNS servers rather than your mullvad DNS servers, which means any DNS server will know where your traffic is going (since it has to locate your destination) unless you disable https over dns in firefox. This isn’t an issue in whonix qubes though since DNS settings are independent of where you choose to route traffic, just who you choose to pathfind for you, and whonix has their dns settings pre configured. Feel free to shoot me any messages if you have any questions!

I downloaded mullvad gui and when i try multihop it doesnt work it says connected but there no internet when i go on the web

Can you ping 8.8.8.8 from terminal?