How to set up private nameservers (DNS servers)

Requirements:

  • A VPS with Debian 6 64 bit minimal (Any distro should do, but the example uses Debian 6)

Steps:

Install bind9:

apt-get update
apt-get install bind9

Now, edit this file:

#cat /etc/bind/named.conf.options
options {
        directory "/var/cache/bind";

        // If there is a firewall between you and nameservers you want
        // to talk to, you may need to fix the firewall to allow multiple
        // ports to talk.  See http://www.kb.cert.org/vuls/id/800113

        // If your ISP provided one or more IP addresses for stable
        // nameservers, you probably want to use them as forwarders.
        // Uncomment the following block, and insert the addresses replacing
        // the all-0's placeholder.

        // forwarders {
        //      0.0.0.0;
        // };

        auth-nxdomain no;    # conform to RFC1035
        listen-on-v6 { any; };
};

And:

# cat /etc/bind/named.conf.local
//
// Do any local configuration here
//

// Consider adding the 1918 zones here, if they are not used in your
// organization
//include "/etc/bind/zones.rfc1918";

// Domain Management drjoel.in

zone "drjoel.in" {
     type master;
     file "/var/lib/bind/db.drjoel.in";
     allow-update { key rndc-key; };
};
# This is the zone definition for reverse DNS. replace 31.167.199 with your network address in reverse notation - e.g my network address is 199.167.31
zone "31.167.199.in-addr.arpa" {
     type master;
     file "/etc/bind/zones/rev.14.31.167.199.in-addr.arpa";
};

You can check /etc/bind/named.conf.local for errors with:

#named-checkconf

If it finds errors, it will report like this:

 #named-checkconf
/etc/bind/named.conf.local:15: missing ';' before '}'

Now edit the master zone file for drjoel.in. This is the main zone record file (resource record file). No blank lines are permitted, except for a newline at the bottom. The latter is compulsory.

#cat "/var/lib/bind/db.drjoel.in"
drjoel.in.       IN      SOA     ns1.joel.co.in. admin.drjoel.in. (
                   2007010401           ; Serial
                         3600           ; Refresh [1h]
                          600           ; Retry   [10m]
                        86400           ; Expire  [1d]
                          600 )         ; Negative Cache TTL [1h]
;
drjoel.in.      IN      NS      ns1.joel.co.in.
drjoel.in.      IN      NS      ns2.joel.co.in.
drjoel.in.      IN      MX      10 aspmx.l.google.com.
drjoel.in.      IN      MX      20 alt1.aspmx.l.google.com.
drjoel.in.      IN      MX      20 alt2.aspmx.l.google.com.
drjoel.in.      IN      MX      30 aspmx2.googlemail.com.
drjoel.in.      IN      MX      30 aspmx3.googlemail.com.
drjoel.in.      IN      MX      30 aspmx4.googlemail.
drjoel.in.      IN      A       198.23.228.223
ns1.            IN      A       199.167.31.14
ns2.            IN      A       38.114.103.106
*.drjoel.in.    3600    IN      CNAME   drjoel.in.

The main records to note are the first line:

drjoel.in.       IN      SOA     ns1.joel.co.in. admin.drjoel.in. (

Here, the first word is “drjoel.in.”. Note the period at the end. Note that all domain names have a period at the end. The fourth coloumn has the primary nameserver. The last coloumn “admin.drjoel.in.” actually denotes the email address “[email protected]”.

Now edit the file for reverse records:

#cat "/etc/bind/zones/rev.14.31.167.199.in-addr.arpa"
//replace example.com with yoour domain name, ns1 with your DNS server name.
// The number before IN PTR example.com is the machine address of the DNS server. in my case, it's 1, as my IP address is 192.168.0.1.
@ IN SOA ns1.drjoel.in. admin.drjoel.in. (
                        2006081401;
                        28800;
                        604800;
                        604800;
                        86400
)

                     IN    NS     ns1.drjoel.in
14                   IN    PTR    drjoel.in

Here, my server’s ipv4 address is 199.167.31.14. The last number 14 is what is typed in the last line on the file.

The resource record file too can be checked for errors. This is with:

named-checkzone

It is invoked as follows. A sample error message is shown:

#named-checkzone relsoft.in /var/lib/bind/db.relsoft.in
/var/lib/bind/db.relsoft.in:1: no TTL specified; using SOA MINTTL instead
/var/lib/bind/db.relsoft.in:17: ignoring out-of-zone data (www)
/var/lib/bind/db.relsoft.in:18: ignoring out-of-zone data (ns1)
/var/lib/bind/db.relsoft.in:19: ignoring out-of-zone data (ns2)
zone relsoft.in/IN: NS 'ns1.relsoft.in' is a CNAME (illegal)
zone relsoft.in/IN: NS 'ns2.relsoft.in' is a CNAME (illegal)
zone relsoft.in/IN: not loaded due to errors.

 

Now, I need to edit /etc/resolv.conf:

#cat /etc/resolv.conf
search drjoel.in
nameserver 199.167.31.14

Here, the nameserver is the ip of this server

Once done, restart bind9:

service bind9 restart

Adding a second domain to use the same nameserver. The zone files etc are created just like previously. The only difference is in the resolv.conf file, which now looks like:

cat /etc/resolv.conf
search drjoel.in relsoft.in
nameserver 199.167.31.14

Note that relsoft.in has been added.

Adding a vanity DNS server:

#cat /var/lib/bind/db.relsoft.in
relsoft.in.       IN      SOA     ns1.joel.co.in. admin.relsoft.in. (
                   2007010401           ; Serial
                         3600           ; Refresh [1h]
                          600           ; Retry   [10m]
                        86400           ; Expire  [1d]
                          600 )         ; Negative Cache TTL [1h]
;
relsoft.in.     IN      NS      ns1.relsoft.in.
relsoft.in.      IN      NS      ns2.relsoft.in.
relsoft.in.      IN      MX      10 aspmx.l.google.com.
relsoft.in.      IN     MX      20 alt1.aspmx.l.google.com.
relsoft.in.      IN      MX      20 alt2.aspmx.l.google.com.
relsoft.in.      IN     MX      30 aspmx2.googlemail.com.
relsoft.in.      IN     MX      30 aspmx3.googlemail.com.
relsoft.in.      IN     MX      30 aspmx4.googlemail.com.
relsoft.in.     IN      A       198.23.228.223
www.            IN      A       198.23.228.223
ns1.relsoft.in.         IN      A       199.167.31.14
ns2.relsoft.in.         IN      A       38.114.103.106
mail.relsoft.in.        3600    IN      CNAME   ghs.google.com
*.relsoft.in.   3600    IN      CNAME   relsoft.in.

 

zone "relsoft.in" {
     type master;
     file "/var/lib/bind/db.drjoel.in";
     allow-update { key rndc-key; };
     allow-transfer { 199.167.31.14; };
};

Note the line:

allow-transfer { 199.167.31.14; };

Also note that the zone record for relsoft.in now has A records of ns1 and ns2 pointing to the ips of the actual nameservers (at ns1.joel.co.in, and ns2.joel.co.in), as did drjoel.in. But in addition, note that ns records now point to ns1.relsoft.in instead of ns1.joel.co.in. That’s a vanity DNS server. It looks like relsoft.in has its own nameserver, while in reality it is using the ns1.joel.co.in nameserver.

Before adding a Vanity server:

#dig ANY relsoft.in

; <<>> DiG 9.9.2-P1 <<>> ANY relsoft.in
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20966
;; flags: qr rd ra; QUERY: 1, ANSWER: 10, AUTHORITY: 2, ADDITIONAL: 6

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;relsoft.in.                    IN      ANY

;; ANSWER SECTION:
relsoft.in.             600     IN      A       198.23.228.223
relsoft.in.             600     IN      MX      30 aspmx3.googlemail.com.
relsoft.in.             600     IN      MX      20 alt1.aspmx.l.google.com.
relsoft.in.             600     IN      MX      30 aspmx4.googlemail.
relsoft.in.             600     IN      MX      10 aspmx.l.google.com.
relsoft.in.             600     IN      MX      30 aspmx2.googlemail.com.
relsoft.in.             600     IN      MX      20 alt2.aspmx.l.google.com.
relsoft.in.             600     IN      SOA     ns1.joel.co.in. admin.relsoft.in. 2007010401 3600 600 86400 600
relsoft.in.             600     IN      NS      ns2.joel.co.in.
relsoft.in.             600     IN      NS      ns1.joel.co.in.

;; AUTHORITY SECTION:
relsoft.in.             600     IN      NS      ns1.joel.co.in.
relsoft.in.             600     IN      NS      ns2.joel.co.in.

;; ADDITIONAL SECTION:
alt2.aspmx.l.google.com. 42     IN      A       173.194.64.27
alt2.aspmx.l.google.com. 70     IN      AAAA    2607:f8b0:4003:c02::1a
aspmx3.googlemail.com.  100     IN      A       173.194.64.27
ns1.joel.co.in.         85648   IN      A       199.167.31.14
ns2.joel.co.in.         920     IN      A       38.114.103.106

;; Query time: 163 msec
;; SERVER: 89.233.43.71#53(89.233.43.71)
;; WHEN: Sun May  5 07:26:17 2013
;; MSG SIZE  rcvd: 427

After:

#dig ANY relsoft.in

; <<>> DiG 9.9.2-P1 <<>> ANY relsoft.in
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55308
;; flags: qr rd ra; QUERY: 1, ANSWER: 10, AUTHORITY: 2, ADDITIONAL: 2

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;relsoft.in.                    IN      ANY

;; ANSWER SECTION:
relsoft.in.             600     IN      A       198.23.228.223
relsoft.in.             600     IN      MX      30 aspmx3.googlemail.com.
relsoft.in.             600     IN      MX      20 alt2.aspmx.l.google.com.
relsoft.in.             600     IN      MX      20 alt1.aspmx.l.google.com.
relsoft.in.             600     IN      MX      30 aspmx2.googlemail.com.
relsoft.in.             600     IN      MX      10 aspmx.l.google.com.
relsoft.in.             600     IN      MX      30 aspmx4.googlemail.com.
relsoft.in.             600     IN      SOA     ns1.joel.co.in. admin.relsoft.in. 2007010401 3600 600 86400 600
relsoft.in.             600     IN      NS      ns1.relsoft.in.
relsoft.in.             600     IN      NS      ns2.relsoft.in.

;; AUTHORITY SECTION:
relsoft.in.             600     IN      NS      ns2.relsoft.in.
relsoft.in.             600     IN      NS      ns1.relsoft.in.

;; ADDITIONAL SECTION:
alt2.aspmx.l.google.com. 115    IN      AAAA    2607:f8b0:4003:c02::1a

;; Query time: 353 msec
;; SERVER: 89.233.43.71#53(89.233.43.71)
;; WHEN: Sun May  5 07:45:14 2013
;; MSG SIZE  rcvd: 357

Note that:

relsoft.in.             600     IN      NS      ns1.joel.co.in.
relsoft.in.             600     IN      NS      ns2.joel.co.in.

has been replaced by:

relsoft.in.             600     IN      NS      ns2.relsoft.in.
relsoft.in.             600     IN      NS      ns1.relsoft.in.

Troubleshooting

Immediately after editing records, you have to check for syntax errors with:

named-checkconf
named-checkzone kgimoa.com /var/lib/bind/db.kgimoa.com

 

Checking error logs:

Bind9 error logs on Debian are stored in /var/log/daemon.log

#tail -n 10 /var/log/daemon.log
May  5 07:41:21 ns1 named[5846]: zone drjoel.in/IN: loaded serial 2007010401
May  5 07:41:21 ns1 named[5846]: /var/lib/bind/db.relsoft.in:1: no TTL specified; using SOA MINTTL instead
May  5 07:41:21 ns1 named[5846]: /var/lib/bind/db.relsoft.in:17: ignoring out-of-zone data (www)
May  5 07:41:21 ns1 named[5846]: zone relsoft.in/IN: loaded serial 2007010401
May  5 07:41:21 ns1 named[5846]: zone localhost/IN: loaded serial 2
May  5 07:41:21 ns1 named[5846]: managed-keys-zone ./IN: loading from master file managed-keys.bind failed: file not found
May  5 07:41:21 ns1 named[5846]: managed-keys-zone ./IN: loaded serial 0
May  5 07:41:21 ns1 named[5846]: running
May  5 07:41:21 ns1 named[5846]: zone drjoel.in/IN: sending notifies (serial 2007010401)
May  5 07:41:21 ns1 named[5846]: zone relsoft.in/IN: sending notifies (serial 2007010401)

 Automating nameserver synchronization

You can use an rsync in cron:

Example on my primary nameserver:

#crontab -l
# m h  dom mon dow   command
*/5 * * * * /usr/bin/rsync -az /var/lib/bind [email protected]:/var/lib/bind
*/5 * * * * /usr/bin/rsync -az /etc/bind/named.conf.local [email protected]:/etc/bind/named.conf.local

Those two lines are enough to sync DNS entries between the two nameservers. In terms of redundancy, I’m not sure how right I am, since if one of the servers has wrong entries, both entries get corrupt. However, since nameserver records are supposed to be identical, this is the only way I can assure that they are in sync perfectly.

Summary of Creation of a new zonefile (Checklist)

  1. Create the zone file from scratch or a template
  2. Edit the zone file and add proper entries
  3. Check the zone file with named-checkzone
  4. Create the entries for the zone file in /etc/bind/named.conf.local
  5. Restart bind9 manually, or optionally create a cron job that restarts the job every x mins
  6. Optionally create a symbolic link to /home.

You are reading this post on Joel G Mathew’s tech blog. Joel's personal blog is the Eyrie, hosted here.