The DIY DNS Recursive Resolver: Your Queries Belong to You
A while ago there were numerous reports that Google Public DNS was having performance problems. I wasn't surprised because there is no web service with 100% uptime and even the major online players with countless servers and comprehensive failover strategies in place have to contend with the occasional Very Bad Thing happening at Exactly The Worst Possible Time. Perhaps this outage affected you. Shame on you, you ought to know better than to trust your DNS resolution to a third party, let alone one who's profit model is based on advanced analysis of your Internet usage patterns. If it's free, you have to ask yourself what the real price is that you're paying for it.
During the reported Google Public DNS outage, it occurred to me that most people don't really understand how the Domain Name System works, or how they can take advantage of it to make it work for them. Many folks don't want to be bothered with learning this stuff and would rather that Google do all the thinking for them.
This is not for them.
For the rest of us, there are easy ways to set up DNS resolution on our own home networks and on our laptops. What follows is a modest tutorial that aims to give a basic overview of how to set up a DNS recursive resolver that you can use to get away from having to rely on Google, or OpenDNS, or your ISP for name lookup services.
Why bother running a DNS server?
DNS is important. It's the mechanism used by computers to turn a domain name, like www.cnn.com, into an IP address, like 157.166.240.13. One of these things is easier to remember than the other. More importantly, the people who do the conversion from domain name to IP address control where your Internet traffic goes. Some ISPs and private networks, for whatever reason, can redirect your traffic wheresoever they choose by hijacking your DNS lookups: you see this often in airports and other public places where there are for-pay wireless Internet services. If you open up your browser and ask it to load "www.cnn.com", it will instead take you to the wifi provider's sign-up page to try to sell you their services. Your browser is not getting the right answers to the question you've asked because it's trusting a strange server that wants you to pay first before it hands out the right answers. In other cases, the network is openly hostile to the user and will redirect or block queries to domains that are deemed questionable or dangerous. For now, the IP for falundafa.org is 4.53.82.215.
So in order to use the Internet freely you need to trust a DNS server, just not necessarily someone else's DNS server. Setting up your own resolver is not as complicated as it may seem.
Why yet another howto on DNS?
This one is going to be a little bit different. There are plenty of tutorials out there on how to build a DNS server, but my preferred setup is currently not documented in any single place. I use a combination of DNS software written in 2001 and patches to improve its security and performance, none of which are kept in a central location for easy reference. What's more, the old era of starting services on most Unix and Linux operating systems has ended and there are now a plethora of new service management technologies that control when and how your system's services start. Appending a line to the end of /etc/inittab doesn't cut it any more. These are the steps learned through years of working with these packages, and picking up the tips and tricks that are never as well-documented as you'd like them when you're sitting in front of an unfamiliar distro wondering what they changed between versions v-dot-now and v-dot-last-time. OpenBSD changed their base service rc.d startup model in version 5.0, so I don't even know what's real and what's fantasy anymore.
Will DNSSEC help me?
DNSSEC can help you if and only if your concern is validating that any of your signed DNS responses are, in fact, signed DNS responses and if you're OK with trusting 1024-bit RSA keys. This can help prevent poisoning your cache, but it doesn't help you if you are interested in privacy or maintaining availability of DNS lookups. This tutorial doesn't cover DNSSEC, but you can still independently use DNSSEC after completing this tutorial. DNSSEC is complicated and error-prone, even for DNSSEC masters.
What do I need to get started?
You're going to want a Unix or Linux system. I assume that this is going to be a home server sort of device you keep on your home network, but you can easily adapt this to run on a laptop. In fact, the author of djbdns recommends a separate cache on every host. I'm sure this can be ported to OS X, and with the right precautions it can even be run on Windows through Cygwin with libsodium. This is all outside the scope of the tutorial though. Get a Unix or Linux system and ensure it has a C compiler and a working network configuration: (sudo apt-get update && sudo apt-get install build-essential gcc make is probably a good start). I assume most source code here will be kept in /usr/local/src. You may need to alter these paths if you keep your source in a different location.
- Get yourself a copy of NaCl, the Networking and Cryptography library.
nacl-20110221.tar.bz2 has the following checksums:
MD5 = 7efb5715561c3d10dafd3fa97b4f2d20
SHA1 = 6007a6aee249f5a534ec53fddfc364601fba9629
SHA256 = 4f277f89735c8b0b8a6bbd043b3efb3fa1cc68a9a5da6a076507d067fc3b3bf8
SHA512 = 4c031ceffe6a28dc74b46ac003d485531f78de467c802df73c8b22ca53644dabb7d2e3080b7bdd6583f0d07ad76b6d95bc0ffdce319ca2f80ee041e6fe618656
Fetch and compile it. This can take some time to compile, so I do this in a GNU screen or tmux session and keep working.
wget -N http://hyperelliptic.org/nacl/nacl-20110221.tar.bz2 openssl dgst -sha256 ./nacl-20110221.tar.bz2 # SHA256(./nacl-20110221.tar.bz2)= 4f277f89735c8b0b8a6bbd043b3efb3fa1cc68a9a5da6a076507d067fc3b3bf8 mkdir -m02755 ./src.new && mv ./src.new ./src bunzip2 < nacl-20110221.tar.bz2 | (cd ./src; tar -xf -) [ -d /usr/local/src ] || sudo mv ./src /usr/local/ cd /usr/local/src/nacl-20110221 time ./do
- While NaCl compiles, fetch more of the necessary components.
daemontools-0.76.tar.gz has the following checksums:
MD5 = 1871af2453d6e464034968a0fbcb2bfc
SHA1 = 70a1be67e7dbe0192a887905846acc99ad5ce5b7
SHA256 = a55535012b2be7a52dcd9eccabb9a198b13be50d0384143bd3b32b8710df4c1f
SHA512 = e4a7938352b745a03ccc41acdddba1e6782f0ca245e5cae060de62ab6c5a23c841a994c30140298d274405a7f26d53ba7e84e5810a3d185b2c01e4c0feffe6c7
Fetch it and extract it:
wget -N http://cr.yp.to/daemontools/daemontools-0.76.tar.gz openssl dgst -sha256 ./daemontools-0.76.tar.gz # SHA256(./daemontools-0.76.tar.gz)= a55535012b2be7a52dcd9eccabb9a198b13be50d0384143bd3b32b8710df4c1f mkdir -m01755 ./package gzip -d < daemontools-0.76.tar.gz | (cd ./package; tar -xpf -) sudo mv ./package /
On Linux systems, you have to work around their errno bug. This can be skipped on BSD:
cd /package/admin/daemontools-0.76 echo gcc -O2 -Wimplicit -Wunused -Wcomment -Wchar-subscripts -Wuninitialized -Wshadow -Wcast-qual -Wcast-align -Wwrite-strings -include /usr/include/errno.h > ./src/conf-cc
- Compile and install daemontools. I don't use the default ./package/install script directly anymore because I can't guarantee its modifications to /etc/inittab or /etc/rc.local will work as desired.
cd /package/admin/daemontools-0.76 ./package/compile sudo ./package/upgrade [ -d /service ] || sudo mkdir /service cd
daemontools must be configured to run when the system starts. This is where the problem of which default service-starting mechanism your operating system enters the equation. These are some basic config files for popular init replacements. Your actual mileage may vary.
For Linux systems running Upstart:
echo description "daemontools" > ./daemontools.conf echo start on filesystem >> ./daemontools.conf echo exec /command/svscanboot >> ./daemontools.conf chmod 0644 ./daemontools.conf sudo cp -p daemontools.conf /etc/init/ sudo service daemontools start
For Linux systems running systemd:
echo [Unit] > ./daemontools.service echo Description=daemontools >> ./daemontools.service echo After=local-fs.target >> ./daemontools.service echo [Service] >> ./daemontools.service echo ExecStart=/command/svscanboot >> ./daemontools.service echo [Install] >> ./daemontools.service echo WantedBy=multi-user.target >> ./daemontools.service chmod 0644 ./daemontools.service sudo cp -p ./daemontools.service /etc/systemd/system/ sudo systemctl start daemontools sudo systemctl enable daemontools
For OpenBSD systems v5.0 and up:
echo #!/bin/sh > ./daemontools echo daemon="csh -cf '/command/svscanboot &'" >> ./daemontools echo . /etc/rc.d/rc.subr >> ./daemontools echo rc_cmd \$1 >> ./daemontools sudo cp ./daemontools /etc/rc.d/daemontools sudo chmod 0555 /etc/rc.d/daemontools
Manually add " daemontools" to the "start_daemon" line of services in /etc/rc.
- Download djbdns-1.05.
djbdns-1.05.tar.gz has the following checksums:
MD5 = 3147c5cd56832aa3b41955c7a51cbeb2
SHA1 = 2efdb3a039d0c548f40936aa9cb30829e0ce8c3d
SHA256 = 3ccd826a02f3cde39be088e1fc6aed9fd57756b8f970de5dc99fcd2d92536b48
SHA512 = 20f066402801d7bec183cb710a5bc51e41f1410024741e5803e26f68f2c13567e48eba793f233dfab903459c3335bc169e24b99d66a4c64e617e1f0779732fa9
wget -N http://cr.yp.to/djbdns/djbdns-1.05.tar.gz openssl dgst -sha256 ./djbdns-1.05.tar.gz # SHA256(./djbdns-1.05.tar.gz)= 3ccd826a02f3cde39be088e1fc6aed9fd57756b8f970de5dc99fcd2d92536b48 gzip -d < djbdns-1.05.tar.gz | (cd /usr/local/src; tar -xf -)
- Update the compiling and linking scripts. On Linux, this will contain the -include /usr/include/errno.h errno workaround. On BSD, the errno fix can be omitted but you still have to add the NaCl include and linker files. Be sure to change the value of ARCH to match your machine's architecture. Usually this is the output of uname -m but not always.
cd /usr/local/src/djbdns-1.05 ARCH=amd64 echo gcc -O2 -Wimplicit -Wunused -Wcomment -Wchar-subscripts -Wuninitialized -Wshadow -Wcast-qual -Wcast-align -Wwrite-strings -include /usr/include/errno.h -I/usr/local/src/nacl-20110221/build/`hostname -s`/include/${ARCH} > /usr/local/src/djbdns-1.05/conf-cc echo gcc -s -L/usr/local/src/nacl-20110221/build/`hostname -s`/lib/${ARCH} > /usr/local/src/djbdns-1.05/conf-ld
On systems that do not prefer gcc, replace these lines with the preferred C compiler for that system (possibly clang). I'm looking at you, FreeBSD.
- Patch djbdns-1.05 with Matthew Dempsky's DNSCurve patch.
djbdns-dnscurve-20090602.patch has the following checksums:
MD5 = 52d7d77015ee7ee6f90c2dc96a303f76
SHA1 = 641a44435230991af26cfa93ed0b38f8ff5df409
SHA256 = 7efc54bd1981d0eb920de02b97f9b152c57e6add8023c9b82566358dc9525bba
SHA512 = 986155a0ac7b866e87ce51005ffc15057c2a2d63778a4071b176f0d618e06abca56b28d0391337ca7db181a972616cf84a2c65717b215056b84c371e20d9ec64
wget -N http://shinobi.dempsky.org/~matthew/patches/djbdns-dnscurve-20090602.patch openssl dgst -sha256 ./djbdns-dnscurve-20090602.patch # SHA256(./djbdns-dnscurve-20090602.patch)= 7efc54bd1981d0eb920de02b97f9b152c57e6add8023c9b82566358dc9525bba mv djbdns-dnscurve-20090602.patch /usr/local/src/djbdns-1.05/ cd /usr/local/src/djbdns-1.05 patch -p1 < djbdns-dnscurve-20090602.patch
- Fix the AXFR bug in response_addname().
--- a/response.c +++ b/response.c @@ -34,7 +34,7 @@ int response_addname(const char *d) uint16_pack_big(buf,49152 + name_ptr[i]); return response_addbytes(buf,2); } - if (dlen <= 128) + if ((dlen <= 128) && (response_len < 16384)) /* <URL:http://www.securityfocus.com/archive/1/501294/30/0/threaded> and <URL:http://marc.info/?l=djbdns&m=123613000920446&w=2> */ if (name_num < NAMES) { byte_copy(name[name_num],dlen,d); name_ptr[name_num] = response_len;
- Wait for NaCl to finish compiling before you proceed.
- Compile djbdns-1.05 and install it.
make sudo make setup check
- Create a new group and two users.
sudo groupadd _dns sudo useradd -d /dev/null -s /usr/bin/false -g _dns _dnscache sudo useradd -d /dev/null -s /usr/bin/false -g _dns _dnslog
Official documentation recommends "Gdnscache" and "Gdnslog". Use whatever works for you.
- The installation of dnscache is automated by the utility dnscache-conf. It requires a specific set of arguments.
sudo dnscache-conf _dnscache _dnslog /etc/dnscache 127.0.0.1
This creates a /etc/dnscache directory with the necessary startup files. You may need to edit some of the defaults:
echo 5000000 > ./CACHESIZE echo 5500000 > ./DATALIMIT chmod 0600 ./CACHESIZE ./DATALIMIT sudo mv ./CACHESIZE ./DATALIMIT /etc/dnscache/env/
- Make a symlink from the dnscache directory to /service.
sudo ln -s /etc/dnscache /service/dnscache
- Update /etc/resolv.conf and any resolv.conf-generating scripts. This is seldom as simple as editing /etc/resolv.conf anymore. In Ubuntu, this usually works.
cd /etc/dhcp/dhclient-enter-hooks.d sudo cp -p resolvconf resolvconf.orig sudo cp -p resolvconf resolvconf.new sudo vi resolvconf.new
Make the following change:
--- resolvconf.orig +++ resolvconf.new @@ -23,20 +23,7 @@ # It gets run later (or, in the TIMEOUT case, MAY get run later) make_resolv_conf() { local R - local nameserver - R="" - if [ "$new_domain_name_servers" ] && [ "$new_domain_name" ] ; then - R="${R}domain $new_domain_name -" - fi - if [ "$new_domain_name_servers" ] && [ "$new_domain_search" ] ; then - R="${R}search $new_domain_search -" - fi - for nameserver in $new_domain_name_servers ; do - R="${R}nameserver $nameserver -" - done + R="nameserver 127.0.0.1" [ ! "$interface" ] || echo -n "$R" | /sbin/resolvconf -a "${interface}.dhclient" } ;;
Move the new resolvconf file in place
sudo mv resolvconf.new resolvconf
- EDIT: I've also had success appending the line "dns-nameservers 127.0.0.1" into /etc/network/interfaces, though your actual mileage may vary. This requires a restart, which happens to be the next step.
- EDIT: I've locked up cloud-based VMs by editing /etc/network/interfaces. So to work around that, I manually edit /etc/resolv.conf and then use chflags or chattr to make it read-only or immutable because resolvconf is stupid and doesn't belong on servers.
- Reboot the system. Test that daemontools runs at system startup and that dnscache is up and can run for more than 1 or 2 seconds without restarting.
ps -ef | grep svscanboot sudo svstat /service/dnscache
Other systems may require additional pokery jiggery. Your actual mileage may vary.
By following these steps, you should have constructed a DNS resolver built with security in mind. You've patched it with a state-of-the-art cryptography library and it now supports sites that utilize DNSCurve-based ECC encryption on their DNS endpoints. Queries to and responses from those DNSCurve-enabled sites are encrypted — really encrypted, not just plaintext with a signature — and you don't have a dependency on an external agency to provide you with a fundamental Internet service. We still have a very long way to go before we have a safe, secure, and reliable DNS infrastructure that we know we can trust. This is only the beginning.
No comments:
Post a Comment