No DNS using Wireguard ProxyVM on Qubes 4.1

I am trying to set up a wireguard VPN (not mullvad) on qubes 4.1

I followed this guide by mullvad (using a fedora 34 template for the ProxyVM) and I also tried this guide (using a debian 11 template as ProxyVM).
Both left me with the same problem: DNS

In my resulting sys-wireguard ProxyVM everything seems fine, I can use DNS and I am connected to the VPN server.

However, in my AppVM, I cannot connect to any URL, just IP-addresses (which can be inconvinient at times…).

I did not find any reports of that issue in this forum, so I guess it is me who made a mistake.

Can you explain how to change the DNS and anti-leak firewall rules when using my own wireguard configuration? This could be the problem…

The result of this DNS-problem led me to setting up the VPN connection right inside the templateVM I use for creating my AppVMs (and setting the AppVM’s networking VM to sys-firewall). This worked fine for some time, but now I face the same DNS problem using this fix aswell. Running sudo dpkg-reconfigure resolvconf in the AppVM fixes that until the next reboot (found this temporary fix somewhere on stackoverflow).

It is worth mentioning that my wireguard configuration already contains some iptables:

PostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show  %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -D OUTPUT ! -o %i -m mark ! --mark $(wg show  %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT

I do not know in detail what this gibberish does, it is somehow used to create a kill-switch…

I would love to have a sys-wireguard because this is what makes Qubes great: compartmentalization (the workaround with sudo dpkg-reconfigure resolvconf is a pain…).

Yes, this is your problem. You need to configure DNS requests redirect.
To change the config in /rw/config/qubes-ip-change-hook for you server you need to change these lines:

NS_MULLVAD_PRIVATE=10.8.0.1
NS_MULLVAD_PUBLIC=193.138.219.228

NS_MULLVAD_PRIVATE - your wireguard server address in VPN network.
NS_MULLVAD_PUBLIC - DNS server used by your wireguard server. If you have none then you can set it to public dns like 9.9.9.9 or 8.8.8.8.

1 Like

Thanks for the help.

The problem persists: I set $NS_MULLVAD_PRIVATE to the local IP-address of my VPN server and I set $NS_MULLVAD_PUBLIC to the global address of my VPN server (my VPN server handles DNS requests, too, to avoid leaking my IP-address via DNS). Even setting $NS_MULLVAD_PUBLIC to 8.8.8.8 did not resolve the DNS-issues.

Maybe the issue is not with my VPN setup, but with the forwarding of DNS requests from my AppVM to my ProxyVM, because in my ProxyVM everything works as expected. In my AppVM I still cannot perform DNS requests.

You need to have default DNS servers 10.139.1.1/10.139.1.2 in AppVM for these rulse to work.
You can check the packet counters in your ProxyVM to see if the DNS requests from your AppVM are redirected by iptables:
sudo iptables -t nat -L PR-QBS -n -v
Also on second look NS_MULLVAD_PRIVATE doesn’t seem to be used so you can just remove it from script.

1 Like

The etc/resolv.conf file in my AppVM looks like this:

nameserver 10.139.1.1
nameserver 10.139.1.2

This seems to be correct.

In my ProxyVM I can see the packet counter increasing when performing a DNS request in the AppVM connected to my ProxyVM.

Actually, I did not notice NS_MULLVAD_PRIVATE not being used, thanks.

Show your iptables rules in ProxyVM:

sudo iptables -L -n -v
sudo iptables -t nat -L -n -v
1 Like

Here’s my iptables rules in my sys-wireguard ProxyVM.
It’s a shame that I do not understand iptables…
I really should look into that at some point…

sudo iptables -L -n -v

Chain INPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            state INVALID
    0     0 DROP       udp  --  vif+   *       0.0.0.0/0            0.0.0.0/0            udp dpt:68
16370   18M ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
    0     0 ACCEPT     icmp --  vif+   *       0.0.0.0/0            0.0.0.0/0           
    5   260 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0           
    0     0 REJECT     all  --  vif+   *       0.0.0.0/0            0.0.0.0/0            reject-with icmp-host-prohibited
   15  8695 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0           

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 DROP       all  --  eth0   *       0.0.0.0/0            0.0.0.0/0           
    0     0 DROP       all  --  *      eth0    0.0.0.0/0            0.0.0.0/0           
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            state INVALID
11622 8976K ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
   85  4532 QBS-FORWARD  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
    0     0 DROP       all  --  vif+   vif+    0.0.0.0/0            0.0.0.0/0           
   85  4532 ACCEPT     all  --  vif+   *       0.0.0.0/0            0.0.0.0/0           
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0           

Chain OUTPUT (policy ACCEPT 11401 packets, 3636K bytes)
 pkts bytes target     prot opt in     out     source               destination         
  588  339K REJECT     all  --  *      !wg0    0.0.0.0/0            0.0.0.0/0            mark match ! 0xca6c ADDRTYPE match dst-type !LOCAL reject-with icmp-port-unreachable

Chain QBS-FORWARD (1 references)
 pkts bytes target     prot opt in     out     source               destination         

sudo iptables -t nat -L -n -v

Chain PREROUTING (policy ACCEPT 74 packets, 11771 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   80 12113 PR-QBS     all  --  *      *       0.0.0.0/0            0.0.0.0/0           
   74 11771 PR-QBS-SERVICES  all  --  *      *       0.0.0.0/0            0.0.0.0/0           

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 26 packets, 2096 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     all  --  *      vif+    0.0.0.0/0            0.0.0.0/0           
    5   260 ACCEPT     all  --  *      lo      0.0.0.0/0            0.0.0.0/0           
   84  5174 MASQUERADE  all  --  *      *       0.0.0.0/0            0.0.0.0/0           

Chain PR-QBS (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    3   171 DNAT       udp  --  *      *       0.0.0.0/0            10.139.1.1           udp dpt:53 to:10.139.1.1
    0     0 DNAT       tcp  --  *      *       0.0.0.0/0            10.139.1.1           tcp dpt:53 to:10.139.1.1
    3   171 DNAT       udp  --  *      *       0.0.0.0/0            10.139.1.2           udp dpt:53 to:10.139.1.2
    0     0 DNAT       tcp  --  *      *       0.0.0.0/0            10.139.1.2           tcp dpt:53 to:10.139.1.2

Chain PR-QBS-SERVICES (1 references)
 pkts bytes target     prot opt in     out     source               destination         

You don’t have the needed DNS rules.
Did you create /rw/config/qubes-ip-change-hook and made it executable sudo chmod +x /rw/config/qubes-ip-change-hook like the guide says?
QubesOS R4: Wireguard VPN
You can run it manually:
sudo /rw/config/qubes-ip-change-hook
And check if it’ll apply the rules:
sudo iptables -t nat -L PR-QBS -n -v

1 Like

The hook is executable (-rwxr-xr-x 1 root root).
When executing the hook manually, all entries in your above snippet in the destination column change to my VPN server’s local IP-address.
In the column right to the destination column, the IP-addresses changed to my VPN server’s public address (eg. $NS_MULLVAD_PUBLIC).

After executing the hook manually, the DNS problem persists.

Edit: Putting sudo /rw/config/ip-change-hook into my /rw/config/rc.local after the wg-quick command seems to make these changes persistent. DNS problems persist however.

Change /rw/config/qubes-ip-change-hook to:

#!/bin/sh
NS1=10.139.1.1
NS2=10.139.1.2

NS_MULLVAD_PUBLIC=x.x.x.x

iptables -F OUTPUT
iptables -I FORWARD -o eth0 -j DROP
iptables -I FORWARD -i eth0 -j DROP
iptables -F PR-QBS -t nat
iptables -A PR-QBS -t nat -d $NS1 -p udp --dport 53 -j DNAT --to $NS_MULLVAD_PUBLIC 
iptables -A PR-QBS -t nat -d $NS1 -p tcp --dport 53 -j DNAT --to $NS_MULLVAD_PUBLIC 
iptables -A PR-QBS -t nat -d $NS2 -p udp --dport 53 -j DNAT --to $NS_MULLVAD_PUBLIC
iptables -A PR-QBS -t nat -d $NS2 -p tcp --dport 53 -j DNAT --to $NS_MULLVAD_PUBLIC 

Where x.x.x.x - your DNS.
Run sudo /rw/config/ip-change-hook and try DNS in AppVM again.

1 Like

I changed the script and replaced x.x.x.x with my VPN server’s local IP address which made it work after explicitly executing the qubes-ip-change-hook.
Putting sudo /rw/config/qubes-ip-change-hook after wg-quick up myInterface in /rw/config/rc.local fixes the issues.

I can use DNS in my AppVM!!!
Thanks a lot mate!
But isn’t the qubes-ip-change-hook supposed to execute on its own (eg. without putting it into /rw/config/rc.local)?

I think it should be executed automatically when you start vpn with wg-quick up.

  • /rw/config/qubes-ip-change-hook - script runs in NetVM after every external IP change and on “hardware” link status change.

Config files | Qubes OS

1 Like

Okay, thanks, marking as solved.

You really made my day…

Can I use this as well to replace Mullvad public DNS with a different DNS-to-TLS?

Yes, you’ll need to run DNS over TLS resolver like stubby in sys-vpn that’ll listen on 127.0.0.1:53 for DNS queries and you’ll need to redirect the DNS queries from connected to sys-vpn qubes to 127.0.0.1:53 with iptables in sys-vpn:

#!/bin/sh
NS1=10.139.1.1
NS2=10.139.1.2

NAMESERVER=127.0.0.1

iptables -F OUTPUT
iptables -I FORWARD -o eth0 -j DROP
iptables -I FORWARD -i eth0 -j DROP
iptables -F PR-QBS -t nat
iptables -A PR-QBS -t nat -d $NS1 -p udp --dport 53 -j DNAT --to $NAMESERVER 
iptables -A PR-QBS -t nat -d $NS1 -p tcp --dport 53 -j DNAT --to $NAMESERVER 
iptables -A PR-QBS -t nat -d $NS2 -p udp --dport 53 -j DNAT --to $NAMESERVER
iptables -A PR-QBS -t nat -d $NS2 -p tcp --dport 53 -j DNAT --to $NAMESERVER 
1 Like

How would this work under Qubes 4.2 with nft?

I tried to replace it with iptables-nft but unfortunately it didn’t work.

1 Like

Thanks, that worked great.

With the firewall rule in ,
/rw/config/qubes-firewall-user-script

Redirect all the DNS traffic to the preferred DNS server

DNS=9.9.9.9
nft add chain qubes nat { type nat hook prerouting priority dstnat; }
nft add rule qubes nat iifname == “vif*” tcp dport 53 dnat “$DNS”
nft add rule qubes nat iifname == “vif*” udp dport 53 dnat “$DNS”