A Method To Prevent DNS Leaks With Wireguard

Introduction

If you are trying to set up a wireguard based VPN on a system like Ubuntu 20.04 and are using systemd-resolved, you may have come across the following problem: even if you set the wildcard domain on your wireguard interface your DNS queries do not exclusively go to the server(s) you configured for your wireguard tunnel i.e. you have a DNS leak. This happens because earlier versions of NetworkManager set the wildcard domain on any interface it expects to be used for DNS queries, so even if the wildcard domain is set on another interface (like wireguard) DNS queries will still go to that interface [1]. Here is an example:

$ resolvectl domain
Global:
Link 3 (<wifi interface>):
Link 2 (<ethernet interface>): ~. <my local domain>

This is obviously not desirable. However this output lends itself well to text manipulation, which will be the basis for a solution. The standard solution for users of newer versions of NetworkManager will also be shown.

Solution

The solution comes from the facts that the local domain on any interface can be easily acquired through manipulation of the output from the command above with standard tools and the wireguard interface configuration file easily allows the running of commands before/after the interface goes up/down. The resolvectl man page explains how to set domains on interfaces [2]. The solution is to use text manipulation of the output from the command above to get the local domain, set the normal network interface to just have the local domain as its search domain, and finally set the wildcard domain as the wireguard interface’s search domain (the following goes in the [Interface] section of the wireguard interface’s configuration file):

PreUp = resolvectl domain <network interface> "$(resolvectl domain <network interface> | cut -d ' ' -f 5)"
PostUp = resolvectl dns %i <wireguard dns address>; resolvectl domain %i "~."
PreDown = resolvectl revert %i;
PostDown = resolvectl domain <network interface> "~." "$(resolvectl domain <network interface> | cut -d ' ' -f 4)"

The PreDown line is just standard clean up and the PostDown line ensures that this solution will work if the wireguard interface is brought up and down multiple times. Of course, if you are using a newer version of NetworkManager you only need to do the following to prevent DNS leaks:

PostUp = resolvectl dns %i <wireguard dns address>; resolvectl domain %i "~."
PreDown = resolvectl revert %i

References

[1] https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/687?commit_id=e757b35c6da03302746d5740ba835ab83e0324b1

[2] https://www.man7.org/linux/man-pages/man1/resolvectl.1.html

2 comments

  1. Thank you for this concise but well-explained guide!
    I was struggling quite a bit with resolving my local domains, but now it not only works, but I learned a bit about Linux networking as well 😉

Leave a comment

Your email address will not be published. Required fields are marked *