DNS Leaks with Network Namespaces
If you use a Virtual Private Network (VPN) in a network namespace, your DNS requests may be leaked to your DNS provider if you use systemd-resolvd as a DNS resolver. These DNS requests can expose the sites you are visiting or your general geographic location whilst using your VPN.
In general, a DNS leak is when DNS requests that should be sent to your VPN provider for name resolution are sent to your otherwise regular DNS provider. Tools such as DNS Leak Test can show you who is resolving your DNS requests. For a properly setup and secured VPN, the only servers responding to your DNS requests should be owned by your VPN provider.
When running a system-wide VPN, DNS leaks may be prevented by simply ensuring your system is using your VPN provider’s DNS servers. Generally this means checking NetworkManager or /etc/resolv.conf
points to the right IP addresses.
However, as mentioned in my Wireguard article, a network namespace may be used to run your VPN in a parallel and isolated networking stack such that only certain applications may use the VPN. Essentially, when an application is launched in that network namespace, the sole way for that application to access the network is through the VPN’s tunneling network adapter. So if the application can only access the network via the VPN how do DNS requests bypass it? Afterall, DNS requests are just simple network requests.
To understand, we must first discuss how an application resolves a host or domain name. Generally speaking, on GNU/Linux systems, most applications use the GNU C (glibc) library at some point to resolve host and domain names. The GNU C library decides how to resolve that name by reading the Name Service Switch configuration file at /etc/nsswitch.conf
.
Below is an example of /etc/nsswitch.conf
configuration file:
|
|
In this file we see many name categories (passwd
, group
, shadow
, hosts
, etc.) and then a list of name resolution methods (files
, mymachines
, dns
, etc.) for each category.
Of interest is the hosts
name category. The hosts
name category tells the GNU C library how to resolve host or domain names.
In this example there are 5 methods the GNU C library will try, in sequence, to resolve a host or domain name. They are:
files
, resolve a host or domain name using/etc/hosts
mymachines
, if the host name is for a container registered with systemd-machined, resolve the IP of that containermyhostname
, resolve the requesting system’s host nameresolve
, resolve the host or domain name using systemd-resolvddns
, resolve the domain name using the DNS servers in/etc/resolv.conf
Take note of resolve
, this method resolves the host or domain name using systemd-resolvd. It accomplishes this by making a name resolution request to the systemd-resolvd daemon over D-Bus. D-Bus is an inter-process communication mechanism (a method by which two independant process can communicate) that is not isolated by a network namespace.
Therefore, what is happening is that inside the network namespace an application makes a name resolution request. The files
, mymachines
, and myhostname
methods fail to resolve the name, so we reach the resolve
method. The request is made to systemd-resolvd over D-Bus. Since systemd-resolvd is not running in the network namespace it resolves the domain name using the DNS servers in /etc/resolv.conf
over your unencrypted internet connection.
Aha! Now how do we solve this?
This is actually pretty simple to solve. Within a network namespace, the file /etc/netns/<name>/nsswitch.conf
is bind mounted to /etc/nsswitch.conf
. Essentially, applications running in the network namespace will see the contents of /etc/netns/<name>/nsswitch.conf
when reading /etc/nsswitch.conf
. So simply copy your /etc/nsswitch.conf
file to /etc/netns/<name>/nsswitch.conf
where <name>
is the name of your network namespace, and remove the resolve [!UNAVAIL=return]
method in the hosts
category. Now name resolution will fallback to the dns
method which uses the DNS servers in /etc/resolv.conf
to resolve the name and performs this resolution in the application process within the network namespace!
Below is a corrected /etc/netns/<name>/nsswitch.conf
configuration file:
|
|
But wait! What if /etc/resolv.conf
contains your ISP’s DNS servers? This can still leak your requests even if they come from a VPN. For example, your ISP can see that you have one connection to your VPN server, and that server is sending DNS requests to your ISP. Theoretically, your requests could be correlated through timing. Not ideal.
We can solve this the same way we solved the previous problem. Within a network namespace, the file /etc/netns/<name>/resolv.conf
is bind mounted to /etc/resolv.conf
. So simply create /etc/netns/<name>/resolv.conf
where <name>
is the name of the network namespace, and populate it with your VPN provider’s DNS servers. Alternatively, sufficiently large and public DNS servers such as or Cloudflare’s 1.1.1.1
can be used if your VPN provider does not provide DNS servers.
Below is an example /etc/netns/<name>/resolv.conf
file:
|
|
With these modifications, your DNS requests will not be leaked inadventently when using a VPN within a network namespace.