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.