Setting up Private DNS Server with BIND9 (DNS Series:Part II)

9 minute read

We already dived into the nitty gritty of the theory about what exactly it is, how it works and what are its parts you can read the part one here. In this series, I will be talking about how to set up a private DNS server using BIND9. Before that, I want to focus on Private and Public DNS.

Like we talked before DNS servers resolve the domain names into IP addresses but there might be the cases that you might have to resolve the domain names to local IPs. For example, let say there is a privately hosted file server in your office network, so you might have to provide domain name of the server instead of providing the IP address. In this case, you might have to create a private DNS server. For the security season it is inadvertently advised to separate private and public DNS server for security and privacy issue, since public exposure of the internal network IPs(or active directory information) to outsiders which might help an attacker compromise the office system.

Apart from this, there is another method called split-horizon DNS server DNS server which serves as both public and private DNS server as in it serves different responses depending on the source IP. For example, the DNS server serves example.com or example. local depending upon the public or private request.

Without further ado lets dive into setting up a private DNS server:

Infrastructure

SN Hostname IP FQDN Remarks
1. ServerNode 10.10.10.10 ns.test.example.com DNS Server
2. FileServerNode 10.10.10.11 fileserver.test.example.com FileServer
3. ClientNode 10.10.10.9 client.test.example.com to test DNS Server

Installing BIND on DNS Servers(Ubuntu Node)

On serverNode update the package

ServerNode$ sudo apt-get update

Now install BIND:

ServerNode$ sudo apt-get install bind9 bind9utils bind9-doc

IPv4 Mode

We need to enable bind to run IPv4, so we provide argument -4 in the ExecStart directive in service file /etc/systemd/system/bind9.service

. . .
[Service]
ExecStart=/usr/sbin/named -f -u bind -4

Save and close the editor when you are finished. Reload the systemd daemon to read the new configuration into the running system: Restart BIND to implement the changes:

ServerNode$ sudo systemctl daemon-reload
ServerNode$ sudo systemctl restart bind9

Finally lets configure our bind:

Configure file: named.conf.options

Lets edit the named.conf.options file. This file contains the main configuration of the DNS server. We can provide the list of IPs or subnet which can access the the DNS through Access Control list (ACL). In our case we only want these three nodes to access our DNS. Edit /etc/bind/named.conf.options as below:

acl "trusted" {
        10.10.10.10;    # ServerNode - can be set to localhost
        10.10.10.11;    # FileserverNode
        10.10.10.12;    # Client Node
};

options {

...

Now that we have our list of trusted DNS clients, we will want to edit the options block. Currently, the start of the block looks like the following:

. . .
};

options {
      directory "/var/cache/bind"; #/var/cache/bind/ stores all the DNS query information
      recursion yes;                 # enables [recursive] queries
      allow-recursion { trusted; };  # allows recursive queries from "trusted" clients
      listen-on { 10.10.10.10; };   # DNS server private IP address - listen on private network only
      allow-transfer { none; };      # disable zone transfers by default

      forward first;
      forwarders {
            8.8.8.8;
            8.8.4.4;
        };
. . .
}

As you can see that I have set recursion to yes, this means that when your query is recieved by the server it will do all the job of fetching the answer, and giving it back to you. During this process, the DNS server might also query other DNS server’s in the internet on your behalf, for the answer.

Forward and forwarders:

The DNS forwarding facility of BIND Version 8 can be used to create a large site-wide cache on a few servers, reducing traffic over links to external nameservers. It can also be used to allow queries by servers that do not have direct access to the Internet, but wish to look up exterior names anyway. Forwarding occurs only on those queries for which the server is not authoritative and does not have the answer in its cache.

Forward : Causes the the server to query the forwarder first , and if that doesn’t answer the question the server will then look for the answer itself.

Forwarder: Specifies the IP addresses to be used for forwarding

Configure Local File

what are zones in DNS?

DNS Zone: DNS zones are the adminstrative portion within DNS.In other words,they are administrative space which allows for more granular control of DNS components, such as authoritative nameservers. Readmore here

Configure file: /etc/bind/named.conf.local

To configure zones file, nn ServerNode, open the named.conf.local file for editing:

ServerNode$ sudo vi /etc/bind/named.conf.local

Add the forward zone with the following lines (substitute the zone name with your own): In /etc/bind/named.conf.local

zone "test.example.com" {
    type master;
    file "/etc/bind/zones/db.test.example.com"; # zone file path
};

zone "10.10.10.in-addr.arpa" {     # for reverse query
    type master;
    file "/etc/bind/zones/db.10.10.10";  # 10.10.10.0/24 subnet
};

Here zone file db.10.10.10 is for reverse query i.e to get domain name from IP.

If your servers span multiple private subnets but are in the same datacenter, be sure to specify an additional zone and zone file for each distinct subnet. When you are finished adding all of your desired zones, save and exit the named.conf.local file.

Now that our zones are specified in BIND, we need to create the corresponding forward and reverse zone files.

Create Forward Zone File

The forward zone file is where we define DNS records for forward DNS lookups. That is, when the DNS receives a name query, “fileserver.test.example.com” for example, it will look in the forward zone file to resolve fileservers’s corresponding private IP address.

Types of DNS records:

  1. SOA: An SOA record or Start of Authority record labels a zone file with the name of the host where it was originally created. Next, it lists the contact email address for the person responsible for the domain. There are also various numbers, which we’ll get into in detail in a moment. First, here’s a typical SOA record:
    @	IN	SOA	ns.test.example.com. admin.test.example.com. (
            3		; Serial
       604800		; Refresh
        86400		; Retry
      2419200		; Expire
       604800 )
    

    Here’s what the numbers mean:

  • Serial number: The revision number for this domain’s zone file. It changes when the file gets updated.
  • Refresh time: The amount of time (in seconds) a secondary DNS server will keep the zone file before it checks for changes.
  • Retry time: The amount of time a secondary DNS server will wait before retrying a failed zone file transfer.
  • Expire time: The amount of time a secondary DNS server will wait before expiring its current zone file copy if it cannot update itself.
  • Minimum TTL: The minimum amount of time other servers should keep data cached from this zone file.
  1. NS: NS records or name server records set the nameservers for a domain or subdomain. The primary nameserver records for your domain are set both at your registrar and in your zone file. Typical nameserver records (you need at least two) look like this:
example.com     NS      ns.test.example.com.
  1. A and AAAA: An A record points your domain or subdomain to your servers’s IP address, which allows tracffice to reach your server. This is the core function of DNS. A typical A record looks like either of the following:
example.com             A      12.34.56.78

test.example.com       A       12.34.56.78

An AAAA record is just like an A record, but for IPv6 IP addresses. A typical AAAA record looks like the following:

example.com     AAAA        0123:4567:89ab:cdef:0123:4567:89ab:cdef
  1. CNAME : A CNAME record or Canonical Name record matches a domain or subdomain to a different domain. With a CNAME record, DNS lookups use the target domain’s DNS resolution as the alias’s resolution. Here’s an example:
alias.com       CNAME   example.com.
example.com     A       12.34.56.78

With this setup, when alias.com is requested, the initial DNS lookup will find the CNAME entry with the target of example.com. A new DNS lookup will be started for example.com, which will find the IP address 12.34.56.78. Finally, visitors to alias.com will be directed to 12.34.56.78.

  1. PTR: A PTR record or pointer record matches up an IP address to a domain or subdomain, allowing reverse DNS queries to function. It performs the opposite service an A record does, in that it allows you to look up the domain associated with a particular IP address, instead of vice versa.

PTR records are usually set with your hosting provider. They are not part of your domain’s zone file. This means that you’ll always set reverse DNS for your Linodes in the Linode Manager, even if your nameservers are elsewhere. Likewise, if you have servers somewhere else but are using Linode’s nameservers, you will still have to set up your PTR records with your hosting provider.

As a prerequisite for adding a PTR record, you need to create a valid, live A or AAAA record that points the desired domain to that IP. If you want an IPv4 PTR record, point the domain or subdomain to your Linode’s IPv4 address. If you want an IPv6 PTR record, point the domain to your Linode’s IPv6 address. Beyond that, IPv4 and IPv6 PTR records work the same way.

Apart from these there few other types, namely MX, DKIM,AXFR.

After that bit of theory, let’s create the directory where our zone files will reside. According to our named.conf.local configuration, that location should be /etc/bind/zones:

ServerNode$ sudo mkdir /etc/bind/zones

We will base our forward zone file on the sample db.local zone file. Copy it to the proper location with the following commands:

ServerNode$ cd /etc/bind/zones
ServerNode$ sudo cp ../db.local ./db.test.example.com

Configure file: /etc/bind/zones/db.test.example.com

Now let’s edit our forward zone file:

ServerNode$ sudo vi  /etc/bind/zones/db.test.example.com

so it look like this

;
; BIND data file for local loopback interface
;
$TTL	604800
@	IN	SOA	ns.test.example.com. admin.test.example.com. (
			      3		; Serial
			 604800		; Refresh
			  86400		; Retry
			2419200		; Expire
			 604800 )	; Negative Cache TTL

; name servers - NS records
    IN      NS      ns.test.example.com.



; name servers - A records
ns.test.example.com.          IN      A       10.10.10.10

; 10.128.0.0/16 - A records
fileserver.test.example.com.    IN      A      10.10.10.11
client.test.example.com.        IN      A      10.10.10.12

Create Reverse Zone File(s)

Configure file: /etc/bind/zones/db.test.example.com

Similarly you can create reverse DNS Zone file /etc/bind/zones/db.10.10.10 as:

$TTL	604800
@	IN	SOA	ns.test.example.com. admin.test.example.com. (
			      3		; Serial
			 604800		; Refresh
			  86400		; Retry
			2419200		; Expire
			 604800 )	; Negative Cache TTL

; name servers - NS records
    IN      NS      ns.test.example.com.



; name servers - A records
10           IN     PTR        ns.test.example.com.       ;10.10.10.10

; 10.128.0.0/16 - A records
11           IN     PTR        filserver.test.example.com.  ;10.10.10.11     
12           IN     PTR        client.test.example.com.     ;10.10.10.12  

We are almost done with configuring the DNS server. DNS configuration file is very obfuscating at times. It wont be be surprizing to miss some configuration so its a good idea to check if the configuration has been done correctly or not. For this BIND provides checking mechanism

ServerNode$ sudo named-checkconf

check Zone files:

ServerNode$ sudo named-checkzone test.example.com db.test.example.com

Similarly,

ServerNode$ sudo named-checkzone 10.10.10.in-addr.arpa /etc/bind/zones/db.10.10.10

Once that checks out, restart BIND:

ServerNode$ sudo systemctl restart bind9

Testing:

In order to test our DNS we first need to change the nameserver of the client machine to the DNS server. In ubuntu this can be done by manually editing /etc/resov.conf as

# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 10.10.10.10   #DNS Server's ip address
search test.example.com

Now you client machine should be able to ping the fileserver host.

vagrant@ClientNode:~$ ping fileserver
PING fileserver.test.example.com (10.10.10.11) 56(84) bytes of data.
64 bytes from filserver.test.example.com (10.10.10.11): icmp_seq=1 ttl=64 time=1.54 ms
64 bytes from filserver.test.example.com (10.10.10.11): icmp_seq=2 ttl=64 time=1.04 ms
64 bytes from filserver.test.example.com (10.10.10.11): icmp_seq=3 ttl=64 time=1.46 ms
64 bytes from filserver.test.example.com (10.10.10.11): icmp_seq=4 ttl=64 time=0.960 ms

Voila!!

References

https://www.digitalocean.com/community/tutorials/how-to-configure-bind-as-a-private-network-dns-server-on-ubuntu-16-04 https://www.ntchosting.com/encyclopedia/dns/zone/#The_DNS_zone https://www.linode.com/docs/networking/dns/dns-records-an-introduction/#dkim https://www.slashroot.in/difference-between-iterative-and-recursive-dns-query