We run a caching name server on all our servers. This speeds up name lookups, and reduces network load to external name servers a bit.
On some servers, we also serve DNS for several domains to the outside world. We use BuddyNS as our secondary servers; they use AXFR to transfer changes to our domains.
We decided to use BIND 9, as it is popular and well supported. BIND 9 was completely rewritten with security in mind, and so it seems to have a lot fewer security issues than BIND 4 and BIND 8 did. We decided to put BIND into a chroot jail, as it's pretty simple to do and well-documented. This will protect us from most BIND and DNS exploits that do turn up.
First, install the required packages:
sudo apt-get install bind9 bind9utils bind9-doc dnsutils
Debian automatically starts the daemon, but we're going to change a lot of its config, so we should stop the daemon until we're done:
sudo /etc/init.d/bind9 stop
Next build out /var/lib/bind/chroot
to contain enough so that bind9 can run chrooted within it:
sudo mkdir -p /var/lib/bind/chroot/{etc,dev,var/cache/bind,var/run/named} sudo mknod /var/lib/bind/chroot/dev/null c 1 3 sudo mknod /var/lib/bind/chroot/dev/random c 1 8 sudo chmod 660 /var/lib/bind/chroot/dev/{null,random} sudo chmod 775 /var/lib/bind/chroot/var/{cache/bind,run/named} sudo chown -R bind:bind /var/lib/bind/chroot/{etc,var/*,dev} sudo rm -rf /var/run/named /var/cache/bind sudo ln -s /var/lib/bind/chroot/var/run/named /var/run/named sudo ln -s /var/lib/bind/chroot/var/cache/bind /var/cache/bind
Copy the configuration into the chroot directory, and link back to the original locations, so we can update the configuration from the original config-file location:
sudo mv /etc/bind /etc/bind.dist sudo cp -a /etc/bind.dist /var/lib/bind/chroot/etc/bind sudo ln -s /var/lib/bind/chroot/etc/bind /etc/bind
Next edit /etc/default/bind9 to tell it to start up chrooted to /var/lib/bind9/chroot:
sudo sed -i -e 's|OPTIONS="-u bind"|OPTIONS="-u bind -t /var/lib/bind/chroot"|' /etc/default/bind9
Edit /etc/bind/named.conf.options
and tell it which interfaces to listen on, and who to forward requests to if we don't have the answer cached. We also include names for a few backup forwarders, in case we decide to use them at a later date.
acl loopback {127.0.0.1;}; # The "localhost" ACL is pre-defined, but includes all interfaces. acl external_subnet {192.168.210.0/24;}; acl buddyns {173.244.206.26; 88.198.106.11; 74.117.59.111;}; # Allow AXFR to these addresses for BuddyNS to provide secondary/authoritative DNS for us. acl opendns {208.67.220.220; 208.67.222.222;}; # OpenDNS public DNS servers. acl verizon_dns {4.2.2.1; 4.2.2.2; 4.2.2.3; 4.2.2.4; 4.2.2.5; 4.2.2.6;}; # Verizon public DNS servers. options { directory "/var/cache/bind"; listen-on {loopback; external_subnet;}; # Listen on loopback interface (for DNS caching), plus any interface on our external subnet (for queries and transfers). forwarders {208.67.220.220; 208.67.222.222;}; # Forward queries here, unless we're authoritative. (Cannot use named ACLs here.) allow-transfer {buddyns; localhost;}; # Allow AXFRs to BuddyNS (so they can mirror us), and all local interfaces (for testing). auth-nxdomain no; # Conform to RFC1035. version none; # Don't publicize our version number. };
For caching-only servers, remove the external_subnet
from listen-on
, and remove the allow-transfer
line.
To get logging out of the chroot jail, we need to set up a socket within the jail, and have the syslog daemon listen to it.
Since Debian 5+ uses rsyslog, we simply had to find the config option that would listen on an additional UNIX socket:
sudo sh -c 'echo "\$AddUnixListenSocket /var/lib/bind/chroot/dev/log" > /etc/rsyslog.d/bind9.conf'
Then restart the logging daemon:
sudo /etc/init.d/rsyslog restart
Start the named server:
sudo /etc/init.d/bind9 start
If startup fails, tail the /var/log/syslog
file to look for errors. The most likely error is forgetting a semi-colon somewhere in the config file.
Edit /etc/resolv.conf
to tell clients to use localhost to resolve DNS names. Again, we include a few other name servers just as documentation.
DOMAIN=$(hostname -d) sudo sh -c 'cat > /etc/resolv.conf' <<EOF domain $DOMAIN nameserver 127.0.0.1 #nameserver 208.67.220.220 # OpenDNS public DNS server #nameserver 208.67.222 .222 # OpenDNS public DNS server EOF
We also need to delete any dns-* lines from /etc/network/interfaces
, as they cause /etc/resolv.conf
to be updated when the interface comes up.
sudo sed -i /etc/network/interfaces -e '/^.*dns-.*/d'
Run nslookup
and/or dig
to resolve some DNS names. Make sure you get answers back from 127.0.0.1.
Run some client programs to make sure they are resolving host names properly.
Check /var/log/daemon.log
and /var/log/syslog
for startup/shutdown info from the bind9 daemon.
Run sudo rndc status
to check the status of the server.
Run sudo rndc stats
and then read /var/cache/bind/named.stats
to get server stats, including number of successful and failed DNS lookups.
The OpenDNS servers are publicly available for anyone to use. It probably doesn't make sense to use them on a server though, because they send unknown addresses to their own servers. Their servers contain search pages for web access; I'm not sure what happens with other services.
The 4.2.2.x addresses are supposedly Verizon's publicly-available DNS server that anyone can use.
We should probably hit the root servers instead of forwarding to OpenDNS. Or forward to the DNS servers provided by our ISP.
Details using BIND under chroot is available on the Debian Wiki page on BIND, as well as the Bind-Chroot-Howto for Debian on HowtoForge.