DJBDNS
Written by Kevin Korb
as a presentation for GOLUG
Presented on 2005-01-06
- Why not BIND?
- Insecure. A quick search for BIND on securityfocus.com will reveal many security advisories for BIND. BIND is one of the 3 UNIX programs that are most well known for being popular despite the fact that they are very insecure (the other 2 are Sendmail and wu-ftpd). Perhaps even less secure than their Microsoft equivalents! This is old data but between BIND-9.0.0 and BIND-9.2.1 there were 672 bugs. Not all of them were security but if even 10% were that is 67 security holes.
- Terrible data file format. The format of the BIND zone files looks nice at first until you try to actually write one. Named cares where you use tabs and where you use spaces. Some records require an FQDN, some require an FQDN with a '.', and some require just a short name. It is an extremely difficult format to manipulate from scripts.
- Combines 2 functions into 1 program. As far as I know BIND is the only UNIX DNS sever that answers authoritative and recursive queries with the same daemon. Most of the other DNS servers out there are authoritative only servers that rely on DJB's dnscache to answer recursive queries. Combining both of these functions into one program makes BIND vulnerable to "DNS cache poisoning" attacks. This is a design flaw within BIND and can never be fixed as long as named provides both functions. DNSSEC does prevent some forms of this attack but not all of them. Of course that is only the "new" DNSSEC because the old one did absolutely nothing.
- Why djbdns?
- Guaranteed security. As far as I know djbdns has NEVER had a published security hole. DJB even puts money behind his security. He offers a $500 prize to anyone who can publish a security hole in djbdns. This prize has been offered for years and it has never been claimed.
- Stability. While I have never seen djbdns crash that is not what I am talking about. I am talking about the constant need to upgrade and patch things. The latest version of djbdns was released on 2001-02-11 (daemontools is almost as old and ucspi-tcp is even older). DNS hasn't changed and since no bugs have been found there is just no reason to make a new version. Running DJB tools can get very boring because once you set them up you almost never need to touch them again.
- Speed. Tinydns uses a hashed database to store its information. This is much faster than using text files.
- Reasonably easy to use. The daemontools and djbdns packages take a bit of getting used to but eventually you will find that they are in fact easier to use than init/inetd and BIND. I have not tried either of these programs but there are web interfaces available from http://www.nattis.com/projects/anydns/ and http://www.control-alt-del.org/code/suaveDNS/
- Very easy to write add-on scripts for. The file formats that DJB uses are designed to be manipulated by scripts instead of text editors. This means it is easy to write your own tools to automate your DNS work and there are plenty of tools out there already. His command line tools are also designed to be used in scripts. By not printing out any extra information other than what you ask for there is no need to filter the output in your scripts. If you don't believe me then check out my MySQL scripts at http://www.sanitarium.net/unix_stuff/tinydns+mysql_scripts/ These scripts allow me to keep my configuration in MySQL and rebuild the tinydns data file from scratch each time I update the database.
- Why not some other DNS package?
- I cannot speak to most of the other DNS packages out there because I have not actually run anything except BIND and djbdns.
- I have heard that PowerDNS is a good program but I have also heard that MyDNS is slow.
- As far as I know djbdns is the only one that offers a cash prize for security holes therefore it is reasonable to assume it is the most secure or at least equally secure to the most secure.
- If you hate DJB or just don't like djbdns then feel free to try some of the alternatives. Just DON'T use BIND. I am not aware of any DNS server program that is less secure than BIND. Even Microsoft's DNS server is more secure!
- What kind of gotcha's am I going to run into when I convert from BIND?
- Only one interface. Unlike BIND, djbdns only listens on one interface. I used to have 2 interfaces on my server to satisfy the requirements for registering a domain name. When I first converted from BIND to djbdns I realized that tinydns was only listening on one interface and I started panicking. The solution is to simply run 2 instances of tinydns on different interfaces using the same data directory.
- Separation of tinydns and dnscache. djbdns puts the functions of serving authoritative queries in tinydns and recursive queries in dnscache. BIND puts both functions into named. This is a design flaw in BIND not a feature! Unfortunately if you are already using BIND you are probably exploiting this flaw and you will have to stop. Tinydns and dnscache must be run on different interfaces with different IP addresses which means you will have to either change your domain registration or your DNS server settings on all of your clients. My suggestion if you are converting from BIND is to setup tinydns on a new interface. That way you can set it up, configure it, and test it while BIND is still running your domain. Then once you are ready you switch your domain registration to the new IP and wait for the change to propagate. After you are sure everything is OK you can replace the BIND on the old interface with dnscache to keep your clients happy.
- What about DJB and his "evil" licensing?
- First, it isn't really all that evil. Many people say that his license doesn't allow binary packages but that just isn't true. The truth is that he doesn't allow you to distribute modified versions of his software. You can distribute the software and you can distribute modifications just not modified versions. Since most distributions modify software or at least the layout of software when they make their binary packages they do not like DJB's license. OTOH, this policy ensures that everyone running djbdns is either running a clean version or they modified it on purpose. Would you be willing to offer a security guarantee if people running distributions like RedHat and Debian were running modified versions of your software? Isn't it better to not allow such things than to risk the bad publicity when some vendor breaks your security to add some silly feature that doesn't belong there anyway?
- If you are running a source based distribution like Gentoo or *BSD then the licensing terms are irrelavant. Gentoo's portage system will unpack the source, modify it to install in the standard "Gentoo way", compile it, and install it.
- Yes, I know DJB can be an arrogant prick but he makes good stuff. All of his programs are either very popular for their security and stability or they are something obscure that he wrote as research project that nobody cares about. He doesn't make bad software but sometimes he does make useless software.
- Why do I need daemontools and ucspi-tcp?
- Technically you do not have to have these packages. It is possible to run djbdns under init and inetd however such configurations will be unsupported if you ask for help in any of the forums where DJB knowledgeable people hang out. These packages are really nice to have around once you get used to them. I have heard many people say that they are not going to try djbdns because it requires an "OS on top of the OS" but that is simply and exaggeration. On the other hand, I have seen people completely replace both init and inetd with these programs. They boot with init=/usr/bin/svscan and run things like agetty under /service. I think that is a bit excessive because init does do a good job of running things like gettys but inetd is pretty much useless once you have these packages installed.
- What are all these pieces?
- daemontools: This is a system for managing services. It is sort of like init but it is easier to use. It has several pieces:
- svscanboot: This is the script that starts it all initially. It is really just a wrapper for svscan that sets the proper $PATH and service directory. It should go in either /etc/inittab or some rc script started by init. If you are on *BSD it should go in /etc/rc.local.
- svscan: This is the primary program that launches everything else. It is responsible for spawning and respawning all supervise processes.
- supervise: This is the program that actually runs services. Each service gets a supervise pid and the supervise monitors and controls the service pid.
- svc: This is the user level control program. It is what you use to tell supervise what to do with a service. Usage is:
svc [params] <service dir>
params:
-u: Up. If the service is not running, start it. If the service stops, restart it.
-d: Down. If the service is running, send it a TERM signal and then a CONT signal. After it stops, do not restart it.
-o: Once. If the service is not running, start it. Do not restart it if it stops.
-p: Pause. Send the service a STOP signal.
-c: Continue. Send the service a CONT signal.
-h: Hangup. Send the service a HUP signal.
-a: Alarm. Send the service an ALRM signal.
-i: Interrupt. Send the service an INT signal.
-t: Terminate. Send the service a TERM signal.
-k: Kill. Send the service a KILL signal.
-x: Exit. supervise will exit as soon as the service is down.
- multilog: This is DJB's logging program. It will be setup in /service/*/log
- tai64nlocal: Translates DJB's timestamps (used in log files) into something that can be read. Run it through a pipe.
- svstat: Shows the status of a service
- /service: All services are symlinked into here. Whenever a new link to a service directory appears svscan will spawn a new supervise for it within 5 seconds.
- /service/*/down: If you touch this file the service will not be started when supervise starts. Use this for things that you normally want to be down but want the convenience of 'svc -u' to enable. I do this with telnetd and only start it when I am upgrading sshd just in case I break my remote access through sshd.
- /var/service: This is where the actual service directories are before you symlink them into /service. While it is possible to put the service directories directly into /service putting them here makes it much easier to change them later. Especially if you have to remove a service. Note that you don't have to use /var/service. You can pick any location you want. Many people use /var/svc.d but /var/service is easier on the fingers.
- /service/*/log/main/current: The log file for a service if it is set to be logged. Of course many people set a different location such as /var/log/[service] but that would be defined in /service/[service]/log/run.
- There are more but they will only be important if you start making your own services.
- ucspi-tcp: This is a set of tools that form a common interface for using tcp ports. It only has 2 programs that we need for djbdns:
- tcpserver: This is a program that can be used under supervise to replace inetd services that listen to tcp ports. You only need this if you need to run axfrdns.
- tcpclient: This is the client side of a tcp connection. You only need it if you need to convert an existing BIND zone into a tinydns datafile.
- djbdns: This is what we are here to talk about ;)
- tinydns: This is the authoritative DNS server program. It is what you want to use if you are actually serving a domain name whether it is a real registered one or just something you are setting up for internal use only. This is what you would point your domain registration to.
- dnscache: This is the recursive server. It is what you run to provide answers to recursive DNS queries for people on your network who want to do things like surfing. Of course it also caches the queries it looks up. This is what you would point your /etc/resolv.conf to.
- axfrdns: This is what you run if you have to serve DNS zone transfers or tcp based DNS requests. Note that both of these things are very rarely needed so it is rare that axfrdns is needed.
- [tinydns|dnscache|axfrdns]-conf: scripts that setup the respective services.
- dns[q|qr|name|ip|mx|txt]: various querying programs. DJB's replacements for the host and dig commands. These are much easier to use in scripts as they don't print out any extra output that you have to filter.
- add-*: These are wrapper scripts for tinydns-edit.
- tinydns-edit: This is a program that adds commonly used records to the tinydns data file. If you need to add records that are beyond its capabilities then you will have to do it by hand. Unfortunately that is more often the case than not.
- tinydns-data: This program compiles the data file created by tinydns-edit (or by hand) into the cdb hash that is actually used by tinydns. It is usually run by calling 'make' in /service/tinydns/root. Tinydns does NOT need to be restarted or HUP'd after the hash is recompiled.
- OK, How do I install all of this stuff?
- The main site for all of the DJB programs is http://cr.yp.to/
- Use distribution packages. If your distribution has packages for these programs just use them. That way they will be installed in locations that you are used to. The rest of this section is for people who want or have to install from source.
- If you use RedHat (or some other distribution that uses .rpm files) there are RPMs available here: http://www.untroubled.org/rpms/
- If you are running OpenBSD there are ports available here: http://experimental.bug.it/ The version of daemontools on that page is ancient though so I suggest building it manually.
- Install daemontools:
- # mkdir /package ; cd /package ; chmod 1755 .
- # tar -pxvzf /path/to/daemontools-0.76.tar.gz
- # cd admin/daemontools-0.76
- # package/install
- you now should have a /service directory and a bunch of new symlinks in /usr/local/bin
- You should now have something like this in your /etc/inittab and it should already be running: SV:123456:respawn:/command/svscanboot
- I have noticed in the past that sometimes init doesn't like this line unless it is near the top of /etc/inittab. I don't know why this problem happens but it is probably an init bug. IIRC I have mainly seen the problem on Solaris. If the line is in there but svscan isn't running move the line closer to the top of the file and run 'init Q'.
- If you are running *BSD it should have added itself to your /etc/rc.local and you will have to start it yourself.
- Install ucspi-tcp:
- # cd /usr/src
- # tar -xvzf /path/to/ucspi-tcp-0.88.tar.gz
- # cd ucspi-tcp-0.88
- # make
- # make setup check
- Install djbdns:
- # cd /usr/src
- # tar -xvzf /path/to/djbdns-1.05.tar.gz
- # cd djbdns-1.05
- # echo gcc -O2 -include /usr/include/errno.h > conf-cc (note that this command is only needed on Linux systems to work around a "bug")
- # make
- # make setup check
- Setup your authoritative DNS server for your domain using tinydns:
- Use the tinydns-conf program to setup your initial service directory. Run it like this:
# tinydns-conf <tinydns userid> <multilog userid> <service directory> <IP to listen to>
my example would be:
# tinydns-conf tinydns dnslog /var/service/tinydns 192.168.100.201
That will create the service directory /var/service/tinydns and set tinydns to run as userid tinydns and listening for queries on 192.168.100.201. It will also setup multilog to log tinydns's output using the dnslog userid. Note that both userids must exist on the system first.
- cd /var/service/tinydns/root
- If you need to convert zones from an existing BIND server use this command:
# tcpclient <IP still running BIND> 53 axfr-get <domain> <new text file> <new text file.tmp>
- Use the add-* scripts to add your DNS data to the data file. If you need to be more specific or more creative than the add-* scripts allow you will have to make the records by hand with a text editor. The add-* scripts are just wrappers for the tinydns-edit program so if they don't do what you want it won't either.
- If you have to make your own records by hand here are the formats:
- Combined Name Server record (This defines the NS, SOA, and A records for your name server):
.[domain]:[ip]:[server name]:[ttl]:[timestamp]:[location]
A practical example would be:
.sanitarium.net:192.168.100.201:ns1.sanitarium.net
Note that I do not actually use this type record because I prefer to do my SOA records seperately.
- Name Server record (This defines the NS and A records but not the SOA):
&[domain]:[ip]:[server name]:[ttl]:[timestamp]:[location]
An actual example would be:
&sanitarium.net::ns1.sanitarium.net.:
- Combined Host record (This defines both an A record and the corresponding PTR record).:
=[fqdn]:[ip]:[ttl]:[timestamp]:[location]
A practical example would be:
=dementia.sanitarium.net:192.168.100.17
Note that I do not use this record type because I like to keep my A records seperate so I can tie different IPs to the same name in different locations.
- A record:
+[fqdn]:[ip]:[ttl]:[timestamp]:[location]
Here is a real example that shows what I mean about locations:
+asylum.sanitarium.net:24.173.162.174:3600::IN
+asylum.sanitarium.net:192.168.100.202:::SN
This gives one answer to people on my network and a differnt answer to everyone else.
This can also be a wildcard like this:
+*.sanitarium.net:24.173.162.174
that would give 24.173.162.174 to anyone asking for anything.sanitarium.net.
- MX record:
@[domain]:[ip]:[fqdn]:[distance]:[ttl]:[timestamp]:[location]
Here is a real example:
@sanitarium.net::asylum.sanitarium.net.:10:300::
Note that you should supply either the IP or the FQDN of the mx but not both. The distance field only matters if you have more than one MX record for a domain.
- TXT record:
'[fqdn]:[string]:[ttl]:[timestamp]:[location]
- PTR record (this is for reverse DNS but is only needed if you refuse to use '=' records like me:
^[in-addr.arpa name]:[fqdn]:[ttl]:[timestamp]:[location]
Here is a real example:
^202.100.168.192.in-addr.arpa:asylum.sanitarium.net.:::SN
- CNAME record:
C[fqdn]:[real fqdn]:[ttl]:[timestamp]:[location]
Many people use these records kind of like symlinks but unfortunately that isn't very effecient. When you use a CNAME record you cause 2 queries instead of 1. The first to look up the CNAME and the second to look up the A that it points to. It is better to just use more A records.
However, CNAME records can be usefull if you want to point to something dynamic and don't want to keep editing the A record yourself or if you are pointing to an A record that is part of a domain that is out of your control.
- SOA record:
Z[domain]:[mname]:[email address for admin]:[serial number]:[ref]:[ret]:[exp]:[min]:[ttl]:[timestamp]:[location]
Most of this crap is usually left blank (If you even bother to make a Z record). This is the header you are used to seeing at the top of a BIND zone. However tinydns makes up most of it on the fly.
Here is my example:
Zsanitarium.net::nic.sanitarium.net.::::::::
I only define this instead of using a combined record so I can set the email address that I want in the SOA record.
- Location record:
%[location name]:[IP mask]
This is how you define the locations to be used in the other record types.
Here are the ones I use:
%SN:192.168.100
%SN:192.168.1
%SN:127.0.0.1
%IN:
This sets all of my internal IP ranges to SN and everything else to IN.
- When you are done making your data file use tinydns-data to compile it into the cdb hash that tinydns will actually use. You probably have a Makefile that will run it for you but if not just run tinydns-data with no params and it will do its job.
- # ln -s /var/service/tinydns /service/
- Use 'dnsq <record type> <query> <server IP>' to query your new DNS server to make sure it is serving the right data.
- Once your new server is registered to be authoritative for your domain you can use dnsname, dnsip, dnsmx, and dnstxt to run the simple queries.
- Setup your DNS cache service to answer recursive DNS queries
- Use dnscache-conf to setup the initial service directory. The syntax is the same as tinydns-conf above.
- In the new service directory run this to get good randomness:
dd if=/dev/srandom of=seed bs=128 count=1
- In /var/service/dnscache/root/ip touch the IPs that you want to allow to connect to the dnscache service. If you leave out an octet like 'touch 192.168.100' you will get the entire block.
- # ln -s /var/service/dnscache /service/
- Use 'dnsqr <record type> <query>' to query your new DNS cache to make sure it works. You should be able to lookup external domains. Note that you can't use dnsq like above because it only sends non-recursive queries.
- Update your root name servers list with this command:
dnsip `dnsqr ns . | awk '/answer:/ { print $5; }' |sort` > /service/dnscache/root/servers/@
- If you are serving a domain with tinydns you can set dnscache to go directly to your tinydns for that domain only. This is not required but it can speed things up because it prevents dnscache from hitting the root servers to see who to ask for your domain. Here is the syntax:
echo [tinydns IP] > /etc/dnscache/root/servers/[domain name]
An example of this would be:
echo 192.168.100.201> /etc/dnscache/root/servers/sanitarium.net
- svc -t /service/dnscache to make sure it gets all the new settings.
- If for some reason you need to serve DNS zone transfers or tcp queries then you need to setup axfrdns. This is almost never required but here it is anyways...
- Use axfrdns-conf to create your initial service directory. There is one extra parameter on this one:
axfrdns-conf <tinydns userid> <multilog userid> <axfrdns service dir> <tinydns service dir> <same IP as was given to tinydns-conf>
example:
axfrdns-conf tinydns dnslog /var/service/axfrdns /var/service/tinydns 192.168.100.201
The reason it needs the tinydns information is that it will use the same data files which is a good thing considering it is serving the same data but in a different format.
- Setup /var/service/axfrdns/tcp with the appropriate access list. Here is an example that only allows localhost to xfer sanitarium.net:
192.168.100.201:allow,AXFR="sanitarium.net/unixevilgenius.org"
:allow,AXFR=""
:deny
- In /var/service/axfrdns run 'make' to update the cdb hash for the tcp rules you just setup.
- # ln -s /var/service/axfrdns /service/
- Test with your favorite xfer client.
- Setup a secondary tinydns service on the same box. This is the easy way to get 2 DNS server IPs for your domain registration. Of course it offers NO redundancy.
- Run tinydns-conf again with a different service directory and a different IP address. The service directory can be as simple as /var/service/tinydns2 or you can be more specific. I like to use the last octet of the IP address so I might have /var/service/tinydns-201 and /var/service/tinydns-202 but that is up to you as it is purely cosmetic. I will assume you are using tinydns-1 and tinydns-2 to save on typing from here on.
- # rm -rf /var/service/tinydns-2/root
- # ln -s /var/service/tinydns-1/root /var/service/tinydns-2/
- # ln -s /var/service/tinydns2 /service/
- Test it just like you did the first instance.
- If you setup axfrdns above you should probably make a second instance of it the same way except you will not need to link the root directory since it is a link to tinydns anyways.
- Setup a secondary server on another box. This is the "right" way to have a secondary server. It gives you your second IP and redundancy. Of course it requires another box.
- Install everything on the second box
- Setup and start a tinydns and an axfrdns service if you need it.
- On the primary server you can either write a script to update the secondary or you can modify the Makefile to do it like the djbdns docs say. All you have to do is get the data.cdb file onto the other server after tinydns-data creates it. DJB suggests using rsync to do that but unless your file is huge it is probably faster to just use scp. The script can be as simple as:
#!/bin/csh -f
tinydns-data
scp data.cdb ns2:/service/tinydns/root/
- If you only want to setup an internal (not registered to you) domain you can do that too.
- Setup tinydns on your loopback (127.0.0.1) interface and configure it to serve the internal domain.
- I recommend that you pick a bogus tld so that it is impossible for the domain to actually exist somewhere. This is because some DNS clients (especially Windows) will try to register their host names with the registered DNS server for the domain they are in. If you pick a domain name that actually exists you will tick off the sysadmin for that domain by filling his logs with errors that look like attacks.
- Setup dnscache on your internal interface IP and configure it to go to your loopback tinydns for your internal domain.
- Also configure the dnscache to look to your tinydns for reverse lookups to your private IPs with something like:
echo "127.0.0.1" > /service/dnscache/root/servers/100.168.192.in-addr.arpa
- Now you have your internal domain and a caching DNS server to speed up your surfing to the outside ;)
- Neat stuff about your new setup:
- You no longer need to worry about serial numbers. Since tinydns doesn't rely on zone transfers to keep secondaries up to date there is no need for serial numbers. If someone asks for one tinydns will make one up to keep them happy.
- You usually don't have to worry about the TTL. Tinydns will slowly increase the TTL reported to clients depending on the age of a record. In other words,, if you change a record often its TTL will be short but if you never change the record the TTL will grow long over time. Of course you can override the TTL on any record if you want to be specific.
- If you watch your logs you will see lots of script kiddies requesting "versoin.bind". You can sit back and laugh at them as they look for vulnerable people running BIND.
- Since you have the daemontools running now you can setup other things to run under /service instead of init or inetd. I have posted a bunch of custom made run scripts for common programs on my web site here: http://www.sanitarium.net/unix_stuff/supervise_run_scripts/ I recommend that you put some more things under /service just so you get used to how it works. If you don't then the daemontools will just be a little black box that makes djbdns work and you will have no idea how it actually functions.