Updated fork:
https://github.com/demosios/Qubes-vpn-support
I have published an updated fork of tasket/Qubes-vpn-support.
This work is based on the original Qubes VPN support project by @tasket. That project provided the core model and much of the structure for running a VPN client inside a dedicated Qubes ProxyVM. Credit belongs to @tasket for the original design and implementation.
I also want to credit @1choice for laying important groundwork for the shift
toward nftables.
The goal of this fork is to update and harden the project for a modern Qubes OS
4.3 setup using nftables, with a stricter fail-closed security model.
Most existing VPN-in-Qubes approaches either rely heavily on manual firewall
configuration, partially documented scripts, NetworkManager behavior, or older
iptables assumptions. Those can work, but they are often less elegant, less
complete, or harder to reason about under failure conditions.
This update keeps the Qubes-native ProxyVM model while making the firewall, DNS, IPv6, and startup behavior more explicit.
iptables handling to nftablesct state established,relatedDownstream DNS is DNATed only after VPN DNS is available
VPN DNS handling falls back to QubesDB on Qubes 4.3 where the older helper file is absent
Full architecture and change documentation:
https://github.com/demosios/Qubes-vpn-support/blob/master/README.md
debian-13-minimal template from the Qubes repositorynftables firewall backendOpenVPN is the primary supported backend.
WireGuard support is included through an optional systemd override selected during ProxyVM initialization, but it should be considered more operationally sensitive because WireGuard is kernel-driven and does not map as cleanly to the same userspace process-group egress model.
In the TemplateVM:
cd Qubes-vpn-support
sudo bash ./install
Shut down the TemplateVM.
Create a dedicated VPN ProxyVM from that template.
In Qubes settings for the VPN ProxyVM:
provides networkSet the NetVM as appropriate for your topology
Enable the relevant Qubes service for this handler, if used by your setup
Start the VPN ProxyVM.
In the VPN ProxyVM:
sudo /usr/lib/qubes/qubes-vpn-setup --config
This prepares /rw/config/vpn/ for you.
It also asks whether to install the optional WireGuard override:
y if this ProxyVM will use WireGuardn or press Enter to keep the default OpenVPN service settingsAdd your provider files to:
/rw/config/vpn/
At minimum, provide:
/rw/config/vpn/vpn-client.conf
Also place any required certs, keys, CRLs, or other provider files in that directory.
If username/password authentication is needed:
sudo /usr/lib/qubes/qubes-vpn-setup --userpass
For WireGuard, vpn-client.conf should be a wg-quick style config.
For strict hostname handling, configure VPN DNS in the provider config.
For OpenVPN:
setenv vpn_dns "X.X.X.X Y.Y.Y.Y"
remote vpn.example.net 1194
For WireGuard:
DNS = X.X.X.X, Y.Y.Y.Y
Endpoint = vpn.example.net:51820
For the strictest setup, use an IPv4 remote or endpoint address instead of a hostname.
If WireGuard was selected during --config, the persistent override choice is
stored under:
/rw/config/qubes-vpn-handler.service.d/10_wg.conf
That override is synced into the live systemd drop-in path on boot.
sudo systemctl restart qubes-firewall.service
sudo systemctl restart qubes-vpn-handler.service
sudo systemctl status qubes-vpn-handler.service
ip -br link
sudo nft list chain ip qubes custom-forward
sudo nft list chain ip qubes dnat-dns
cat /var/run/qubes/qubes-vpn-ns
cat /proc/sys/net/ipv6/conf/all/disable_ipv6
Expected high-level results:
The VPN tunnel interface exists, for example tun0
The tunnel interface is assigned to group 9
custom-forward contains downstream-to-VPN forwarding and stateful return
rules
dnat-dns contains DNS DNAT rules after the VPN is up
/var/run/qubes/qubes-vpn-ns contains the VPN DNS values
IPv6 disablement reports 1
Only attach downstream AppVMs to the VPN ProxyVM after verifying the tunnel and DNS rules.
This is security-sensitive networking code. Please test carefully before relying on it for important workloads.
Useful things to test:
OpenVPN reconnects behave as expected
WireGuard startup and reconnect behavior work as expected when the override is enabled
If you find bugs, edge cases, provider-specific issues, or Qubes-version differences, please open an issue or submit a pull request.
The intent is to make this easier to audit and more robust for the community
while preserving the basic architecture that made the original Qubes-vpn-support project
useful.