01.04.2019 linux dns

Building a caching/forwarding DNS with bind9

Whenever you connect to a website, the computer needs to exchange the URL with the actual IP address of the remote server. In this article, I tell you why it is a good idea to cache this request.

Why do it?

As you have already read, computer URLs, or more specific the host name (ex.: tuxdstash.de), need to be translated into IP numbers. That is because networking only works with those. It can be either IPV4 or IPV6 . To do that, any computer has an important information in the network section, called a DNS resolver, often written as “Nameserver”.

Now, if you are a single like me, it probably makes not as much sense to cache that request. But, if you live in a household of 3 and more, it makes more sense. Family members may visit the same site at the same time, with each computer requesting the same IP. If you don’t have a device in your network that caches this request, it needs to be pulled off the internet only once. It makes even more sense if you live country side and don’t have a very speedy connection. Or in companies with a lot more users, campus etc.

If you want to see the Domain Name System in action, you can use nslookup to test it. On Linux it is often not installed by default, but dig is. nslookup can be installed on Linux via apt install dnsutils.

Here is an example with nslookup:

1
2
3
4
5
6
7
8
~ $: nslookup tuxstash.de
Server:  UnKnown
Address:  fe80::1916::edde:d500

Non-authoritative answer:
Name:    tuxstash.de
Addresses:  91.216.248.21
          91.216.248.22

As you can see, Windows used the IPV6 local-link address (those are like APIPA addresses, but actually useable) of my Raspberry to get the answer off the internet. The funny thing is, you actually can’t use those to connect to my website directly, because my hoster runs Virtual Hosts on Apache and only identifies requests by server alias. But that is a topic for another day.

Ok, so what are our tools?

I am using a Raspberry Pi as a device for services in my network. It is small and reasonably fast for this kind of thing. As DNS server, we are going to use bind9. It comes directly from the Internet Consortium . You can install it via

1
apt install bind9 bind9utils bind9-doc

How do I get started?

Since a networked device always needs to know where your Nameserver is located without relying on one, the Pi needs a static IP address. On Raspbian, this is done via the file /etc/dhcpcd.conf:

1
2
3
4
5
6
interface eth0

static ip_address=192.168.1.252/24
static ip6_address=FC00::DEAD:BEEF:BAD
static routers=192.168.1.254
static domain_name_servers=192.168.1.252 fc00::DEAD:BEEF:BAD

The Raspberry has the IP 192.168.1.252/24 in our scenario.

Of course, you need to adjust it to your IP setup in your home, it should be in the same network as your home router, unless you are running a big network with professional routers. Your network interface might also have a different name, especially if friendly names are enabled, but you can find that out via ip a.

What you might notice, is that it also includes an IPV6 address. Addresses beginning with fc00 are so called unique local address and corresponds to the private IPV4 addresses, such as 192.168.0.0, 172.16-32.0.0 or 10.0.0.0. This is important on Windows, because Windows will always prefer IPV6, especially when you test with nslookup.

Configuration

Generally, the config files we are looking for are inside of /etc/bind. We have lots of files there, but are only interested in one:

1
/etc/bind/named.conf.options

Without further ado, here is the code you need for this file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
acl goodclients{
        192.168.1.0/24;
        localhost;
        localnets;

        ::1/128;
        fc00::/64;


options {
        directory "/var/cache/bind";
        recursion yes;
        allow-query { goodclients; };
        listen-on { 192.168.1.252; };
        listen-on-v6 { fc00::DEAD:BEEF:BAD; };

        forwarders {
                1.1.1.1;
                8.8.8.8;
        };

        //========================================================================
        // If BIND logs error messages about the root key being expired,
        // you will need to update your keys.  See https://www.isc.org/bind-keys
        //========================================================================
        dnssec-enable yes;
        dnssec-validation yes;

        auth-nxdomain no;    # conform to RFC1035
};

Let’s have a look at it, step by step, shall we?

1
2
3
4
5
6
7
8
acl goodclients{
        192.168.1.0/24;
        localhost;
        localnets;

        ::1/128;
        fc00::/64;
};

acl means “access list”. So what we are doing here, is telling bind9 to see those addresses as trusted addresses. In our case, the Raspberry (identified by localhost) and the private address space we use, both in IPV4 and IPV6. localnets tells bind to match all IP that are in the same subnet as bind is. In my case, it is a bit redundant, because I only have one net at home, but in a bigger network this may be quite significant.

1
2
directory "/var/cache/bind";
recursion yes;

The first one is self-explanatory. It sets the data directory for the service. The second one requires a bit more explanation and the good folks over at zytrak have done a fine job at that. You may also choose no, default is yes, so I am going to leave it at that.

1
allow-query { goodclients; };

This puts our access-list from above into effect. It tells our service to accept all clients on this list.

1
2
listen-on { 192.168.1.252; };
listen-on-v6 { fc00::DEAD:BEEF:BAD; };

Here we tell bind on which interfaces to listen. This is also quite important. As you can see, I have told bind to only listen on the fc00:: IPV6 addresses. But since my phones do not get such an address and only use the equivalent of APIPA, you may choose to use any in the IPV6 section. But, it is up to you.

Example:

1
listen-on-v6 { any; };

Now comes the magic:

1
2
3
4
forwarders {
        1.1.1.1;
        8.8.8.8;
};

This is where bind9 get’s the DNS records then, which it cashes internally, until the caching time has expired. That time is set by the domain owner and we have no control over it. In my case, I have used Cloudflare (1.1.1.1) as my primary DNS and Google (8.8.8.8) as my secondary. You may choose different name servers, but that is up to you. I am comfortable with my choice.

To bring this into effect, the best way is to reboot your Pi, especially if you connect over SSH only, since you don’t want to have your network interface down without any ability to bring it back up. Or having to plug in a monitor and a keyboard. Generally, you can also restart bind with service bind9 restart. If there are any errors, you can see them with a quick journalctl -xe, after a restart. You can also use named-checkconf /etc/bind/filename to check a specific file’s syntax, before you attempt to restart the service.

The last thing you have to do, is to force the devices in your network to use those name server by default. That is done via DHCP and we are going to cover this in our next tutorial. You can assign it manually as well, for V4 and V6 in the network properties of Windows.

Conclusion

So, now you have learned how to configure a caching DNS. If you have a public WiFi or administrating a large office, your patrons will be very thankful for you having that, it can speed up the networking experience significantly. Have fun and keep a lookout for more tutorials. Send any comments or errata to contact@tuxstash.de.

Thanks for reading.

Link to the author's twitter Link to the authors ko-fi page

comments

Characters: 0/1000

gravatar portrait

 Pinned by contact@tuxstash.de

Come join the discussion and write something nice. You will have to confirm your comment by mail, so make sure it is legit and not a throwaway. Only the name part of it will be displayed, so don't worry about spam. If it does not show up after confirming it, it may be considered spam, but I curate them manually, so don't worry. Please read the privacy statement for more.