A few weeks ago, I was asked to help solve a tricky DNS problem on a
laptop that needed to connect to two openvpn networks. The first network
had been set up long ago, and was outside our administrative control. It
forced the client's /etc/resolv.conf to point to a nameserver that was
accessible over the VPN and served a bogus .example-org TLD zone in
addition to acting as a general resolver.
The problem arose when the machine was configured to connect to a new
VPN. Here, clients were configured to connect to vpn.example.org. When
they were in office, the DHCP-supplied nameserver would resolve that to
10.0.0.1. Outside the office, example.org's "real" nameserver would hand
out the server's external IP address. This worked fine, but if the user
connected to the old VPN first, the forced nameserver change meant that
vpn.example.org no longer resolved to 10.0.0.1 inside office. But if we
left resolv.conf unchanged, names in .example-org could not be resolved.
The solution involved many moving parts. First, I set up BIND on the
laptop, and configured it to forward .example-org queries to the old
VPN's nameserver.
zone "example-org" {
type forward;
forwarders { 172.77.66.1; };
}
Then I put "nameserver 127.0.0.1" in /etc/resolv.conf, and disabled the
openvpn script that was changing resolv.conf after connecting. But then
came the real problem—how to make BIND forward all other queries to the
DHCP-supplied resolver, whether in office or outside? Sadly, there is no
way to do this directly. BIND can be told to use a default forwarder or
forwarders, but it has no way to discover which one to use.
The only thing to do is to rewrite the BIND configuration file with new
forwarders (every time we get one via DHCP) and restart BIND. There is a
hackish and confusingly-documented Debian program called resolvconf that
can be used to do this (among many other things). When you install it,
it turns resolv.conf into a symlink to /etc/resolvconf/run/resolv.conf,
and runs the scripts in /etc/resolvconf/update.d when a new nameserver
is discovered (e.g. when the DHCP client brings up an interface). It
also rewrites resolv.conf, which I didn't want (127.0.0.1 was fine).
/etc/resolvconf/update.d/bind takes /etc/bind/named.conf.options,
changes the forwarders {} section, and writes the result to
/var/run/bind/named.options, which you can include in
/etc/bind/named.conf instead of /etc/bind/named.conf.options. It then
restarts named. I disabled the other update scripts, fixed
/etc/apparmor.d/usr.sbin.named (which said /var/run/named/named.options
instead of /var/run/bind/named.options, and as a result named couldn't
read that file), and made /etc/resolv.conf a read-only plain old file
(to discourage resolvconf from rewriting it, which it really wanted to
do).
It works now, but it feels very hackish.