Monochromatic

z3bra, the stripes apart

Name that domain

10 July, 2014

Hello folks !

I recently reinstalled my home server, and during the process, I decided to throw away the heavy BIND nameserver from my application stack. I decided to go with the light couple tinydns + dnscache.

This wasn't really "easy" to do so, as there are really few resources on the web explaining the step to get this working (If you're aware of one, please send me a link, I'll be glad to share it). So here is a full tutorial !

TL;DR

  1. create tinydns zone file
  2. make tinydns listen on 127.0.0.1
  3. create the ip files for dnscache
  4. create the servers file for dnscache containing 127.0.0.1
  5. Fire it up
  6. Enjoy.

The whole installation

First of all, I had to find out what were my needs. It seems stupid to say so, but I have the bad habit to dive into configuration, barely knowing what I'm trying to do. Here's what I wanted:

The DNS nameserver will be running on a machine with IP 10.0.0.1. My local network will be named domain.lan. I want all my machines to contact 10.0.0.1 to resolve URIs, and the server will provide authoritative answers for domain.lan, and forward everything else to some other servers.

How should we organise things then ?

Here's how I see it:

Let's set this up then!

tinydns

tinydns is fairly simple to use, and even more simple to configure. But it's nothing you've ever seen before. You can configure tinydns from a single file:

─── cat /etc/tinydns/root/data
Zdomain.lan:dns.domain.lan::contact.domain.lan
&domain.lan::dns.domain.lan
+domain.lan:10.0.0.1
+pc1.domain.lan:10.0.0.1
+pc2.domain.lan:10.0.0.2

That's a basic configuration file to use with tinydns. It will resolve domain.lan and pc1.domain.lan to ip 10.0.0.1 and pc2.domain.lan to 10.0.0.2.

For more infos on the syntax, check the alpine wiki page.

Once your config file is created, you can generate the data that tinydns will use. Remember to correctly set the ROOT variable to define the directory where tinydns will store it's data:

# ROOT=/var/cache/tinydns/ tinydns-data

As we said earlier, we want tinydns to listen on the loopback interface. To do so, we have to export the variable IP before running the binary.

# ROOT=/var/cache/tinydns/ IP=127.0.0.1 tinydns

And there you go ! Tinydns is listenning on address 127.0.0.1.
To check if it's correctly running, you can use nslookup.

# cp /etc/resolv.conf.old /etc/resolv.conf.orig
# cat <<EOF > /etc/resolv.conf
nameserver 127.0.0.1
EOF
# nslookup pc2.domain.lan
Server:    (null)
Address 1: ::1 localhost
Address 2: 127.0.0.1 dns.domain.lan

Name:      pc2.domain.lan
Address 1: 10.0.0.g.2
# mv /etc/resolv.conf.orig /etc/resolv.conf

It works ! Don't use tinydns as a resolv.conf entry though. Because it's authoritative and ONLY serves our domain.lan zone, it would not be efficient...

dnscache

No DNS server can answer all the queries, so in most case, if the DNS can't provide an answer, it will just forward the query to another depending on some internal rules.

That's how we're gonna set up dnscache. Intercept queries from the local network, forward every query for domain.lan to tinydns, and everything to a standard list of known root servers.

But first, we need to tell dnscache to answer every query from local domain. Let's say my current configuration is the following:

Network : 10.0.0.0
Netmask : 255.255.0.0
Gateway : 10.0.0.254
DNS     : 10.0.0.1

We need to listen on 10.0.0.0/16 for DNS queries. To set this up, take a look at the /etc/dnscache/ip/ directory:

─── ls /etc/dnscache/ip
127
─── wc 127
0         0         0 /etc/dnscache/ip/127

A single file named 127, which is totally empty... What does that mean ? Upon startup, dnscache will read this directory and check the filenames to know which IP address it should accept queries from.

The filename also act as a netmask, so in this case, 127 really means 127.0.0.0/8.

Back to our configuration. We want dnscache to accept queries from our local network: 10.0.0.0/16. Just create the corresponding file:

# :> /etc/dnscache/ip/10.0

And we're done !

Now, we need to tell dnscache who will answer the queries it receives. This is done in the /etc/dnscache/servers/ directory. We can see that there is already a file here:

─── ls /etc/dnscache/servers/
@
─── cat /etc/dnscache/servers/@
198.41.0.4
192.228.79.201
192.33.4.12
128.8.10.90
   ...

This is a list of the root servers extracted from this list.

In this directory, each file represent a domain, and the content of this file is the list of the servers to contact in order to resolve those names.
"@" is a special name for the "fallback" entry.

In our case, we want tinydns to resolve names from "domain.lan", and forward everything else to the root servers in the "@" file. To query tinydns, we need to forward queries to 127.0.0.1. Here's how to do this:

# cat <<EOF > /etc/dnscache/servers/domain.lan
127.0.0.1
EOF

That's all. Pretty simple isn't it ?

It's now time to start dnscache. It needs (like tinydns) two environment variables: ROOT for the configuration directory path, and IP for the address of the interface to listen on (note that you can use 0.0.0.0 to listen on all interfaces).

# ROOT=/etc/dnscache IP=10.0.0.1 dnscache

You can now check if everything is working fine with nslookup:

# cp /etc/resolv.conf.old /etc/resolv.conf.orig
# cat <<EOF > /etc/resolv.conf
nameserver 10.0.0.1
EOF
# nslookup pc2.domain.lan
Server:    (null)
Address 1: ::1 localhost
Address 2: 127.0.0.1 dns.domain.lan

Name:      pc2.domain.lan
Address 1: 10.0.0.g.2
# mv /etc/resolv.conf.orig /etc/resolv.conf

And there you are ! You can now specify the IP address of your server in the resolv.conf on your local computers.