Enforcing correct DNS upstreams for internal zones
When you’re frequently working with internal DNS zones of a company
whose DNS server sits behind a VPN, you’ll probably soon encounter DNS
shenanigans where you’ll find that resolving internal domain names is a
lot more tricky than it should be. I’ve found a way that works using
dnsmasq
, but I also found that you need to be careful to
keep an overly-eager NetworkManager in check.
The problem
When you’re using a laptop that connects to many different wifi
networks, which upstream DNS servers do you use? Usually it’s desirable
to use the ones from the LAN you’re currently connected to, so that
internal DNS names resolve properly wherever you are. Thus just
configuring 8.8.8.8
as the upstream usually sucks. So
you’ll need to find a way to have NetworkManager and your DNS resolver
interact properly.
What works
apt-get install resolvconf dnsmasq
, then create a file
named /etc/dnsmasq.d/company.conf
that points to the
internal DNS server:
server=/internal.company.com/10.1.2.3
Now restart dnsmasq
, then restart
NetworkManager
, connect your VPN and things should work.
NetworkManager should call into resolvconf
to set the
upstream server when the Wifi is connected, resolvconf
knows about dnsmasq
and writes the changes into
/run/dnsmasq/resolv.conf
, and that one is included by the
Debian dnsmasq
configuration:
[10:36]root@mz1030-nb251:~# ps aux |grep dnsmasq
dnsmasq 10783 0.0 0.0 14492 2532 ? S 10:16 0:00 /usr/sbin/dnsmasq -x /run/dnsmasq/dnsmasq.pid -u dnsmasq -r /run/dnsmasq/resolv.conf -7 /etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new --local-service --trust-anchor=something_long_and_boring
Notice the -r /run/dnsmasq/resolv.conf
bit, this is the
relevant part. All the name servers configured by the system will end up
in this file, and the actual /etc/resolv.conf
will always
point directly at dnsmasq
.
The resolvconf
package is key in making this work, so be
sure to have it installed.
What doesn’t work
Before this, I tried solving this through NetworkManager. NM also
offers a dnsmasq.d
directory where custom configuration
files can be placed, so this looked like the perfect place to put these
configs (and in fact, I found guides on the web that show to do it this
way). Then you set dns=dnsmasq
in
/etc/NetworkManager/NetworkManager.conf
and think you’re
done, only to find out that the configuration does not care at all about
what you configured.
It took me some digging to find out why that is, and
journalctl -u NetworkManager
had the answer: dnsmasq loads
the config just fine, but when NetworkManager calls it via DBus to
update the upstream name servers, these seem to override the
configuration files and dnsmasq
even explicitly logs that
it is now using the new upstreams for the internal DNS zones, which is
precisely the opposite of what I want it to do. Luckily I had an old
laptop lying around with the config that worked.