by Jeremy Rauch
Traditionally, BIND has been the nameserver of choice when doing name service on a Unix system. Like many of its close relatives, such as sendmail, it was designed at a time when the internet wasn't even known as the internet, and security wasn't a concern. This has caused more than a few problems over the years, and many point to the age of its codebase, and lack of designed-in security as part of the problem.
djbdns, formerly known collectively as DNScache,is a replacement for BIND, written by Dan Bernstein, the author of qmail, a mail transport agent that has been rapidly growing in popularity in the last few years. djbdns, like qmail, was written as a replacement to the traditional program used for its service, keeping the needs and requirements of the modern internet in mind. By taking a more modular approach, it has become possible to reduce code sizes in to smaller, more manageable chunks, reduce the number of lines of codes run with privileges, and take a more practical approach to security.
That being said, djbdns was designed with security in mind. Each major portion of what is traditionally considered part of name service has been split out in to a variety of daemons and auxiliary programs. The dnscache program itself serves only as a caching DNS server, providing name resolution for outbound requests. Designed with lessons learned from the cache poisoning problems BIND and other name servers had, it is immune to cache poisoning. The tinydns is a basic name server, which responds to incoming requests for domains or IP's it is authoritative for. It never caches any information, and will not respond to inverse queries, zone transfers, and a variety of other problematic query types. pickdns, walldns and rbldns are daemons that serve other types of name service requirements. pickdns is used to load-balancing type applications, where one of a set of IP's will be returned in response to a query, allowing for round-robining of servers. walldns is used to respond to in-addr.arpa request for addresses, without giving up too much information about the local host. rbldns is an IP address listing server, that provides information about IP address queries, based on locally configured list. Like pickdns, none of these services cache any information, or will perform any action other than their primary one. Finally, zone transfer services are split out in to the axfrdns daemon, which responds to request for zone transfers from hosts allowed to perform a transfer, and axfr-get, which will perform a zone transfer, and translate it in to a format usable by djbdns.
djbdns lends itself particularly nicely to split horizon DNS. Since tinydns and the other response daemons included with the djbdns package are so limited in their abilities, exposing only these to the outside world, with a limited set of records (perhaps with walldns serving in-addr.arpa records) allows for a reduction in the risk associated with running a name server. A tinydns can be used internally to map internal names, and individual, or a centralized dnscache used as a caching nameserver. The high degree of modularity makes it easy to do so.
Individually, each portion of djbdns lives inside a chrooted jail. Unlike chrooting BIND, it does not require moving libraries around.1 An easy to use installation and configuration program accompanies each part of djbdns, eliminating the often confusing task of creating devices within the chroot jail. Each chroot jail contains only the information relevant to the service in the jail. This creates a situation where compromising any single part of the system will not yield access to other parts of djbdns, or the system as a whole.
Interested in trying djbdns for your nameserver? Many people think installing djbdns is difficult. Most of the complexity comes from needing to set up a number of other packages first, and from the general unfamiliarity of the configuration files. We'll go through, step by step, installing the parts needed for djbdns, installing a centralized caching nameserver, and a tinydns to resolve our internal domain and IP's.
In a follow up article, we'll discuss installing some of the other portions of djbdns, including walldns for reverse ip resolution, using axfrdns to allow secondary nameservers to perform zone transfers, and setting up a secondary name server using the djbdns tools.
Daemontools is a set of tools for managing Unix processes, similar to the way init will monitor things listed in /etc/inittab.
This is the latest version of the djbdns package at the time of writing.
Compiling daemontools is very straight forward.
> gunzip -c daemontools-0.70.tar.gz | tar xf -
will uncompress and de-tar daemontools. It will create a directory named daemontools-0.70.
> cd daemontools-0.70
We will install everything in /usr/local. If this isn't to your liking, simply edit the conf-home file. If you don't use gcc, edit conf-cc, and if you don't use gcc as a linker, edit conf-ld. Pretty simple.
To compile daemontools, just run:
The build environment is designed to determine what settings are needed without going through the often slow, often overcomplex gyrations that something like autoconf goes through.
Once the compilation completes, run:
# make setup check
This will install the daemontool programs in /usr/local/bin. The svscan program is the control process, which handles starting processes under the control of another part of daemontools, called supervise. To configure svscan:
# mkdir /service
# cat >> /etc/rc.d/rc.local
env - PATH=/usr/local/bin:/usr/sbin:/usr/bin:/bin csh -cf 'svscan /service &'
This will start a copy of svscan monitoring the /service directory. Shortly, we'll start supervising dnscache from there. Obviously, the startup procedure will differ depending on the system you're running on. Solaris users, for instance, will need to create a script in the /etc/init.d directory, and link to it from the /etc/rc2.d directory. You can manually start svscan by running the same command you just added to the startup files. To confirm it is running:
# ps aux|grep sv
root 15635 0.0 0.2 1124 336 pts/0 S 11:48 0:00 svscan /service
root 15639 0.0 0.3 1284 492 pts/0 S 11:49 0:00 grep sv
It is. Great! The first step to installing dnscache is done. We perform an identical setup of daemontools on both machines we'll be using.
Step 3: Compiling and installing djbdns
Again, very simple and straightforward. For a basic installation:
# gunzip -c dnscache-1.00.tar.gz | tar xf -
# cd dnscache-1.00
Altering paths and compilers is, again, accomplished by editing conf-home, conf-cc and conf-ld. To install, run:
# make setup check
Files are installed in /usr/local/bin/ and /usr/local/etc/. Again, an identical installation on both machines we're using.
Step 4: Configure the central dnscache
The first step we'll undertake is configuring a dnscache for use by all the machines on our internal network. We'll use 10.20.30.* for this example setup.
dnscache is configured using the /usr/local/bin/dnscache-conf program. We'll install things in /var/dnscache/. We need to add some accounts first.
# mkdir /var/dnscache
# /usr/sbin/useradd -d /var/dnscache -s /bin/false -c dnscache dnscache
# /usr/sbin/useradd -d /var/dnscache -s /bin/false -c dnslog dnslog
To configure our cache, run:
# /usr/local/bin/dnscache-conf dnscache dnslog /var/dnscache/dnscache 10.20.30.12
# touch /var/dnscache/dnscache/root/ip/10.20.30
This configures a copy of dnscache, using the dnscache user to run the actual daemon, user dnslog to run the logging service, placing it in /var/dnscache/dnscache, and binds it to the ip 10.20.30.12 (the IP of the name server). We then touch the root/ip/10.20.30 file to let dnscache know to accept queries from any host in the 10.20.30.* range. By default, dnscache will not accept queries. All hosts that are allowed to query the dnscache much be in the range of a filename in the root/ip directory.
Finally, to start the dnscache, run:
# ln -sf /var/dnscache/dnscache /service/
This will indicate to svscan to run dnscache. Testing the server reveals:
# nslookup Default Server: ns1.x Address: 10.17.3.8 > server 10.20.30.12 Default Server: [10.20.30.12] Address:10.20.30.12 > www.securityfocus.com Server: [10.20.30.12] Address: 10.20.30.12 Non-authoritative answer: Name: www.securityfocus.com Addresses: 184.108.40.206, 220.127.116.11 > exit #
So our cache is working. Usually, you can specify the nameserver on the command line, and it will query that server:
# nslookup www.securityfocus.com 10.20.30.12 *** Can't find server name for address 10.20.30.12: Non-existent host/domain *** Default servers are not available #
However, nslookup tries to do a reverse lookup on all servers, and fails if names are not available in non-interactive mode. Installing our internal tinydns will eliminate this problem.
Step 5: Configuring the internal tinydns.
Configuring an internal tinydns will require a little more configuration than installing the cache did, as we'll need to construct our zone file, but it is still fairly easy.
For the initial setup, we again need to create accounts.
# /usr/sbin/useradd -d /var/dnscache -s /bin/false -c tinydns tinydns
# /usr/sbin/useradd -d /var/dnscache -s /bin/false -c tinylog tinylog
We have a slight departure from the dnscache installation pages here. Normally, all logging runs under the context of dnslog according to the directions. Instead, we use unique id's for each logging, to prevent compromising multiple logging compartments, should one be compromised.
We're going to bind the internal tinydns to loopback. What happens is, we will configure the dnscache to query the tinydns for forward resolution for our internal domain, as well as reverse dns for the 10.20.30.* range.
# /usr/local/bin/tinydns-conf tinydns tinylog /var/dnscache/tinydns 127.0.0.1
This sets up a tinydns, running as tinydns, logging as tinylog, in the /var/dnscache/tinydns directory, bound to 127.0.0.1. We now need to set up the tinydns configuration file.
There are a number of ways we can set up the configuration file. The best way is to use the scripts included for adding hosts, aliases, child dns servers, and nameservers. We'll configure using the scripts, and then look at the configuration file, and see what it all means.
# cd /var/dnscache/tinydns/root
# ./add-ns internal 10.20.30.12
# ./add-ns 30.20.10.in-addr.arpa 10.20.30.12
# ./add-mx internal 10.20.30.4
# ./add-host ns.internal 10.20.30.12
# ./add-host mail.internal 10.20.30.4
# ./add-alias unagi.internal 10.20.30.4
This will set an NS entry for the domain .internal, with the IP of 10.20.30.12. We then configure reverse DNS by setting the NS entry for the 30.20.10.in-addr.arpa domain also to 10.20.30.12. (Since all queries will be going via the dnscache, these IP's are correct.). An MX record for the internal network points at our mail server, 10.20.30.4. We then add host entries for the actual nameserver, 10.20.30.12, and our mail server, mail.internal on 10.20.30.4. Since some people on the network think the mail server is named unagi, we add an alias, mapping the name unagi to the 10.20.30.4 address. While not necessarily important, this is not mapped as a CNAME, as might be expected, but as another A record. The author of djbdns has his reasons for not creating CNAME records, which are fairly sound. Those more interested in the reasoning behind this should see http://cr.yp.to/dnscache/notes.html.
All the information for tinydns is stored in a file name 'data'. Looking at its contents, we see:
# cd /var/dnscache/tinydns/root
# cat data
The first 2 lines specify the name server information. The 3rd line, the MX record for the internal domain. The next 2 lines create an A record for ns.internal and mail.internal, an a PTR for their reverses. The final line creates an A record for unagi.internal, but no PTR for reverse. If you want further documentation, consult http://cr.yp.to/dnscache/tinydns-data.html.
Finally, to start tinydns, we create a link from the /service directory, to let svscan know to manage the process.
# ln -sf /var/dnscache/tinydns /service
Step 6: Making dnscache work with tinydns
To make this all work, we need to tell the dnscache about the tinydns server. To do so, we create two files in the /var/dnscache/dnscache/root/servers directory. One will be named 'internal', which is our domain, and one is named 30.20.10.in-addr.arpa, used for reverse mapping.
# cat > /var/dnscache/dnscache/root/servers/internal
# cp /var/dnscache/dnscache/root/servers/internal /var/dnscache/dnscache/root/servers/30.20.10.in-addr.arpa
This lets the dnscache process know to consult the nameserver running on 127.0.0.1 for queries for x.internal and 10.20.30.x. You'll need to HUP dnscache process to get it to re-read these files. As it is run by a supervise process, it will restart itself.
# svc -h /service/dnscache/
Testing: Making sure everything works
We can now use nslookup to test everything. djbdns also comes with additional tools we can use to test that everything is working. These are dnsmx, dnsip, dnsname, dnsipq and dnstext. They return the type of information their names suggest.
# nslookup Default Server: ns1.x Address: 10.17.3.8 > server 10.20.30.12 Default Server: ns.internal Address: 10.20.30.12 > unagi.internal Server: ns.internal Address: 10.20.30.12 Non-authoritative answer: Name: unagi.internal Address: 10.20.30.5 > set type=mx > internal Server: ns.internal Address: 10.20.30.12 Non-authoritative answer: internal preference = 0, mail exchanger = a.mx.internal > ^D # dnsip unagi.internal 10.20.30.5 # dnsmx internal 0 a.mx.internal # dnsname 10.200.204.12 ns.internal #
Everything looks good.
If you want to set up a nameserver for use in resolving your hosts for the outside world, you'd simply set up the tinydns server on a publically accessible IP address, instead of the loopback address we used. A dnscache should not be externally accessible. Additionally, you should only put addresses that must be externally resolvable into the outside tinydns data file.
Zone transfers can't be conducted directly via tinydns -- it does not listen for TCP queries. Another portion of dnscache, axfrdns, listens for these queries. We'll touch on this in the next part of our article.
For more in depth tinydns information, consult http://cr.yp.to/djbdns.html
Hopefully, you've come to the conclusion that djbdns isn't all that difficult to install or use. In some ways, its more straightforward than BIND. As it was designed from the get go with security in mind, you can be sure it won't suffer from some of the more common problems that afflict other DNS software. If you're looking for a secure alternative to what is out there for DNS software, you really need look no further.
As one of the reviewers of this article pointed out to me, what we've set up in this article isn't quite a BIND replacement; we still need to cover setting up djbdns to handle zone transfers to secondary name servers, and discuss using djbdns as a secondary name server. There are a number of different opinions on how this is best done, and we'll discuss some different methods that work better than others in certain situations.
To read Installing djbdns (DNScache) for Name Service Part 2, click here.
This article originally appeared on SecurityFocus.com -- reproduction in whole or in part is not allowed without expressed written consent.