Fixing wg-quick's DNS= directive with a hatchet
Jason A. Donenfeld
Jason at zx2c4.com
Thu Oct 26 00:43:49 CEST 2017
A sensible way to manage DNS on a distribution is via openresolv's
resolvconf(8) . Various networking utilities can call resolvconf
with various arguments, and resolvconf will handle everything.
Resolvconf offers two useful options: metric via -m and exclusive via
-x. The metric option controls the order of entries in the
resultant /etc/resolv.conf. The exclusive option sets the provided
nameserver to be the _only_ one listed in /etc/resolv.conf. This
general system works great and is what wg-quick(8) uses at the moment.
Debian (and by extension Ubuntu), however, ship with a butchered
resolvconf  that silently ignores the -m and -x options. Instead,
Debian's resolvconf orders the entries of /etc/resolv.conf according
to some pre-defined interface naming heuristics, and does not allow
for an exclusive mode at all. wg-quick thus uses openresolv's -m 0 and
-x arguments, described above, but does so for an interface called
"tun.wg0". The "tun." prefix tricks Debian's resolvconf into ordering
WireGuard entries first, but still not exclusively.
Meanwhile, Fedora does not ship resolvconf at all, and instead either
uses NetworkManager or dhclient-script, depending on the configuration
of a variable inside of some file in /etc/sysconfig/network-scripts/.
I haven't really looked at how to coherently interface with all the
possibilities, and I'm kind of reluctant to look.
So I have three options: 1) require openresolv, 2) punt the issue to
distro package managers, by making wg-quick "source" a file in
/usr/lib/wg-quick/dns.bash that provides the distro-specific
DNS-setting function, or 3) the hatchet, described below.
Before I describe the hatchet, though, it might be worthwhile to
remind ourselves of the three goals of DNS setting in this
environment: a) be the exclusive DNS entry, b) restore the previous
settings when the wireguard interface is removed, and c) not allow
other things on the system (like roving dhcp daemons) to overwrite our
The hatchet works as follows. On interface addition:
# echo nameserver 188.8.131.52 > /etc/resolv.conf.wg-quick.wg0
# [ -e /etc/resolv.conf ] || touch /etc/resolv.conf
# mount -Br /etc/resolv.conf.wg-quick.wg0 /etc/resolv.conf
# unlink /etc/resolv.conf.wg-quick.wg0
On interface removal:
# umount /etc/resolv.conf
This achieves all goals. Goal (a) is achieved because we're mounting
over the existing /etc/resolv.conf, so we blow away old entries. Goal
(b) is achieved because unmounting reveals the original file just
below it. Goal (c) is achieved because we're mounting as read-only; we
can't even remove the file without unmounting.
So, I'm leaning over going with (3) the hatchet rather than (2) the
distros, because I think this will likely work more universally.
However, it's a hatchet. And hatchets have sharp dangerous blades, and
Linux is not the rewarding bush terrain of Gary Paulsen.
Can anybody think of any potential issues with this?
More information about the WireGuard