1. INSTALL TEMPLATE (fedora-40-minimal) AND UPDATE IT

Open dom0 terminal and type: > sudo qubes-dom0-update qubes-template-fedora-40-minimal && sudo qvm-run -u root --pass-io fedora-40-minimal "xterm -e 'dnf update && dnf upgrade && dnf autoremove && read'" && qvm-shutdown fedora-40-minimal

2. CLONE TEMPLATE & CREATE VPN TEMPLATE

dom0: > qvm-clone fedora-40-minimal f40m-vpn && qvm-run -u root --pass-io f40m-vpn "xterm -e 'dnf install qubes-input-proxy-sender qubes-core-agent-passwordless-root qubes-core-agent-networking qubes-core-agent-dom0-updates qubes-core-agent-network-manager NetworkManager-wifi network-manager-applet notification-daemon thunar qubes-core-agent-thunar qubes-menus pciutils iputils less psmisc gnome-keyring chromium dbus-x11 dejavu-sans-fonts tinyproxy notification-daemon xfce4-terminal nethogs geany openvpn openssl && read'"

3. FIXING freedesktop.notifications.service VPN TEMPLATE

dom0: > qvm-run -u root f40m-vpn 'geany /usr/share/dbus-1/services/org.freedesktop.Notifications.service'

when geany prompts copy and paste the text below then save the file and shutdown the template.

[D-BUS Service]
Name=org.freedesktop.Notifications
Exec=/usr/libexec/notification-daemon

4. CREATE APPVM-VPN

> qvm-create --template f40m-vpn --class AppVM --label red default-f40m-vpn && qvm-prefs default-f40m-vpn provides_network on

5. SETUP FILES/SCRIPTS INSIDE DEFAULT-APPVM-VPN 5.1 Create vpn directory: >qvm-run -u root --pass-io --nogui default-f40m-vpn 'mkdir /rw/config/vpn'

5.2 Edit /rw/config/qubes-firewall-user-script

> qvm-run -u root default-f40m-vpn 'geany /rw/config/qubes-firewall-user-script'

copy and paste this:

#!/bin/bash

#    Block forwarding of connections through upstream network device
#    (in case the vpn tunnel breaks):
# Prevent the qube to forward traffic outside of the VPN

nft insert rule qubes custom-forward oifname eth0 counter drop
nft insert rule ip6 qubes custom-forward oifname eth0 counter drop
nft insert rule qubes custom-forward iifname eth0 counter drop
nft insert rule ip6 qubes custom-forward iifname eth0 counter drop

#    Add the `vpntunnel` group to system, if it doesn't already exist
if ! grep -q "^vpntunnel:" /etc/group ; then
     sleep 3s
     groupadd -rf vpntunnel
     sync
fi
sleep 2s

#    Accept traffic to VPN
nft 'add chain qubes output { type filter hook output priority 0; policy accept; }'

#    Allow traffic from the `vpntunnel` group to the uplink interface (eth0);
#    Our VPN client will run with group `vpntunnel`.
nft insert rule ip qubes output oifname eth0 skgid vpntunnel accept

While using geany app create a new file named qubes-vpn-status.sh and save it in /rw/config/vpn/ folder with this content:

#!/bin/bash
set -e
export PATH="$PATH:/usr/sbin:/sbin"

case "$1" in

up)
# To override DHCP DNS, assign DNS addresses to 'vpn_dns' env variable before calling this script;
# Format is 'X.X.X.X  Y.Y.Y.Y [...]'
if [[ -z "$vpn_dns" ]] ; then
    # Parses DHCP foreign_option_* vars to automatically set DNS address translation:
    for optionname in ${!foreign_option_*} ; do
        option="${!optionname}"
        unset fops; fops=($option)
        if [ ${fops[1]} == "DNS" ] ; then vpn_dns="$vpn_dns ${fops[2]}" ; fi
    done
fi


nft flush chain ip qubes dnat-dns

if [[ -n "$vpn_dns" ]] ; then
    # Set DNS address translation in firewall:
    for addr in $vpn_dns; do
        nft add rule qubes dnat-dns iifname == "vif*" tcp dport 53 dnat "$addr"
        nft add rule qubes dnat-dns iifname == "vif*" udp dport 53 dnat "$addr"
    done
    su - -c 'notify-send "$(hostname): LINK IS UP." --icon=network-server' user
else
    su - -c 'notify-send "$(hostname): LINK UP, NO DNS!" --icon=dialog-error' user
fi

;;
down)
su - -c 'notify-send "$(hostname): LINK IS DOWN !" --icon=dialog-error' user

# Restart the VPN automatically
sleep 5s
sudo /rw/config/rc.local
;;
esac

5.3. CHANGES IN /rw/config/rc.local

using geany open the file /rw/config/rc.local

copy and paste this content and then save it:

#!/bin/bash
VPN_CLIENT='openvpn'
VPN_OPTIONS='--cd /rw/config/vpn/ --config openvpn-client.ovpn --daemon'

# vpntunnel
sudo sg vpntunnel -c "$VPN_CLIENT $VPN_OPTIONS"

5.4 Make some files with chmod and shutdown default-appvm-vpn

dom0: > qvm-run -u root --pass-io --nogui default-f40m-vpn 'chmod +x /rw/config/vpn/qubes-vpn-status.sh' && qvm-run -u root --pass-io --nogui default-f40m-vpn 'chmod +x /rw/config/rc.local' && qvm-run -u root --pass-io --nogui default-f40m-vpn 'chmod +x /rw/config/qubes-firewall-user-script' && qvm-shutdown default-f40m-vpn

Now you have created a default vpn template and appvm, from this one you can clone and make many appvms-vpns you want.

6. Clone the default-f40m-vpn to use with your config files..

dom0: > qvm-clone default-f40m-vpn sys-vpn-test

Start sys-vpn-test copy all your vpn config files into the folder /rw/config/vpn/

Edit your .ovpn file and inside it add the lines

ATTENTION: rename your .ovpn file to openvpn-client.ovpn

script-security 2
up 'qubes-vpn-status up'
down 'qubes-vpn-status down'

Save it in /rw/config/vpn/ renamed to: openvpn-client.ovpn

the folder /rw/config/vpn/ inside sys-vpn-test, in this example the keys/certs was gen separated, it will look like this:

rw folder|640x477, 100%

Restart your sys-vpn-test and if you have done everything right you'll be able to use it, run a dispvm, change the net-qube to sys-vpn-test browser to ipleak.net and test it, while running the test you can check in vpn terminal the connection using: sudo nethogs.