home January 01, 2017

BIND


Bind Authoritative Caching DNS with DNSSEC (named.conf)

Bind (also referred to as named) is a DNS, or domain name server daemon. Bind has the ability to locally cache dns queries as well as serve authoritative name resolution. By using a locally cached dns server you can significantly speed up local dns resolution of commonly resolved names. You can also setup a resolving dns server and assign host names to the ip address of your internal LAN machines.

A caching server helps the most when a host name is asked for many times by local clients. For example, google.com, cnn.com and slashdot.com are all requested many times by most users. By caching the dns query your dns server can respond with the results quickly and without having to use any external bandwidth.

An authoritative DNS server means we control names on a domain. This example will setup a local LAN with internal names on the domain called "domain.lan.

Setting up an authoritative caching DNS server is the best of both worlds. It will resolve local domain.lan names and also cache the results of external names.

BIND (Berkeley Internet Name Domain) is the most commonly used DNS server on the Internet, especially on Unix-like systems, where it is a defacto standard. Supported by Internet Systems Consortium. BIND was originally created by four graduate students with CSRG at the University of California, Berkeley and first released with 4.3BSD. Paul Vixie started maintaining it in 1988 while working for DEC.

A new version of BIND (BIND 9) was written from scratch in part to address the architectural difficulties with auditing the earlier BIND code bases, and also to support DNSSEC (DNS Security Extensions). Other important features of BIND 9 include: TSIG, DNS notify, nsupdate, IPv6, rndc flush, views, multiprocessor support, and an improved portability architecture. It is commonly used on Linux and BSD systems. Wikipedia, Bind

HELPFUL HINT: If you are interested in a DNS solution which is faster and more secure that BIND, check out our Unbound resolving caching DNS (unbound.conf) and the NSD authoritative only DNS. Both support DNSSEC by design!

Getting Started

This exercise will setup a dns server available to the local LAN (10.10.10/24) in this example. It will cache all queries of external host names from internal clients and also serve out authoritative dns answers about our local LAN machines. This is a fully working example so you can cut/paste the examples below without issue.

IMPORTANT NOTE: In order to use DNSSEC and the signed root server trusted-key (KSK) you need to have a recent version of BIND (named) installed. For this example we are using BIND v9.7.1-P2 .

By default, OpenBSD installs bind and its support files into /var/named. We will be using the same directory structure for the example. You can use the following 3 files to replace what bind installs by default. The three files we are going to cover are /var/named/etc/named.conf which is the main config file, /var/named/master/db.domain.lan the forward lookup file and /var/named/master/db.10.10.10 which is the reverse lookup file.

As you look at the config files take some time and look at all of the options. There are a lot of things to learn about bind and we are only covering a small subset. Read the man pages and join the bind discussion groups. Block out a few minutes a day and find out what a few of the named.conf options do. You may find out the options here are perfect for your uses or you may want to tweak a few. Just take your time and have fun. Bind (named) is a really good dns server.

Before using the config file take a look it in the scrollable text window below. Under the window you will find a short explanation of the lines that need your attention. If you need more information take a look at the options in the man page. This file can be saved under /var/named/etc/named.conf in the standard OpenBSD install.

// $OpenBSD:  moneyslow.com BIND v9.7.1-P2 named.conf

options {
        allow-query { 127.0.0.1; 10.10.10/24; };
        allow-recursion { 127.0.0.1; 10.10.10/24; };
        allow-transfer { none; };
        dnssec-enable yes;
        dnssec-validation yes;
       #forward first;
        forwarders { 8.8.4.4; 8.8.8.8; };
        query-source address 127.0.0.1 port *;
        listen-on { 127.0.0.1; };
        listen-on-v6 { none; };
        version "named";
};

## Signed Root zone key for DNSSEC
## look lower down on this page for a script to,
## "Retrieve and independently verify the root zone"
# include "/var/named/etc/root_trusted_key";

## enable rndc commands
#controls {
#  inet 127.0.0.1 allow { localhost; };
#};

## disable rndc commands
controls { };

logging {
        category lame-servers { null; };
};

zone "." {
        type hint;
        file "standard/root.hint";
};

zone "localhost" {
        type master;
        file "standard/localhost";
        allow-transfer { localhost; };
};

zone "127.in-addr.arpa" {
        type master;
        file "standard/loopback";
        allow-transfer { localhost; };
};

zone "domain.lan" {
        type master;
        file "master/db.domain.lan";
        allow-update { none; };
};

zone "10.10.10.in-addr.arpa" {
        type master;
        file "master/db.10.0.10";
        allow-update { none; };
};

Looking at the named.conf

In the Options section we are going to setup the methods and access control lists bind will run with.

The "allow-query" and "allow-recursion" list is an access control list (acl) which limits access to the bind daemon to the ips listed.

The "allow-transfer" is disabled for this server. We do not have more than one dns server, so we do not allow zone transfers from this machine.

"Forwards" allow our dns server needs to ask another external dns server for queries we do not know. The "forwarders" directive is the list of fast dns server you have access to. Check at the end of this exercise about how to test dns server speed from your location. Our example has a few public Google DNS servers as "forwarders". The directive "forward first" simply tells bind to query the forwarders if our server does not have the answer. This means if we are not the authority for the domain or if we do not have a cached answer then ask the forwarders.

"include" statement is to a file holding the pre-verified root keys you trust. The root dns zone was just signed. You can can add the root zone KSK key here. This was all domain signed against the root zone can be validated.

"listen-on" is the ip the daemon will listen for queries on. We are listening on localhost and you can use pf to forward queries from the local lan to the dns server.

Finally, "version" is the version information the server will send to client who ask for it. We do not need anyone to know what version of Bind we are really running so we can replace the version string with the name of our machine or any other string.

In the Logging section we are simply asking the server to log all requests except lame-servers. Those are servers who are not valid and can be ignored.

The Zone sections tell our bind server what to do when a query comes in. For example, if a query from a local machine asks for Google.com then zone "com" { type delegation-only; }; instructs our bind server to delegate the query to a "forwards" dns server. If a query comes in for one of our local machine names like host2.domain.lan then our dns server will look in the file db.domain.lan for the answer.

Looking at the locally resolving db files

Once the bind server is installed and you have the named.conf file in place you can now setup the db files for your lan. Below are two files that support forward and reverse dns lookups. You will need both files.

The first file is the db.domain.lan and is the forward resolving db file. If you ask for the ip address associated with dhcp4.domain.lan bind will look in this file and respond with the ip 10.10.10.4. The forward lookup file contains A records only and has a default dns cache timeout of 86,400 seconds or 24 hours.

The scrollable text window below contains the /var/named/master/db.domain.lan

domain.lan.     86400   IN SOA  dns.domain.lan. root.dns.domain.lan. ( 1 10800 3600 6044800 86400 )
                86400   IN NS   dns.domain.lan.

dns.domain.lan.      86400   IN   A    10.10.10.1
host2.domain.lan.    86400   IN   A    10.10.10.2
host3.domain.lan.    86400   IN   A    10.10.10.3
dhcp4.domain.lan.    86400   IN   A    10.10.10.4
dhcp5.domain.lan.    86400   IN   A    10.10.10.5
dhcp6.domain.lan.    86400   IN   A    10.10.10.6
dhcp7.domain.lan.    86400   IN   A    10.10.10.7
dhcp8.domain.lan.    86400   IN   A    10.10.10.8
dhcp9.domain.lan.    86400   IN   A    10.10.10.9
dhcp10.domain.lan.   86400   IN   A    10.10.10.10
dhcp11.domain.lan.   86400   IN   A    10.10.10.11
dhcp12.domain.lan.   86400   IN   A    10.10.10.12
dhcp13.domain.lan.   86400   IN   A    10.10.10.13
dhcp14.domain.lan.   86400   IN   A    10.10.10.14

The following config is the db.10.10.10 and is considered to be the reverse lookup file. If a clients asks for the host name for the ip address 10.10.10.8 named will reply with dhcp8.domain.lan. The db file contains the PTR also referred to as pointer references from the ip to the host name.

The scrollable text window below contains the /var/named/master/db.10.10.10

10.10.10.in-addr.arpa. 86400   IN SOA  dns.domain.lan. root.dns.domain.lan. ( 1 10800 3600 6044800 86400 )
                       86400   IN NS   dns.domain.lan.

1.10.10.10.in-addr.arpa.    86400   IN   PTR   dns.domain.lan.
2.10.10.10.in-addr.arpa.    86400   IN   PTR   host2.domain.lan.
3.10.10.10.in-addr.arpa.    86400   IN   PTR   host3.domain.lan.
4.10.10.10.in-addr.arpa.    86400   IN   PTR   dhcp4.domain.lan.
5.10.10.10.in-addr.arpa.    86400   IN   PTR   dhcp5.domain.lan.
6.10.10.10.in-addr.arpa.    86400   IN   PTR   dhcp6.domain.lan.
7.10.10.10.in-addr.arpa.    86400   IN   PTR   dhcp7.domain.lan.
8.10.10.10.in-addr.arpa.    86400   IN   PTR   dhcp8.domain.lan.
9.10.10.10.in-addr.arpa.    86400   IN   PTR   dhcp9.domain.lan.
10.10.10.10.in-addr.arpa.   86400   IN   PTR   dhcp10.domain.lan.
11.10.10.10.in-addr.arpa.   86400   IN   PTR   dhcp11.domain.lan.
12.10.10.10.in-addr.arpa.   86400   IN   PTR   dhcp12.domain.lan.
13.10.10.10.in-addr.arpa.   86400   IN   PTR   dhcp13.domain.lan.
14.10.10.10.in-addr.arpa.   86400   IN   PTR   dhcp14.domain.lan.

Starting Bind

To start bind manually execute the daemon using "named -4". To start Bind (named) at boot you can edit your /etc/rc.conf.local file and put in the following line. If you have not made a rc.conf.local file you can always edit the /etc/rc.conf file and put the "-4" option in the named directive. The "-4" argument will simply start bind and listen to ipv4 address only.

named_flags="-4"

HELPFUL HINT: Check out our DNS Verify (ip to hostname to ip) script. It will help you verify your hostnames match your ip addresses and spot any problems in name resolution.

Building BIND from source

The version of BIND which comes with your OS might not be new enough to use DNSSEC. You may want to build BIND from source.

First goto the Internet Systems Consortium (ISC) webpage and download the latest version. We will download the tar file to /tmp, untar and change directory to the source tree.

cd /tmp
wget http://ftp.isc.org/isc/bind9/9.7.1-P2/bind-9.7.1-P2.tar.gz
tar zxvf bind-9.7.1-P2.tar.gz
cd bind-9.7.1-P2

We now need to build BIND. For our example we will be installing BIND into the directory /usr/local/named as to _not_ overwrite any version of BIND that came with the OS. You can always go back to the OS's version of BIND if you want to.

./configure --prefix=/usr/local/named && make && make install

Once the build finishes the binaries and config files will all be under the /usr/local/named directory. You can put the named.conf configuration file from the top of this tutorial in /usr/local/named/etc/named.conf.

If you want to update the root DNS servers.

/usr/local/named/standard
wget ftp://ftp.rs.internic.net/domain/db.cache -O root.hint

To run your custom version of BIND you need to specify the path to the binary and the configuration file.

/usr/local/named/sbin/named -c /usr/local/named/etc/named.conf

Retrieve and independently verify the root zone

In order to verify that a DNS entry equals an ip we need to use DNSSEC. This involves setting up named with a trusted "Key Signing Key" (KSK) from the root zone server; this is what the following script will do. Once the root zone is verified then the sub zones can be verified.

For example, if we are trying to go to moneyslow.com. The root zone is represented by a period ".". Using the method in this script we have the root zone KSK and we trust that key. The next sub zone is ".org". Org will be signed against the root zone "." so we know ".org" is safe. Finally, named checks "moneyslow.com" against the ".org" zone. If "moneyslow.com" passes the checks then we have a trusted path from root to .org to moneyslow.com and an ip we can trust. This is the basis for DNSSEC.

The reason for the shell script is the root zone server's will change their signature perhaps monthly. This is a simple shell script to download the root zone dnssec key. The script will retrieve the key using dns and verify it independently using the digest signature from the IANA website though https. If the digests match the trusted-key is put into the "/var/named/etc/root_trusted_key" file specified by the "include" directive in the named.conf . If the digests do not match the script exits. Copy the script from this scrollable window and put it into a shell script you can call "dnssec_verify_rootzone.sh"

NOTE: To use the signed root zone in DNSSEC validation in your BIND 9 resolvers, you must be running BIND 9.6 or higher. Earlier versions do not support the required algorithms to enable validation using the root zone's key. The recommended procedure to use differs for the BIND 9.6 series and later versions, including BIND 9.7. For BIND 9.6, you must use a trusted-keys statement, which must be manually updated when the root's key changes. For BIND 9.7 like in our example, the key can be automatically tracked by BIND using a managed-keys statement. For BIND 9.7 and later versions, using a managed-key allows automatic tracking of the key using a protocol known as RFC-5011.

#!/bin/sh
#
## moneyslow.com DNSSEC root zone 
#

echo ""
echo "   moneyslow.com DNSSEC root zone verification"
echo ""

## The location where the root trusted key will go
roottrustedkey="/var/named/etc/root_trusted_key"

## change to /tmp and clean up old files
cd /tmp
rm -rf root-*

## retrieve the root zone DNSKEY 
echo "retrieve the root zone DNSKEY using DNS  (root-ds)"
dig +noall +answer DNSKEY . > root-dnskey
dnssec-dsfromkey -f root-dnskey . > root-ds

## retrieve the IANA key digest
echo "retrieve the IANA key digest using https (root-anchors)"
wget -q --no-check-certificate https://data.iana.org/root-anchors/root-anchors.xml

## clean up the digest so we can clearly see the output
rootds=`cat root-ds | grep "8 2" | awk '{print $7 $8}'`
rootanchors=`cat root-anchors.xml | grep \<Digest\> | sed 's/<Digest>//' |sed 's/<\/Digest>//'`

## Print both digests for the user to view
echo " "
echo "Visually compare the digests"
echo -n " root-ds     : "; echo $rootds
echo -n " root-anchors: "; echo $rootanchors
echo " "

## verify that the "root zone DNSKEY" and the "IANA Digest" match
if [ $rootds = $rootanchors ]
 then
  echo " VERIFIED: Good Signature. Digests match."

  ## collect the verified DNSKEY
  keytype=`cat root-dnskey | grep 257 | awk '{print $5" "$6" "$7 }'`
  trustedkey=`cat root-dnskey | grep 257 | awk '{print substr($0, index($0,$8)) }'`

  ## put the DNSKEY in the format needed by named.conf include file
  echo "managed-keys {" > $roottrustedkey
  echo "   \".\" initial-key $keytype \"$trustedkey \";" >> $roottrustedkey
  echo "};" >> $roottrustedkey
  echo " "
  echo "done. The key is in $roottrustedkey"

 else 
  echo " FAILED: BAD Signature. NO MATCH !!"
  exit
fi

What does the output look like? Here we can see the output of the digests and the message saying that the signature was good and verified. The last part is the trusted-keys in the format which named.conf expects in the file specified by the "include" directive.

   moneyslow.com DNSSEC root zone verification

retrieve the root zone DNSKEY using DNS  (root-ds)
retrieve the IANA key digest using https (root-anchors)
 
Visually compare the digests
 root-ds     : 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
 root-anchors: 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
 
 VERIFIED: Good Signature. Digests match.
 
done. The key is in /disk01/support/named/etc/root_trusted_key


root@dns_server#  cat /var/named/etc/root_trusted_key
managed-keys {
   "." initial-key 257 3 8 "AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq QxA+Uk1ihz0= ";
};

How do I setup named to query the root servers and verify with DNSSEC ?

The example named.conf queries the DNS servers of Google. You can replace these ips with your ISP's dns server or OpenDNS or any other servers. But, what if you do not trust any one else's DNS server or want to resolve all ips yourself? This is easy to do by modifying the named.conf example.

First, comment out the two lines, "forward first;" and "forwarders { 8.8.4.4; 8.8.8.8.; };". We will not be forwarding or asking any other servers for information.

Second, if you want to use DNSSEC to verify the authenticity of the DNS responses make sure the "include "/var/named/etc/root_trusted_key";" line is no commented and you have followed the instructions for the "dnssec_verify_rootzone.sh" in the previous section.

To test if DNSSEC is working we will use dig. The dig command should give you the response for the root severs. You are looking for the "ad" flag for an authenticated response. If the ad is missing then the name server can not verify the dnssec path. This is an example from my machine.

user@machine$ dig +dnssec . | grep ";; flags:"
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL:

Questions?

Do you support the use of SRV dns records? Absolutely! SRV records allow one to specify a priority, weight and port to a service or protocol through dns records. For example, what if we wanted all web traffic to example.com to come in on port 8080 because our ISP blocks ports 80 and 443? We would set the service to "_http.", the protocol type to "_tcp." and the host to "example.com". Then we could specify "0 0 8080 www.example.com" for priority=0, weight=0 and port=8080 and the target hostname "www.example.com". Our finally record would look like "_http._tcp.example.com has SRV record 0 0 8080 www.example.com." This would clearly tell any client looking to connect to example.com's http web server to connect on port 8080. Simple and efficient.

The biggest problem with SRV records are that they are just not widely supported. SRV has been around since the year 2000 and developers (Firefox, Microsoft, and many many others) just do not support it. This is a really shame because SRV records are just _so_ incredibly useful.

Here is an example our moneyslow.com's public SRV records. These say that moneyslow.com's mail server is found at mail.moneyslow.com:25 and the web server is at moneyslow.com:80 and moneyslow.com:443 for SSL encrypted pages. Even though we are using the standard ports for our services we could use any ports we want to (port 12345 for http for example) if all clients supported SRV record lookups.

user@machine: host -t srv _smtp._tcp.moneyslow.com
_smtp._tcp.moneyslow.com has SRV record 0 0 25 mail.moneyslow.com.

user@machine: host -t srv _http._tcp.moneyslow.com
_http._tcp.moneyslow.com has SRV record 0 0 80 moneyslow.com.

user@machine: host -t srv _https._tcp.moneyslow.com
_https._tcp.moneyslow.com has SRV record 0 0 443 moneyslow.com.

Want to check your resolver's source port behavior?

To see if your version of BIND (named) is vulnerable to the latest "Birthday (CERT VU#800113)" vulnerability please goto dns-oarc. This will allow you to test the standard deviation of ports your DNS server used to access their DNS resolver.

How can I find a fast dns server to use for my "forwarders"? Ideally you want to use the local dns servers of your isp as they should be the fastest you have access to. You DO NOT want to use the root dns servers as they are not for individual use. You want to find local tier 3 dns servers like what your isp offers to you.

In the example we used two Google Public DNS servers (8.8.8.8 and 8.8.4.4 ) because they resolve quickly and are geographically close to the moneyslow.com server. You may want to use dns servers other than your isp to help anonymize browsing. Check out the OpenDNS site as well to find out more about their other free services like adult site blocking, phishing site blocks and statistics.

To test if the dns server for your isp or OpenDNS is performing better than others you can use the "dig" command. Here we see that a dns query for "google.com" pointed at each individual OpenDNS server return results in 11 to 25 milliseconds. Try to find dns servers that will respond in less than 50ms.

user@machine: dig google.com @208.67.222.222 | grep "Query time"
;; Query time: 11 msec

user@machine: dig google.com @208.67.220.220 | grep "Query time"
;; Query time: 25 msec

How do I turn off Firefox's caching ability? If you dislike Firefox's caching behavior (cache expires anywhere from 24 hours to 1 minute depending on your version) and your name server's cache just fine then disable all caching in the browser. Firefox's cache is just another level of expirations to go through. Here's the cross-platform method, if you should wish to do so:

In about:config, add two new integer entries:             
  network.dnsCacheExpiration  -> 0
  network.dnsCacheEntries     -> 0