Recently released into the public domain, djbdns is a fast and secure replacement for BIND.
Let's face it, DNS is not the most sexy component of the Internet's infrastructure. It is an old technology and doesn't get the same attention as newer, more flashy tools and software. Your Web site visitors may comment on how cool your new AJAX widget is, but I guarantee they will never tell the world how pleased they are with your DNS response time.
Nevertheless, DNS is crucial to the Internet. It is one of those services that always should “just work”, and it's only when it doesn't work that people notice (and complain, loudly). Readers may remember the great “Google vanishing act” in May 2005, when the search engine giant briefly disappeared from the Internet. Many quickly assumed the site had been hacked, but the problem turned out to be a DNS configuration issue. The mishap was fixed quickly, but it highlighted how even the mightiest of the mighty can be crippled easily by a simple misconfiguration.
My primary goal for this article is to demonstrate that there is a free, secure and easy-to-configure alternative to BIND: djbdns. This article is intended for people who may have some experience with DNS, but who would like to consider new approaches. I assume only a basic understanding of DNS—specifically, familiarity with the basic record types, such as A, CNAME, MX, NS and SOA, as well as the concept of a TTL (time to live).
For the first 15 years of the Internet as we know it, there was only one real choice when it came to DNS server software: BIND. BIND began life as a project by several graduate students at the University of California, Berkeley (thus the acronym, the Berkeley Internet Name Domain). In the early 1990s, the Internet Systems Consortium (ISC) was created to maintain, distribute and support this critical software formally. The ISC released BIND 8 in May 1997 as a major update to the aging BIND 4. Although there were major configuration differences, both BIND 4 and 8 were based on the original Berkeley code from the early and mid-1980s. While trying to raise funding for a major rewrite, one of BIND's authors described this code as “sleazeware produced in a drunken fury”. A new team worked on the rewrite for several years, and BIND 9 was released formally in September 2000.
After years of dealing with security problems in BIND and frustration with its configuration syntax, Dan J. Bernstein began work on djbdns in 1999. Bernstein (or DJB as he is commonly known) already had made a name for himself as the author of qmail, the mail server software that was quickly gaining popularity among system administrators. At the time, Sendmail was the dominant mail server on the Internet, and, like BIND, it was notoriously difficult to configure and had a history of security problems. Bernstein's “thinking outside the box” design decisions about security and configuration simplicity not only catapulted qmail to success, but it also affected the way developers thought about writing software for the increasingly volatile Internet (Postfix, Courier and others were inspired by qmail's security partitioning design). Now that Bernstein had secured and simplified mail, it was time to do the same for DNS. The first alpha of djbdns was released in December 1999, and the current version, djbdns 1.05, eventually was released on February 11, 2001. That's right, the current version is more than seven years old. Remember, DNS is an old protocol, and it doesn't change very often. BIND software updates almost always are for bugfixes or security patches.
In the past, Bernstein's software was controversial because it lacked an explicit license. OS vendors were reluctant to distribute his packages because of the uncertainty around its licensing. However, in December 2007, Bernstein placed djbdns (as well as daemontools and qmail) into the public domain, allowing people to use or distribute it as they see fit.
BIND has been around since the earliest days of the Internet. It's still the most popular DNS server out there, so why should you consider switching to djbdns? For one, djbdns does not have BIND's history of problems. BIND's security record is on par with Sendmail's (not something to be proud of), and configuring it beyond the basics can be downright painful.
To complicate things further, BIND blurs the distinction between the different functions of DNS. There are two primary types of DNS services: DNS caches (also called recursive DNS servers) and DNS servers (also called authoritative servers or name servers).
A DNS cache is what your desktop computer talks to when it needs to find the address for a Web site you are trying to reach. When a cache receives your request for the location of www.google.com, it first checks to see whether it already knows the answer to your question. If it does, it quickly tells you. If it does not know the answer already, it begins by first asking the root servers for the answer. The root servers respond with something similar to “I don't know the answer but the .com servers might; here are their addresses, go ask them.” The caching server continues doing this until it has the IP for www.google.com, and then it returns the answer to your computer. The IP addresses you see in /etc/resolv.conf are for DNS caches. Caches talk to authoritative servers to get answers.
An authoritative server has a much more straightforward responsibility. Its job is simply to publish information from domains for which it is “authoritative”. An authoritative server will give answers only to questions about domains for which it has been explicitly configured. For example, ns1.google.com (one of Google's authoritative DNS servers) never will answer a request for the address of www.microsoft.com (unless Microsoft and Google merge some day).
Although these are completely different services, BIND uses the same server for both. This may seem handy, but it complicates the configuration and quickly can become a security headache.
On the other hand, djbdns adheres to the UNIX philosophy of “do one thing, and do it well”. The server components of djbdns are separated, with dnscache as the caching component and tinydns as the authoritative server (I detail the advantages of each shortly).
This separation allows each program to run individually chrooted as its own unprivileged user. If an attacker is able to crash your DNS cache, it will not impact your authoritative DNS service. A side effect of this is that dnscache and tinydns need separate IP addresses, so that each may bind to port 53. You can't run both on the same IP address.
The latest version of djbdns compiles on all the major Linux distributions. You also need to install daemontools (see sidebar), another package by Dan Bernstein.
Download djbdns from Bernstein's Web site, and run the following commands. The third line is a workaround for a glibc bug on Linux:
# tar xzf djbdns-1.05.tar.gz # cd djbdns-1.05 # echo gcc -O2 -include /usr/include/errno.h > conf-cc # make # make setup check
See Bernstein's official documentation if you have further questions about installing djbdns.
One of the easiest ways to begin using djbdns is to configure a DNS cache on your local network. There are many reasons why you may want to do this—from faster DNS lookup times to avoiding those pesky mistyped domain search result pages. In either case, installing dnscache can help.
Let's assume you have a home network with several computers on 192.168.10.0/24. Additionally, a Linux machine (named linux1) is running on 192.168.10.10. You want to install dnscache on linux1, so it can provide DNS resolution service for the other machines on the network.
Fortunately, installing dnscache is trivial, thanks to the dnscache-conf utility provided with djbdns. Before running dnscache-conf, you need to create one new group and two accounts on linux1. These will be used exclusively by djbdns and should not be available for login:
# groupadd djbdns # useradd -s /bin/false -d /etc/dnscache -g djbdns dnscache # useradd -s /bin/false -d /dev/null -g djbdns dnslog
The next step is to run dnscache-conf and provide it with four parameters: the account for the dnscache process, the account for the logging process, the dnscache service directory and the IP on which dnscache should listen:
# dnscache-conf dnscache dnslog /etc/dnscache 192.168.10.10
The /etc/dnscache directory now should exist. Before you can begin using your new cache, you need to allow access to it from your local network. dnscache checks to see if a machine is allowed to access it by comparing the IP of the incoming request address against files in /etc/dnscache/root/ip/. You can grant access to your whole network simply by touching a single file:
# touch /etc/dnscache/root/ip/192.168.10
At this point, you are ready to start the cache. If you are running BIND, you need to stop and disable it so that dnscache can take ownership of port 53. Assuming daemontools is installed and running, you now can start dnscache:
# ln -s /etc/dnscache /service/
That's it. You now have a DNS cache running on your local network. Your next step is to update the /etc/resolv.conf file on all your machines to point to 192.168.10.10:
nameserver 192.168.10.10
If your network is very busy, you may find you need to increase the amount of memory that is allocated to your cache. Dan Bernstein provides instructions on his Web site for adjusting the cache size, but you also may want to take a look at Paul Jarc's cache-effect.pl Perl script or Mike Babcock's dnscacheproc.py Python script.
If you have ever run BIND as an authoritative DNS server, it is likely that at some point you neglected to increment the serial on an SOA record, overlooked a missing semicolon somewhere or simply forgot to append a period (.) at the end of a record. These are just a few of the common mistakes people make when dealing with BIND's zone files. If you have been bitten by any of these issues, you probably remember the trouble it created for you. These errors can cause big headaches (just ask Google).
tinydns, the authoritative DNS server in djbdns, takes an entirely different approach and makes it much more difficult to get yourself in trouble. One major difference is that instead of separate zone files for each domain, tinydns uses a single text file named data to store every record of every domain. This data file is then compiled into a very fast database in cdb format. Of course, if you prefer managing domains in separate files, you still can, just concatenate them together before compiling the database.
Let's get started by configuring our tinydns instance. You should have daemontools already installed and running. Again, let's assume we are running a home network on 192.168.10.0/24, and we now want to access each host by name using DNS. We have another Linux machine (named linux2) running on 192.168.10.20 that will publish DNS information with tinydns.
First, create the tinydns user:
# useradd -s /bin/false -d /etc/tinydns -g djbdns tinydns
Like dnscache, there is a utility for creating and configuring instances of tinydns. It also takes four parameters: the account for the tinydns process, the account for the logging process, the tinydns service directory and the IP on which tinydns should listen:
# tinydns-conf tinydns dnslog /etc/tinydns 192.168.10.20
This creates the /etc/tinydns directory and populates it with everything needed to begin publishing your DNS data. The last step is to create a symbolic link for the tinydns service into /service. Again, be sure to stop and disable any BIND instances first:
# ln -s /etc/tinydns/ /service/
Now you can begin adding records for each host on your network.
Before we begin, let's see how our DNS data would look in the traditional BIND zone file format (versions 8.2 and greater). Listing 1 shows everything needed to configure forward records for example.com and reverse records for 192.168.10.0/24. This includes the configuration for named.conf, as well as the zone data for example.com and 10.168.192.in-addr.arpa. This clocks in at 38 lines of configuration for our two domains.
As I mentioned, tinydns takes a different approach. Instead of defining records separately for forward and reverse zones, tinydns allows you to combine them into single records. Listing 2 contains the exact same configuration from Listing 1, except in tinydns format. Instead of 38 lines of configuration, we now have only ten lines. Let's go over what these lines do.
The first character of each line is used to specify the type of record or records that should be created. A period (.) line tells tinydns that it is authoritative for example.com:
.example.com::linux2.example.com
This creates an SOA (start of authority) record and sets linux2.example.com as an NS record. If an IP address was provided between the two colons, an A record also would have been created for linux2.example.com with that IP address. This one @ line replaces eight from the BIND zone file:
@example.com:192.168.10.15:mail.example.com:0
This line creates two records. An A record is created for mail.example.com with an address of 192.168.10.15, and an MX record is created for example.com pointing to mail.example.com with a distance of 0. Now, let's start defining our hosts:
=linux1.example.com:192.168.10.10 =linux2.example.com:192.168.10.20 =linux3.example.com:192.168.10.30
These lines each create two records. For example, the first line creates an A record for linux1.example.com with an address of 192.168.10.10 and a PTR record (a reverse record) for 10.10.168.192.in-addr.arpa pointing to linux1.example.com. If you manage both the forward and reverse zones for your network, you probably already can see what a huge time-saver this can be.
Finally, we define simple aliases for our hosts. Each host has an alias that we prefer to use instead of the generic linux{1,2,3} names. To create alias A records, we use + lines, which are exactly like = lines, except PTR records are not created:
+flying.example.com:192.168.10.10 # alias for linux1 +spaghetti.example.com:192.168.10.30 # alias for linux2 +monster.example.com:192.168.10.30 # alias for linux3
Although it's discouraged, you also could define an alias with a CNAME using a C line:
Cnoodly-appendage.example.com:linux1.example.com
All these records go in a single file, which in our case is /service/tinydns/root/data. Save the file, and from that directory run make. This compiles the text file into data.cdb, a constant database. If a data.cdb already exists, tinydns will continue serving from it until the new one is ready, at which point it is moved into place, and tinydns instantly begins using it. The Makefile simply calls the tinydns-data command:
data.cdb: data /usr/local/bin/tinydns-data
You can test that your new records are in the database by using the tinydns-get utility. tinydns-get accesses the data.cdb file directly, so you don't need to worry about your test queries being cached anywhere. For example, you can use tinydns-get to see that your MX record is configured properly. First, make sure you are in the /service/tinydns/root directory and that you have run make so that the database is up to date:
# tinydns-get mx example.com 15 example.com: 103 bytes, 1+1+1+2 records, response, authoritative, noerror query: 15 example.com answer: example.com 86400 MX 0 mail.example.com authority: example.com 259200 NS linux2.example.com additional: mail.example.com 86400 A 192.168.10.15 additional: linux2.example.com 86400 A 192.168.10.20
This shows that linux2.example.com is defined as the authoritative name server for example.com, that mail.example.com is the MX record for the domain, and that its IP address is 192.168.10.15.
There are many other convenience features that tinydns offers. For example, with tinydns, you do not need to remember to increment the serial on the SOA record each time you change something in a zone file. tinydns automatically generates serials from the last-modified timestamp on the data file, which ensures that they are incremented whenever the file changes.
If you ever have had to migrate DNS for an active domain, you will appreciate per-record timestamps. You can specify an exact time in the future for a record to change, without worrying about how it is cached around the Internet. tinydns dynamically calculates the TTL as it responds to queries. For example, if you want to migrate samba.example.com from 192.168.10.25 to 192.168.10.35 at 2 AM on October 15, 2008, you can add the following two records:
=samba.example.com:192.168.10.25:0:4000000048f594fa =samba.example.com:192.168.10.35::4000000048f594fa
The last field on these records is a TAI64 timestamp representing 2008-10-15 02:00:00. (See Resources for tips on generating TAI64 timestamps.)
A cache that requests the A record for samba.example.com at 1:50:00 AM on October 15, 2008, will receive a response of 192.168.10.25 with a TTL of 600 seconds (ten minutes). A cache that requests the same record at 1:59:45 AM will receive the same response, except with a TTL of 15 seconds. After 2:00 AM, tinydns will begin responding automatically with the new IP, 192.168.10.35. Because all prior responses were set to expire at exactly 2:00 AM, all caches will check back immediately for the new address.
It's the little things like this that make djbdns such a wonderful piece of software.
BIND servers use zone transfers to replicate DNS data between servers. This process is rather complicated, has a history of problems and is not exactly easy to configure. Instead, Bernstein recommends using existing data transfer tools, such as rsync or scp, that are known to be fast, efficient and secure.
Let's add linux3.example.com as second DNS server for the example.com domain. Install djbdns on linux3 and configure tinydns as above (using the appropriate IP address). Update your data file on linux2 with the new record (anywhere in the file is fine):
.example.com::linux3.example.com
Next, update /service/tinydns/root/Makefile on linux2 with the new make target. Replace everything in the Makefile with the following:
remote: data.cdb rsync -az -e ssh data.cdb \ 192.168.10.30:/service/tinydns/root/data.cdb data.cdb: data /usr/local/bin/tinydns-data
Be sure to use tabs instead of spaces at the beginning of the command lines in your Makefile. Now, when you run make it will compile data.cdb and immediately rsync it to linux3. We are using the IP for linux3 in the rsync command, because DNS should not rely on itself (it would fail if your DNS was broken). Also, you may want to create a special account for this purpose and configure passwordless ssh access using keys. Dan Bernstein provides more thorough instructions on his Web site for configuring DNS replication.
As I hope you have seen, DNS does not have to be a headache. Although BIND is ubiquitous on Linux, djbdns is more secure, more efficient and simply easier to use. And, now that it has been released into the public domain, there are no longer any philosophical reasons for rejecting it. We've only briefly covered what djbdns has to offer, so I hope you will read the on-line documentation, download it and experiment with it yourself. If you ever have found yourself babysitting a BIND instance, you may want to consider giving djbdns a chance.