Build your own DNS server on Linux

Learn how to use BIND to set up your own server for resolving domain names.
630 readers like this.
Pixelated globe

Geralt. CC0.

In the previous article in this two-part series, Introduction to the DNS (Domain Name System), I described how the DNS database is structured and how to configure name services on a client. I also listed and described some of the more common DNS records you are likely to encounter when building a name server or just trying to interpret the results of a dig command.

In this article, I show you how to build your own name server using BIND (Berkeley Internet Name Domain). It is not as difficult as you might think, especially because you can do it in two stages.

In this article, you will start by learning how to create a caching name server, then you will move on and learn how to upgrade that to a complete primary (master) domain name server for your network, complete with forward and reverse zone files.

Setting up a DNS server using BIND

Setting up a name server using BIND is quite straightforward, so I'll show you how to do so on any computer you might have available for experimentation. This little lab project will show you how to install and configure BIND on your computer as a caching name server, test it, then set it up as a primary name server with a zone file that you can use as a name resolver for your network or just for testing.

Setting up a name server on any GNU/Linux computer you have available is technically possible because it will not interfere with other hosts on the network or their operation. However, you should probably not do this on a computer that you do not own or have the right to modify unless you have explicit permission to do so.

My setup

You only need one computer to perform all but one of the tasks in this lab project. I use this setup on my much more powerful ThinkPad because the name servers provided by DHCP (Dynamic Host Configuration Protocol) when I connect to non-home networks using either wired or wireless connections can sometimes be unreliable. To show that almost any host can perform well as a name server, I have tested this project on an old ASUS EeePC 900 netbook.

I will use the private IP address of my ASUS for this project but you should use the IP address of the host that you are using.

The hosts file

First, let's take a look at the /etc/hosts file. In its default state, there should only be two lines in the hosts file, the first two lines seen in Listing 1, below.

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

# Lab hosts
192.168.25.1        server
192.168.25.21      host1
192.168.25.22      host2
192.168.25.23      host3
192.168.25.24      host4

Listing 1: You can maintain a simple hosts file to perform the function of a resolver in small networks.

Although you can add hostnames and their respective IP Addresses as shown in Listing 1, this is not an optimal solution to name services, especially when traveling. If there are other entries in your hosts file, you may need to comment them out for the duration of this project if they interfere with naming or IP addresses. Most of you will not have any entries other than the two default lines.

Preparation

A caching name server can't replace your use of /etc/hosts to resolve hostnames on the internal network; however, compared to using an ISP or other public name server a caching name server can improve performance when resolving commonly used external names, such as www.cnn.com. The best part is that setting up a caching name server is quite easy.

Before starting, you should prepare by performing the following steps.

First, make backup copies of the files /etc/hosts, /etc/named.conf, resolv.conf, and /etc/sysconfig/iptables.

If they are not already installed, use your distribution's package manager to install the following BIND RPMs: bind, bind-chroot, and bind-utils. To enable your lab host to use the caching name server, you must add a name server line to point to your own host in /etc/resolv.conf. For example, if your lab host IP Address is 192.168.0.203, as is my epc, add the following line to the top of the name server list in /etc/resolv.conf:

name server         192.168.0.203

Be sure to use the IP Address of the host on which you are doing this project.

You could use the IP Address of your localhost, 127.0.0.1 instead of the external IP address. You should also comment out any lines pointing to other hosts as name servers. Be sure to save the revised resolv.conf file.

These changes will take effect immediately and no reboot or service restart is required. Now attempt to ping a common public host that does not block ICMP (Internet Control Message Protocol) packets; feel free to use my firewall, which is a Raspberry Pi.

ping wally2.both.org

You should get an "unknown host" or "Name or service not known" error because you currently have no working DNS service or resolver defined in the resolv.conf file. Now use the dig command to see if name services is working.

dig wally2.both.com

You should get the error, "Connection timed out; no servers could be reached."

Set up a caching name server

A caching name server is not an authoritative source for any domain. It simply caches the results of all name resolver requests from the network that it serves to speed up responses to future requests for the same remote host.

Note: The named.conf file is very particular about syntax and especially punctuation. Semicolons are used to delineate the end of an entry and the end of a stanza as well as the end of a line. Be sure to add them in correctly as shown in the samples.

For the initial setup of the caching name server making a couple modifications to the default /etc/named.conf file is necessary, so edit that file using your favorite editor. First, add the IP address of your local test host to the "listen-on port 53" line as shown in Listing 2, below. This enables named to listen on the external IP Address of your host, so that other computers can use it as a name server as well.

By default, BIND refers to the Internet's root name servers to locate the authoritative name servers for a domain. It is possible to specify other servers that are called "Forwarders" to which the local instance of BIND will send requests instead of the root servers. This does increase the possibility of DNS hijacking.

Add a "forwarders" line as shown below. This tells your caching DNS server where to obtain IP Addresses when they are not already cached locally. The IP Addresses in the listing below is for the Google public DNS servers You could use your local ISP or OpenDNS or some other public name server as your forwarder. It is not necessary to define any forwarders and, in that case, BIND would use the Internet root servers as defined in the file /var/named/named.ca to locate the authoritative name servers for domains if no forwarders are defined. But for this exercise, please define the forwarders as I have in Listing 2.

Comment out the IPV6 line because we are not using IPV6 in the lab environment. Note that the "//" two forward slashes denote comments in the named.conf file.

//
// named.conf
// Provided by Red Hat bind package to configure the ISC BIND named(8) DNS
// server as a caching only name server (as a localhost DNS resolver only).
// See /usr/share/doc/bind*/sample/ for example named configuration files.
//
//

options {
        listen-on port 53 { 127.0.0.1; 192.168.0.203; };
//      listen-on-v6 port 53 { ::1; };
        forwarders { 8.8.8.8; 8.8.4.4; };
        directory       "/var/named";
        dump-file       "/var/named/data/cache_dump.db";
        statistics-file "/var/named/data/named_stats.txt";
        memstatistics-file "/var/named/data/named_mem_stats.txt";
        allow-query     { localhost; 192.168.0.0/24; };
        recursion yes;


        dnssec-enable yes;
        dnssec-validation yes;
        dnssec-lookaside auto;


        /* Path to ISC DLV key */
        bindkeys-file "/etc/named.iscdlv.key";


        managed-keys-directory "/var/named/dynamic";
};
logging {
        channel default_debug {
                file "data/named.run";
                severity dynamic;
        };
};
zone "." IN {
        type hint;
        file "named.ca";
};
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";

Listing 2: The /etc/named.conf file provides the simple configuration required to set up a caching name server. The lines that need to be added or changed are highlighted in bold.

Add the local network address, 192.168.0.0/24, to the allow-query line. This line specifies the network(s) from which DNS queries will be accepted by this DNS server.

Start the name service

Now start the named service and configure the named service to start at every boot. I use the systemctl command on my Fedora host, but the command may be different on your host, depending upon the distribution you are using. Note that the name of the BIND resolver service is named.

systemctl enable named
systemctl start named

The first test you can perform to ensure that your caching name server is working is to use dig to locate the DNS database information for wally2.both.org. To further test your caching name server, use the dig command to obtain the IP Address(es) for some common Internet websites, such as www.opensource.com, CNN, Wired, and any others you like. The results should now show your host as the responding server.

At this point your caching name server will correctly resolve hosts on the Internet and that is because those DNS requests for public hosts are forwarded to the Google public name servers—refer to the "forwarders" line in named.conf. However, you are still dependent upon the /etc/hosts file for internal name services. Creating a primary name server can solve that problem.

Creating a primary name server

Once you create a caching name server, converting it into a full-fledged primary name server is not too difficult. A primary name server is the authoritative source for the domain it represents.

You need to change named.conf again and create a couple new files. You'll create a domain called Example.com, which is a domain name reserved for example purposes in documents like this one. The Example.com domain does have an IP address on the Internet and a very sparse website, but you can use the name in the rest of your lab project without causing problems for anyone. You'll use the Example.com domain as the internal domain name for the rest of this exercise.

The two new files you'll create are the forward and reverse zone files, which you'll place in the /var/named directory. This location is specified by the "directory" directive in the named.conf configuration file.

Create the forward zone file

The forward zone file contains "A" records that pair the names of the hosts in the zone, aka domain, with their respective IP addresses. It may also contain CNAME records, which are aliases for the real hostnames in the A records, and MX records for mail servers.

Create a basic forward zone file, /var/named/example.com.zone, and add the following lines to it. Your zone file should look like the sample zone file in Listing 3, below, when you're finished.

; Authoritative data for example.com zone
;
$TTL 1D
@   IN SOA  epc.example.com   root.epc.example.com. (
                                       2017031301      ; serial
                                       1D              ; refresh
                                       1H              ; retry
                                       1W              ; expire
                                       3H )            ; minimum

$ORIGIN         example.com.
example.com.            IN      NS      epc.example.com.
epc                     IN      A       127.0.0.1
server                  IN      A       192.168.25.1
www                     IN      CNAME   server
mail                    IN      CNAME   server
test1                   IN      A       192.168.25.21
t1                      IN      CNAME   test1
test2                   IN      A       192.168.25.22
test3                   IN      A       192.168.25.23
test4                   IN      A       192.168.25.24

; Mail server MX record
example.com.            IN      MX      10      mail.example.com.

Listing 3: The forward zone file for the Example.com domain contains the hostnames and their IP addresses for this domain.

The first non-comment line in Listing 3 is the Time to Live specifier, which in this case is one day for all records that are not otherwise specified. D stands for Day. The specifiers in the SOA (Start of Authority) line are just as obvious. Details of the parameters in the SOA record are described in some detail here.

The NS record must have the FQDN (Fully Qualified Domain Name) of the host on which you are performing this lab project. There must also be an A record in the file with a valid IP address for the host. In this case, you should use the localhost IP address of 127.0.0.1.

The entries shown above will give you a few hostnames with which to experiment.

Be sure to use today's date and append a counter starting at 01 for the serial number. The serial number above is the first change of March 4, 2017. The serial number is incremented whenever the zone file is changed. If there were secondary name servers that used this one for a primary, they would not be updated unless the serial number is incremented.

Add the forward zone files to named.conf

Before your DNS server will work, however, you need to create an entry in /etc/named.conf that will point to your new zone file. Add the following lines below the entry for the top-level hints zone but before the "include" lines.

zone "example.com" IN {
        type master;
        file "example.com.zone";
};

Listing 4: Add these lines to the named.conf file to add the Example.com zone file to the resolver configuration.

Now restart named to make these changes take effect. Test your name server by using the dig and nsloookup commands to obtain the IP Addresses for the hosts you have configured in the forward zone file. Note that the host does not have to exist on the network for the dig and nslookup commands to return an IP Address.

dig test1.example.com
dig t1.example.com
dig mx example.com
dig mail.example.com
nslookup test3.example.com
dig www.amazon.com

Be aware that using the FQDN for these commands is necessary except for the nslookup command as long as the domain and search entries of Example.com are provided in the /etc/resolv.conf file. In this case, they probably are not, so just use the FQDNs for all testing in this project.

Using the root name servers

Notice that the root name servers are given as the authoritative servers for the Amazon.com lookup. But remember you're using the Google public name servers as forwarders. Now comment out the forwarders line in named.conf and restart named. Run the above commands again to compare the results that are returned. The results should look similar to the ones below in Listing 5.

# dig www.amazon.com     

; <<>> DiG 9.10.4-P6-RedHat-9.10.4-4.P6.fc25 <<>> www.amazon.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65004
;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 4, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.amazon.com.                        IN      A

;; ANSWER SECTION:
www.amazon.com.         1800    IN      CNAME   www.cdn.amazon.com.
www.cdn.amazon.com.     300     IN      CNAME   d3ag4hukkh62yn.cloudfront.net.
d3ag4hukkh62yn.cloudfront.net. 60 IN    A       52.85.147.120
d3ag4hukkh62yn.cloudfront.net. 60 IN    A       52.85.147.50
d3ag4hukkh62yn.cloudfront.net. 60 IN    A       52.85.147.92
d3ag4hukkh62yn.cloudfront.net. 60 IN    A       52.85.147.109

;; AUTHORITY SECTION:
d3ag4hukkh62yn.cloudfront.net. 1831 IN  NS      ns-1144.awsdns-15.org.
d3ag4hukkh62yn.cloudfront.net. 1831 IN  NS      ns-130.awsdns-16.com.
d3ag4hukkh62yn.cloudfront.net. 1831 IN  NS      ns-2021.awsdns-60.co.uk.
d3ag4hukkh62yn.cloudfront.net. 1831 IN  NS      ns-824.awsdns-39.net.

;; Query time: 3857 msec
;; SERVER: 192.168.0.203#53(192.168.0.203)
;; WHEN: Mon Mar 13 09:18:30 EDT 2017
;; MSG SIZE  rcvd: 306

Listing 5: The results of a lookup on www.amazon.com have some interesting information including times to live for the various record types.

When I did this, the first call to resolve the external address for Amazon took 3857ms while the data was located and returned. Subsequent results to perform the same query was 1ms, which shows the advantage of caching resolver results locally. Notice the numbers 1800, 300, and 60 in the answer section lines and 1831 in authority section lines—these are TTL (Times To Live) in seconds. If you perform the lookup multiple times, these numbers will change, showing the amount of time that the records have remaining to live in local cache.

Creating the reverse zone file

A reverse zone for your domain will provide the ability to do reverse lookups. Many organizations do not do these internally, but reverse lookups can be helpful in doing problem determination. Many spam fighting configurations, such as SpamAssassin, look for reverse lookups to verify valid email servers.

Create the reverse zone file, /var/named/example.com.rev and add the following contents. Be sure to use an appropriate serial number.

; Authoritative data for example.com  reverse zone
;
$TTL 1D
@   IN SOA  test1.example.com   root.test1.example.com. (
                                        2017031501      ; serial
                                        1D              ; refresh
                                        1H              ; retry
                                        1W              ; expire
                                        3H )            ; minimum

@       IN      NS      epc.example.com.
example.com.    IN      NS      epc.example.com.
1               IN      PTR     mail.example.com.
1               IN      PTR     server.example.com.
21              IN      PTR     test1.example.com.
22              IN      PTR     test2.example.com.
23              IN      PTR     test3.example.com.
24              IN      PTR     test4.example.com.

Listing 6: Use this reverse zone file, example.com.rev, for your name server.

You could also name your reverse zone file /var/named/25.168.192.in-addr.arpa, which follows older conventions. You can actually name it anything you want because you will point to it explicitly in the named.conf file, but using one of the two conventions will make it easier for others to follow your work.

Add the reverse zone to named.conf:

zone    "25.168.192.in-addr.arpa" IN {
       type master;
       file "example.com.rev";
};

Listing 7: Adding this stanza to the named.conf file enables reverse lookups.

Add the stanza in Listing 7 to the /etc/named.conf file to point to the new reverse zone. Now reload named and test your reverse zone using the commands in Listing 8. Your results should look similar to those below.

​systemctl reload named

# dig -x 192.168.25.23

; <<>> DiG 9.10.4-P6-RedHat-9.10.4-4.P6.fc25 <<>> -x 192.168.25.23
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48607
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;23.25.168.192.in-addr.arpa.    IN      PTR

;; ANSWER SECTION:
23.25.168.192.in-addr.arpa. 86400 IN    PTR     test3.example.com.

;; AUTHORITY SECTION:
25.168.192.in-addr.arpa. 86400  IN      NS      epc.example.com.

;; Query time: 21 msec
;; SERVER: 192.168.0.203#53(192.168.0.203)
;; WHEN: Wed Mar 15 16:18:59 EDT 2017
;; MSG SIZE  rcvd: 112

Listing 8: After restarting named you should see results similar to these when you do a reverse lookup on an IP address in the reverse zone.

Be sure to test some of the other reverse entries in your network and also try the following as well as other reverse lookups you want to experiment with. The -x option means reverse lookup.

dig -x 192.168.25.23
dig -x 192.168.25.1

Note that not all hosts that have entries in the forward zone need to have entries in the reverse zone, but it does make for more consistent results if they do.

At this point, you have a working name server using BIND. However, external hosts can't yet use this name server because the firewall should not yet be configured to allow DNS requests.

Configuring IPTables for DNS

You can do this step if you want other hosts on your local network to use your host as their name server.

The firewall on your test host probably blocks access to your host for name services. IPTables must be configured to allow UDP (User Datagram Protocol) packets inbound on your name server in order for other hosts to use it for name resolution. Use the following commands to add the required entries and save them.

Add a rule to your firewall with firewalld or IPTables that allows incoming packets on port 53 (domain) for UDP and save the new ruleset. Be sure to insert the new rule after the -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT line, so you will have to count the number of INPUT lines in the filter table in order to do that. The number 7 in the following command means that this rule will be inserted in position number 7 in the existing INPUT rules.

iptables -t filter -I INPUT 7 -p udp -m conntrack --ctstate NEW -m udp --dport 53 -j ACCEPT

You can save your new firewall rules if you like, and you would if this were to be a permanent installation and not a lab project. Then test this from one of your other hosts using the command in Listing 9, below. The @epc argument tells the dig command to use the specified name server with the hostname epc. You should substitute either the IP address of the DNS server you have just created, or a resolvable hostname on your network that points to your new name server. Of course, you could always add that hostname with its IP address to the /etc/hosts file of the host you are using for the remote test.

# dig @epc test1.example.com

; <<>> DiG 9.10.4-P6-RedHat-9.10.4-4.P6.fc25 <<>> @epc test1.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27957
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;test1.example.com.             IN      A

;; ANSWER SECTION:
test1.example.com.      86400   IN      A       192.168.25.21

;; AUTHORITY SECTION:
example.com.            86400   IN      NS      epc.both.org.

;; Query time: 0 msec
;; SERVER: 192.168.0.203#53(192.168.0.203)
;; WHEN: Mon Mar 13 08:45:34 EDT 2017
;; MSG SIZE  rcvd: 92

Listing 9: Testing the name resolver you have created from a different host on the same network.

Cleanup

For cleanup, you should perform the following tasks using the tools appropriate for your distribution. You may just wish to keep this name server for your network if you do not already have one.

  1. Restore the original /etc/hosts file.
  2. Stop named on the resolver host used for this lab project.
  3. Disable the named service.
  4. Delete the zone files.
  5. Restore the original named.conf file.
  6. Restore the original resolv.conf file.

Final thoughts

The functioning of name services seemed very obscure to me until I actually created a name server for my network using BIND. It is quite straightforward and can improve DNS lookup performance. Having your own name server can also prevent many of the relatively minor yet annoying name service interruptions caused by poorly maintained ISP name servers.

Note that, even though my little EeePC is running with 100% CPU usage for Seti@Home, it responds extremely quickly to resolver requests. You should be able to try this project on any Linux host you have available with minuscule impact. I hope that many of you will try to set up your own name server and experiment with it. The specifics of your name server installation will depend upon the details of your host and network.

Resources

David Both
David Both is an Open Source Software and GNU/Linux advocate, trainer, writer, and speaker. He has been working with Linux and Open Source Software since 1996 and with computers since 1969. He is a strong proponent of and evangelist for the "Linux Philosophy for System Administrators."

5 Comments

Amazing article. This should be required reading for budding sys admins.

For straightforward deployments, dnsmasq is a far more convenient solution.

Why don't you also open port 53 (domain) for TCP? This is required.

UDP is the primary protocol for DNS. However from Wikipedia:
======
Protocol transport
DNS primarily uses the User Datagram Protocol (UDP) on port number 53 to serve requests.[3] DNS queries consist of a single UDP request from the client followed by a single UDP reply from the server. The Transmission Control Protocol (TCP) is used when the response data size exceeds 512 bytes, or for tasks such as zone transfers. Some resolver implementations use TCP for all queries.
======
And apparently some name resolvers and ISPs use TCP but not UDP. So , yes, I could have opened TCP on port 53, but I chose not to for this relatively simple example.

Thank you for pointing that out.

In reply to by CertDepot (not verified)

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.