You are not logged in.

#1 2025-05-10 19:54:50

sewes56244
Member
Registered: 2025-05-10
Posts: 3

[SOLVED] How do I setup a VPN with split DNS (or DNS per interface)?

I've tried Private Internet Access and ProtonVPN with their .confs/.ovpn files. IPv6, IPv4. I've tried dnsmasq, openresolv, systemd-resolveconf, just systemd-resolved, etc.
In every setup, /etc/resolve.conf adds both DNS servers. How it leaks is determined by DNS Domain '~' (seen via resolvectl status).
If that is used for neither connection, the real DNS is used for both. If that is used for the real connection, the real DNS is used for both. If that is used for the VPN connection, the VPN DNS is used for both connection. But really, the VPN connection should use its DNS, and the real connection should use its DNS.

This article further explains the usage of ~ and ~. https://fedoramagazine.org/systemd-reso … split-dns/
For nmcli enjoyers, the equivelant of "Use this connection only for resources on its network" is "ipv4.never-default yes."

I don't want my VPN always in use, but I do want it always on. I can then route applications to it, and they will always use it when started. The two options "resources on its network" and "never-default" does that. Yet, DNS isn't appearently a resource on that connection, only the IP is so it isn't being respected there.

Basically, how do I set it up so esp4s0 (Wired connection 1) uses 9.9.9.9 and tun0 uses its VPN DNS (or any other than 9.9.9.9)?
In a simple way, like have a network interface use its specified DNS. I don't want to have to specify which IP should use which DNS (mine are dynamic and so it will be sad).
ProtonVPN with its Wireguard .conf applies ~ default,

My setup is simple and very out-of-the-box. NetworkManager, systemd-resolved, and nothing else is used. /etc/resolv.conf is managed by NetworkManager (default). IPv4 only to eliminate IPv6 leaks. Wireguard because it's included in the kernel, networkmanager-openvpn isn't (but every setup, including openvpn, experience the same behavior where only adding/removing ~ changes the DNS leak).
none of the following: openresolv, systemd-resolvconf, wireguard-tools, networkmanager-openvpn, dhcp, dhcpclient, dnmasq, systemd-networkd, systemd-firewalld, PiHole, servers.

I've also tried shortcuts, before you mention it, like using the VPN provider's official GUIs. For PIA, this works great, not a single problem. For ProtonVPN, it always fails to connect.

Traceback (most recent call last):
  File "/usr/lib/python3.13/asyncio/tasks.py", line 507, in wait_for
    return await fut
           ^^^^^^^^^
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/lib/python3.13/concurrent/futures/_base.py", line 449, in result
    return self.__get_result()
           ~~~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3.13/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/lib/python3.13/site-packages/proton/vpn/core/connection.py", line 394, in connect
    await self._on_connection_event(
        events.Up(events.EventContext(connection=connection))
    )
  File "/usr/lib/python3.13/site-packages/proton/vpn/core/connection.py", line 463, in _on_connection_event
    event = await self._handle_on_event(event)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/site-packages/proton/vpn/core/connection.py", line 448, in _handle_on_event
    return await self._update_state(new_state)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/site-packages/proton/vpn/core/connection.py", line 485, in _update_state
    new_event = await self._current_state.run_tasks()
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/site-packages/proton/vpn/connection/states.py", line 240, in run_tasks
    await self.context.kill_switch.enable(
    ...<2 lines>...
    )
  File "/usr/lib/python3.13/site-packages/proton/vpn/backend/linux/networkmanager/killswitch/default/nmkillswitch.py", line 63, in enable
    await self._ks_handler.add_full_killswitch_connection(permanent)
  File "/usr/lib/python3.13/site-packages/proton/vpn/backend/linux/networkmanager/killswitch/default/killswitch_connection_handler.py", line 155, in add_full_killswitch_connection
    await _wrap_future(
        self.nm_client.add_connection_async(kill_switch.connection, save_to_disk=permanent)
    )
  File "/usr/lib/python3.13/site-packages/proton/vpn/backend/linux/networkmanager/killswitch/default/killswitch_connection_handler.py", line 53, in _wrap_future
    return await asyncio.wait_for(
           ^^^^^^^^^^here^^^^^^^^^^^^^
    ...<2 lines>...
    )
    ^
  File "/usr/lib/python3.13/asyncio/tasks.py", line 506, in wait_for
    async with timeouts.timeout(timeout):
               ~~~~~~~~~~~~~~~~^^^^^^^^^
  File "/usr/lib/python3.13/asyncio/timeouts.py", line 116, in __aexit__
    raise TimeoutError from exc_val
TimeoutError

That would be, proton-vpn-gtk-app. Turning on killmode, while this error is present when it's off, left me still unable to connect to the VPN servers but as a bonus also unable to use my real connection. The app then starts to crash on boots so killmode can't be turned off. I really want to setup NetworkManager just using nmcli or its GUIs. I've been supporting PIA for a while, I'd now like to support Proton for a while.


Here is resolvctl status when the VPN is active. I've specified 9.9.9.9 as my DNS. It was until the VPN were enabled.

Global
           Protocols: +LLMNR +mDNS -DNSOverTLS DNSSEC=no/unsupported
    resolv.conf mode: foreign
  Current DNS Server: 10.2.0.2
         DNS Servers: 10.2.0.2 9.9.9.9
Fallback DNS Servers: 1.1.1.1#cloudflare-dns.com 9.9.9.9#dns.quad9.net 8.8.8.8#dns.google 2606:4700:4700::1111#cloudflare-dns.com 2620:fe::9#dns.quad9.net 2001:4860:4860::8888#dns.google

Link 2 (enp4s0)
    Current Scopes: LLMNR/IPv4 mDNS/IPv4
         Protocols: -DefaultRoute +LLMNR +mDNS -DNSOverTLS DNSSEC=no/unsupported
     Default Route: no

Link 3 (wlp0s20f3)
    Current Scopes: none
         Protocols: -DefaultRoute +LLMNR +mDNS -DNSOverTLS DNSSEC=no/unsupported
     Default Route: no

Link 12 (tun0)
    Current Scopes: DNS
         Protocols: +DefaultRoute +LLMNR +mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 10.2.0.2
       DNS Servers: 10.2.0.2
        DNS Domain: ~.
     Default Route: yes

wireguard conf files from ProtonVPN automatically applies ~ for DNS domains. I've described earlier what happens when I switch them around. I basically want to tell enp4s0 to stop using VPN DNS. The only way I've found is to remove ~ but then the inverse happens, also described that.

Here is nmcli output.

enp4s0: connected to enp4s0
	"Realtek Killer"
	ethernet (r8169), 98:EE:CB:3G:A5:2B, hw, mtu 1500
	ip4 default
	inet4 192.168.0.33/24
	route4 192.168.0.0/24 metric 100
	route4 default via 192.168.0.1 metric 100

tun0: connected to tun0
	"tun0"
	wireguard, sw, mtu 1420
	inet4 10.2.0.2/32

lo: connected (externally) to lo
	"lo"
	loopback (unknown), 00:00:00:00:00:00, sw, mtu 65536
	inet4 127.0.0.1/8

wlp0s20f3: unavailable
	"Intel Tiger"
	wifi (iwlwifi), 98:EE:CB:3G:A5:2B, sw disabled, hw, mtu 1500

DNS configuration:
	servers: 10.2.0.2
	interface: tun0
	type: vpn

	servers: 9.9.9.9
	interface: enp4s0

This is really stuff that doesn't tell you anything more than I already said but if I didn't include them then people would've complained. Arch users likes logs.
Any help would be greatly appreciated. In whatever form, like telling me you use dnsmasq and it specifically avoids this so I should try it again (hopefully with some quick steps, because I followed the wiki but this still happened). Thanks!
If this isn't Arch enough, please do send me wherever might be more appropriate to ask this.

Last edited by sewes56244 (2025-05-12 11:29:38)

Offline

#2 2025-05-11 17:07:10

-thc
Member
Registered: 2017-03-15
Posts: 892

Re: [SOLVED] How do I setup a VPN with split DNS (or DNS per interface)?

sewes56244 wrote:

I don't want my VPN always in use, but I do want it always on. I can then route applications to it, and they will always use it when started. The two options "resources on its network" and "never-default" does that. Yet, DNS isn't appearently a resource on that connection, only the IP is so it isn't being respected there.

Basically, how do I set it up so esp4s0 (Wired connection 1) uses 9.9.9.9 and tun0 uses its VPN DNS (or any other than 9.9.9.9)?

Think from the application "downward" to your Ethernet NIC: App -> DNS resolver -> IP -> Routing (VPN) -> Ethernet.

How should an application know where to send the DNS request before knowing it's IP?
Is there something that sets the "VPN DNS requests" apart from the "normal" ones?

Offline

#3 2025-05-11 19:24:36

sewes56244
Member
Registered: 2025-05-10
Posts: 3

Re: [SOLVED] How do I setup a VPN with split DNS (or DNS per interface)?

Maybe I'm just fundamentally misunderstanding routing on Linux, but either everyone is
-running their VPN 24/7, while downloading their entire Steam libraries, hundred Gig modpacks, Pacman -Syu, logging into accounts with a VPN connection and getting banned for using a VPN
-turning their VPNs online on-demand, like if they're self-hosting a site or seeding archiso torrents. Before turning it off to game. Maybe running it just 2 hours a day is good enough, sorry Australians
-using their VPNs GUI apps via AUR, eliminating many VPN providers completely
-leaking DNS like crazy.

I expect the ability to tell a network interface what DNS server to use. When a connection attempt is made, it will decide what DNS server to use before proceeding with the connection.
tun0 should use VPN DNS, enp4s0 should use 9.9.9.9 (or my ISP DNS).
But if that's not how it's done, then I want to recreate what the VPN GUI apps out there do. Or I want any alternative? I don't know what to do

"App -> DNS resolver -> IP -> Routing (VPN) -> Ethernet."
Private Internet Access can do split tunneling, even reverse split tunelling. i.e. either only selected applications use the VPN connection or all but some do. And they use their respective DNS.
PIA creates tun0, from GUI the DNS server can be selected, tun0 uses that DNS server while the other applications bypassing the VPN uses its own DNS (like 9.9.9.9).
I don't know how it does it and in what order it would change your description of flow of routing.

Is this some incredibly rare concept other VPN providers and DNS solutions just don't do?

Excuse any errors in these quick snippets. I started to write them from memory just to record-keep.

# Just NetworkManager.
systemctl disable --now systemd-resolved
rm -r /etc/resolve.conf
echo "nameserver 9.9.9.9" > /etc/resolve.conf
systemctl restart NetworkManager
# Only 9.9.9.9 will be used, even if you specify DNS via nmcli connection modify and even if resolvectl status shows them using it.
# NetworkManager and systemd-resolved
systemctl enable --now systemd-resolved
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
resolvectl dns enp4s0 9.9.9.9
resolvectl dns tun0 8.8.8.8
systemctl restart NetworkManager
# systemd-resolve will add both 8.8.8.8 and 9.9.9.9 in /etc/resolve.conf, and both will be used with the first line having more priority.
# i.e. tun0 would use 9.9.9.9.
# you can make it permanent by adding tun0.conf to nano /etc/systemd/network/ but it won't work if the temporary doesn't.
# this is funny because Piotr's response systemd-resolved is all that you need, https://serverfault.com/questions/423882/configure-a-dns-server-per-nic-interface-eth0-eth1
# Note: NetworkManager does not support using systemd-resolved's resolvconf interface (resolvectl(1) §COMPATIBILITY WITH RESOLVCONF(8)) which is provided by systemd-resolvconf. 
# NetworkManager and dnsmasq
 server=/example.com/8.8.8.8
   interface=tun0
   server=9.9.9.9
   interface=enp4s0
#wont work: server=9.9.9.9 and interface=tun0 means it will use 9.9.9.9 for tun0, but I can't specify for multiple. The global DNS will become 9.9.9.9 and I can't add server=enp4s0=8.8.8.8 because it will treat the new server=8.8.8.8 as global too.
# NetworkManager and unbound  
```
  server:
       interface: 127.0.0.1
       access-control: 127.0.0.0/8 allow

   forward-zone:
       name: "."
       forward-addr: 8.8.8.8

   forward-zone:
       name: "."
       forward-addr: 9.9.9.9
```
This doesn't work, because how do I specify what forward-zone is for what interface? I can't?
# NetworkManager and openresolv
works the same as systemd-resolved.
/etc/resolve.conf just gets more nameservers = 9.9.9.9. 
# NetworkManager and bindtointerface
It uses LD_PRELOAD to bind an application to a network interface, and that works. But it has an DNS_OVERRIDE but that doesn't work?

https://bbs.archlinux.org/viewtopic.php?id=245541
This conversation simply seems to suggest that NetworkManager can't do it, implying that something else can do it?

https://serverfault.com/questions/87210 … ic-domains
explains that /etc/resolv.conf is just the only Linux resolver there is, no support was ever made for other DNS behavior. So we got dnsmasq, unbound, etc, but they also don't support split DNS?

The links I've mentioned were questions asked by others desiring the same as me, I think. Surely someone out there has done what I want other than VPN GUI providers?
If not, what should I do? I guess I can just keep on using PIA GUI via AUR, in spite of their parent company's problems they appearently do provide a service very few does.
I really like having browser A use my real IP with Quad9 DNS and browser B with VPN IP and VPN DNS, while all my games bypass and use Quad9, while my torrent app seeding archisos (I collect them) uses my VPN and VPN DNS.

Offline

#4 2025-05-11 19:32:29

Whoracle
Member
Registered: 2010-11-02
Posts: 116

Re: [SOLVED] How do I setup a VPN with split DNS (or DNS per interface)?

You seem to have some misconceptions here. To make sure I'm going high-level and absolute basics:

DNS entries are pointers to IP addresses (well, A and AAAA records are)
Routing is in the IP Level and happens AFTER the DNS queries
One can absolutely route specific IP-Traffic via a VPN and still talk to, say Steam via the ISP
Your system can have one DNS server that is polled, followed by more if the first one is not responding. Not if the first one doesn't return a valid answer

So "Use DNS of my VPN for VPN stuff" does not make sense. HOWEVER, you can delegate specific search domains to specific DNS servers, which is what a lot of companies usually do. They have some kind of company.local DNS zone, and the DNS servers for that get pushed via VPN, so your system will ask the VPN DNS server for *.company.local. If the answer is an IP address that's not behind the VPN though, the traffic will still get routed through your ISP.

I for example have my company DNS as well as a bunch of Azure Private DNS zones set up that way to talk to our Azure-Stuff that's not internet-exposed.

Does that clarify things for you?

Online

#5 2025-05-11 19:56:42

-thc
Member
Registered: 2017-03-15
Posts: 892

Re: [SOLVED] How do I setup a VPN with split DNS (or DNS per interface)?

I have a working "split DNS" setup - but the key is that all my VPNs have their own "DNS search domains" (targets) and DNS queries do not overlap.

sewes56244 wrote:

I really like having browser A use my real IP with Quad9 DNS and browser B with VPN IP and VPN DNS, while all my games bypass and use Quad9, while my torrent app seeding archisos (I collect them) uses my VPN and VPN DNS.

How should browser B "know" to ask the VPN DNS? You want to split the DNS queries based on the source ("browser B", torrent) - IMHO not possible without additional software.

If third-party VPN clients actually can do this it's news to me.

Offline

#6 2025-05-11 20:30:08

topcat01
Member
Registered: 2019-09-17
Posts: 186

Re: [SOLVED] How do I setup a VPN with split DNS (or DNS per interface)?

-thc wrote:

I have a working "split DNS" setup - but the key is that all my VPNs have their own "DNS search domains" (targets) and DNS queries do not overlap.

sewes56244 wrote:

I really like having browser A use my real IP with Quad9 DNS and browser B with VPN IP and VPN DNS, while all my games bypass and use Quad9, while my torrent app seeding archisos (I collect them) uses my VPN and VPN DNS.

How should browser B "know" to ask the VPN DNS? You want to split the DNS queries based on the source ("browser B", torrent) - IMHO not possible without additional software.

If third-party VPN clients actually can do this it's news to me.

You can do this by running the vpn inside a network namespace, and then using the application inside the namespace.

Offline

#7 2025-05-11 20:52:31

sewes56244
Member
Registered: 2025-05-10
Posts: 3

Re: [SOLVED] How do I setup a VPN with split DNS (or DNS per interface)?

Thank you two for your elaborations on how routing works.

I've started to accept that the last few hours that system DNS is global so each network interface cant have its own, whatever ("split DNS" is).

But yes, that is how PIA VPN works. I haven't found any other VPN that allows torrenting, has this functionality, and supports Linux.
Even Arch calls them the leading VPN here, https://archlinux.org/donate/

ProtonVPN's .ovpn/.conf (both for OpenVPN and Wireguard) sets up a DNS server and ~ as DNS domain. That means, when online, all traffic is routed through it (the first link in first post explains ~).
Either ProtonVPN doesn't have a company.local or they do but it's only "happens" with their GUI, which seems to only work on Gnome (no River, Hyrpland, etc).

PIA's .ovpn (no Wireguard on Linux yet) doesn't set a DNS server or a DNS domain. I don't know if it'll leak. But with their GUI from AUR, it didn't leak.
Set browsers to use system DNS, and, say, Waterfox would use PIA DNS (or as specified) while Librewolf would use real IP with real DNS (or any I specified via NetworkManager, the system DNS ig).

If I'm meant to create the local DNS zone, what do I use? dnsmasq? How do I specify which IPs/applications should enter it? Surely it's per-application, because I can't specify for each website IPs and urls (too many). Is there an Arch wiki entry for that?
As it stands, I'm still pretty clueless on how to achieve that functionality. I'm okay using other software beyond NetworkManager too. Vopono, mentioned https://wiki.archlinux.org/title/ProtonVPN , can run temporary network namespaces but I weren't so sure that's where I'm meant to be going to figure this all out?

Offline

#8 2025-05-11 20:56:21

topcat01
Member
Registered: 2019-09-17
Posts: 186

Re: [SOLVED] How do I setup a VPN with split DNS (or DNS per interface)?

Split-horizon DNS works perfectly if one does not use third party tools. For example, both NetworkManager and systemd-networkd can do this with my wireguard VPNs. However, almost no third-party client can do this right in my experience and and hamfists /etc/resolv.conf; horrific!

Currently my company uses Cisco AnyConnect (or, as I call it, Any$hit) which is the absolute worst. In the past they used amazon vpn which allowed using openvpn as client on Linux. While not ideal, it allowed me to run a custom script to wrap the vpn connection in its own network namespace (ideal), or use the DNS info to set up split horizon using resolvectl. If your VPN allows running custom scripts then you can do this quite easily.

Last edited by topcat01 (2025-05-11 21:12:19)

Offline

#9 2025-05-12 06:34:04

-thc
Member
Registered: 2017-03-15
Posts: 892

Re: [SOLVED] How do I setup a VPN with split DNS (or DNS per interface)?

topcat01 wrote:

You can do this by running the vpn inside a network namespace, and then using the application inside the namespace.

Thanks for the reminder - since network namespaces are (in my mind) mostly academic I tend to forget them.

On the other hand the scenario sewes56244 wants fits namespaces quite well.

Offline

#10 2025-05-12 23:26:33

topcat01
Member
Registered: 2019-09-17
Posts: 186

Re: [SOLVED] How do I setup a VPN with split DNS (or DNS per interface)?

I went all in on the namespaces thing. It works really well. I use mount namespaces (netns creates an associated mount ns as an anchor) and cgroups a lot. The totally awesome and unique thing about Linux, and systemd, is the pervasive namespacing/containerisation out of the box. As a result I now find it difficult to use other OSs.

Offline

Board footer

Powered by FluxBB