Wireguard VPN setup (4.2 and 4.3)

There is nothing to do for the WireGuard interfaces. Qubes OS allows forwarding from a vif interface (client qubes) to the default route interface.

The new rules are there to prevent forwarding to happen on eth0. What’s good with them is that they are using a default table in nftables that is exposed by Qubes OS, so if for some reasons Qubes OS modify the rules in the running VPN qube, it should never alter those rules.

But I know how software suck, so it’s important to add rules in the VPN qube firewall (which are deployed on the netvm of that VPN qube)

Okay this is my install script for wg-quick.After running this and qubes-vpn-firewall-ip-and-ports.sh (in app and dom0) and adding the config files, you only have to add qubes-firewall in qvm-service and you are finished

Code
#!/bin/sh
if [ "$(id -u)" -ne 0 ]; then
  echo "This script must be run as root." >&2
  exit 1
fi

cat <<'EOF' > /rw/config/rc.local
#!/bin/sh
wg_conf_location="/home/user/wireguard"
mkdir -p /etc/wireguard
if [ -d "$wg_conf_location" ]; then
	chmod -R 0600 "$wg_conf_location" && chmod 0700 "$wg_conf_location"/ && chown -R root:root "$wg_conf_location"
	cp "$(find "$wg_conf_location" -type f -print0 | shuf -z -n 1 | tr -d '\0')" "/etc/wireguard/current.conf" && \
        systemctl start wg-quick@current.service
fi
EOF
chmod 755 /rw/config/rc.local

cat <<'EOF' > /rw/config/qubes-firewall-user-script
#!/bin/sh
nft add rule qubes custom-forward oifname eth0 counter drop
nft add rule ip6 qubes custom-forward oifname eth0 counter drop
EOF

echo "Remember to activate qubes-firewall in the qvm-service tab"

cat <<'EOF' > /usr/local/bin/qubes-vpn-firewall-ip-and-ports.sh
#!/bin/bash
# dom0 get the file: qvm-run --pass-io vpn-test 'cat /home/user/ip_and_port.txt' > $HOME/ip_and_port.txt

LIST_LOCATION="$HOME/ip_and_port.txt"

usage() {
  echo "Usage: $0 [--hostname TARGETVM] [--directory DIR] [--list-location FILE]"
  exit 1
}

while [[ "$#" -gt 0 ]]; do
  case "$1" in
  --hostname)
    shift
    HOSTNAME_TARGET="$1"
    ;;
  --directory)
    shift
    DIRECTORY="$1"
    ;;
  --list-location)
    shift
    LIST_LOCATION="$1"
    ;;
  ,*)
    echo "Unknown parameter: $1"
    usage
    ;;
  esac
  shift
done

if [ -f /run/qubes/this-is-proxyvm ]; then
  if [[ -z "$DIRECTORY" ]]; then
    echo "Error: --directory is not set."
    exit 1
  fi
  if [[ -z "$HOSTNAME_TARGET" ]]; then
    HOSTNAME_TARGET=$(hostname)
  fi
  echo "$HOSTNAME_TARGET" >"$LIST_LOCATION"

  find "$DIRECTORY" -type f -name "*.conf" | while read -r conf_file; do
    grep 'Endpoint' "$conf_file" | while read -r line; do
      ip=$(echo "$line" | cut -d'=' -f2 | awk '{print $1}' | cut -d':' -f1)
      port=$(echo "$line" | cut -d'=' -f2 | awk '{print $1}' | cut -d':' -f2)
      echo -e "$ip\t$port" >>"$LIST_LOCATION"
    done
  done

else
  read -p "Your are in dom0 and want to Proceed? (y/n): " ans && [[ "$ans" =~ ^[Yy]$ ]] || {
    echo "Canceled"
    exit 1
  }

  if [[ ! -f "$LIST_LOCATION" ]]; then
    echo "list does not exist in specified location"
    exit 1
  fi

  if [[ -z "$HOSTNAME_TARGET" ]]; then
    HOSTNAME_TARGET="$(head -n 1 <"$LIST_LOCATION")"
  fi
  qvm-firewall "$HOSTNAME_TARGET" reset
  counter=0

  while IFS=$'\t' read -r ip port; do
    qvm-firewall "$HOSTNAME_TARGET" add --before "$counter" accept proto=udp dstports="$port" dsthost="$ip/32"
    ((counter++))
  done < <(tail -n +2 "$LIST_LOCATION")

  qvm-firewall "$HOSTNAME_TARGET" add --before "$counter" drop specialtarget=dns
  ((counter++))
  qvm-firewall "$HOSTNAME_TARGET" add --before "$counter" drop proto=tcp
  ((counter++))
  qvm-firewall "$HOSTNAME_TARGET" add --before "$counter" drop proto=udp
  ((counter++))
  qvm-firewall "$HOSTNAME_TARGET" add --before "$counter" drop proto=icmp
  ((counter++))
  qvm-firewall "$HOSTNAME_TARGET" add --before "$counter" drop
  ((counter++))
  qvm-firewall "$HOSTNAME_TARGET" del --rule-no "$counter"
  qvm-firewall "$HOSTNAME_TARGET" list
fi
EOF
chmod 755 /usr/local/bin/qubes-vpn-firewall-ip-and-ports.sh

echo "Remember to add the ip and port to the qvm-firewall rules in dom0..."


You need to escape all $ in the cat EOF otherwise they are interpreted as variables in the output before the redirection, you will end up with

[..]
if [ -d "" ]; then
    chmod -R 0600 "" ......
[...]

No ‘EOF’ prevents that

1 Like

Wow, this is so cool! :star_struck:

Major improvement would be translating these rules to nftables and adding them to the configs. Would be an additional killswitch. WireGuard Kill Switch - IVPN Help

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

This is attempted translate with iptables-translate:

PostUp = nft insert rule ip filter OUTPUT oifname != "%i" mark !=  $(wg show %i fwmark) fib daddr type != local counter reject && nft insert rule ip6 filter OUTPUT oifname != "%i" mark !=  $(wg show %i fwmark) fib daddr type != local counter reject
PreDown= nft delete rule ip filter OUTPUT oifname != "%i" mark != $(wg show %i fwmark) fib daddr type != local counter reject && nft delete rule ip6 filter OUTPUT oifname != "%i" mark != $(wg show %i fwmark) fib daddr type != local counter reject

Haha tried to do with LLM not sure if the result if garbage:

PostUP = nft add rule inet filter output oifname "%i" mark != $(wg show %i fwmark) ipaddr != 127.0.0.1 drop && nft add rule inet filter output oifname "%i" mark != $(wg show %i fwmark) ip6 daddr != ::1 drop
PreDown = nft delete rule inet filter output oifname "%i" mark != $(wg show %i fwmark) ipaddr != 127.0.0.1 drop && nft delete rule inet filter output oifname "%i" mark != $(wg show %i fwmark) ip6 daddr != ::1 drop

Okay apparently these are not enough packages:

qubes-core-agent-networking wireguard wireguard-tools openresolv

I get this error:
RTNETLINK answers: No such device

Does anybody know what’s missing?
Or maybe some other error ? Wanted to create a new template and suddenly this happened no idea why

An error when you do what?

the wg-quick service fails to start

This is actually the same error I had with qubes-vpn-support today.

Wg-quick was working a couple hours ago not sure what changed
The default debian template also has this error …

Fixed low MTU error

1 Like

Hi all,
I read a lot, but it seems guides are for adding a VPN for all traffic, while I need only to configure a VPN to access my house private network when I am outside, since when at home I do not it to be active.

It seems natural to me to add it to sys-net qubes, using NetworkManager, but since I am very new to Qubes OS, I am not sure which is the best practice in this case, any advice?

hello,

If you do not route all traffic, then you need to figure if you want to block all traffic that will not go through the VPN (so, not going to house network) or if you want to pass traffic.

If you want to block the traffic, this does not change much from the guide, in fact, it’s just a difference in the peer configuration in WireGuard configuration file that will list all subnets to route through the VPN.

If you do not want to block the traffic, it’s a lot easier:

  • create a VPN qube
  • import the wireguard configuration
  • use it as a netvm

I am in the second scenario, I do not need to block other traffic, but be able to access public internet + my private network, when I am not at home.

I think I have already tried what you suggest:

  • Created a sys-wg0 qube as describe in the guide, it is aphv based on fedora-41-xfce that uses sys-firewall as NetVM
  • I the created a qube Home that uses sys-wg0 as NetVM

When I open Firefox in Home then I am able to access hosts via the VPN, but when at home, I lost the possibility to reach other hosts on my local network, that are not exposed via the VPN.

Let’s says that the Wireguard VPN uses the subnet 10.1.0.0/24, while my home private network is using 10.2.0.0/24, then using the sys-wg0 as NetVM, I can access public internet and hosts on 10.1.0.0/24 but no more hosts on 10.2.0.0/24.

What am I doing wrong?

what do you have in WireGuard allowed IPs?

I have allowed ips: 10.1.0.0/24

You need to add a route to 10.2.0.0/24 through 10.1.0.1 I guess (if it’s the VPN server IP).

You can add a static route in the VPN parameters in Network Manager.

But why? If I am at home sys-net is connected to 10.2.0.0/24, and sys-wg0 uses sys-firewall as NetVM, addresses that are not routed to the VPN, so everything different from 10.1.0.0/24, should not go to the default gateway and so use sys-net?

So after some more debugging, it turns out that I can reach the host on the local network by IP but not by DNS, so I need to understand how to add my local router to the list of DNS servers

I thought you were accessing its IP address. If you have a local DNS you need to set it in network manager, not sure it will work but try it first.

If something like zeroconf is used, it won’t work.

I personally went with a simple script in rc.local that add the host in /etc/hosts for this situation

I reading many posts thanks you, but now can’t understand which is better solution and how working.

What is different:
a. work_vm <—> work_firewall_1_vm <—> vpn <—> work_Firewall_2_vm <—> sys-net
b. work_vm <—> work_firewall_1_vm <—> sys-net
c. work_vm <—> work_firewall_1_vm <—> sys-firewall <—> sys-net

And when I need this:

  • qvm-firewall sys-vpn reset # (1)
  • qvm-firewall sys-vpn add accept dsthost=1.2.3.4 # (2)
  • qvm-firewall sys-vpn del --rule-no 0 # (3)

And if use mirageOS what steps I must to do? Thanks you.