Basic master and slave DNS setup with Bind

DNS or Domain Name System is one of the most important building blocks of the modern IT and internet. DNS allows you to use meaningful names instead of IP addresses. Especially since IPv6 is getting more popular, DNS remains a very important part of your network. This article will describe how to set up a basic master DNS-server and a slave which will replicate the data from the master.

Why a master and a slave

No single system or installation has a perfect 100% uptime and since DNS can be a quite critical component of your network, it is recommended to have a secondary DNS to provide a backup in case the primary one fails. You could simply set up two equal DNS-servers which have the same configuration and data. While this would perfectly work, it would require you to do all changes two times. Once on the primary and once on the secondary.

By setting up one server as the master and the other as the slave, the slave will replicate it’s zone-data from the master to itself. This way, when making changes in some zone on the master, the slave gets notified and takes over the changes almost instantly without the need to do any configuration on the slave’s side.

The master

The example will be done with CentOS 7 but the syntax of the commands should be equal for almost all Linux and Unix variants since Bind is quite universal.

We will start by configuring a single master DNS. The IP of the machine which will be the master is 192.168.202.101.

The first step is to install bind and bind-utils:

[jensd@master ~]$ sudo yum install bind bind-utils
...
Complete!

After the installation, perform a basic bind configuration by adjusting the sample configuration file in /etc/named.conf. We will also add two test zones.

/etc/named.conf on the master:

options {
        listen-on port 53 { 127.0.0.1; 192.168.202.101;};
        #listen-on-v6 port 53 { ::1; };
        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     { any;};
        recursion no;
        dnssec-enable no;
        dnssec-validation no;
        dnssec-lookaside auto;
        bindkeys-file "/etc/named.iscdlv.key";
        managed-keys-directory "/var/named/dynamic";
        pid-file "/run/named/named.pid";
        session-keyfile "/run/named/session.key";
};
logging {
        channel default_debug {
                file "data/named.run";
                severity dynamic;
        };
};
zone "blaat.test" {
        type master;
        file "/var/named/data/db.blaat.test";
        check-names fail;
        allow-update { none; };
        allow-query { any; };
};
zone "miauw.test" {
        type master;
        file "/var/named/data/db.miauw.test";
        check-names fail;
        allow-update { none; };
        allow-query { any; };
};
zone "." IN {
        type hint;
        file "named.ca";
};
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";

The above file differs from the supplied example on the following lines:

2: added this machines IP address
3: commented the line since we don’t do anything with IPv6 here
8: allow querying from everywhere
9: disable recursion by default (you don’t want this on a public DNS)
10+11: disable DNSsec
24-30: a test zone called “blaat.test”
31-37: a test zone called “miauw.test”

In the configuration file, we specified that the data for the zones can be found in the files /var/named/data/db.miauw.test and /var/named/data/db.blaat.test. We need to create those files and enter some zone-data:

/var/named/data/db.blaat.test:

@       IN SOA  ns.blaat.test admin.blaat.test. (
                                2014082201      ; serial
                                        1D      ; refresh
                                        1H      ; retry
                                        1W      ; expire
                                        3H )    ; minimum
@                       NS      ns.blaat.test.
ns                      IN      A               192.168.202.101
blaat.test.             IN      A               192.168.202.1
host1.blaat.test.       IN      A               192.168.202.10

/var/named/data/db.miauw.test

@       IN SOA  ns.miauw.test admin.miauw.test. (
                                2014082201      ; serial
                                        1D      ; refresh
                                        1H      ; retry
                                        1W      ; expire
                                        3H )    ; minimum
@                       NS      ns.miauw.test.
ns                      IN      A               192.168.202.101
miauw.test.             IN      A               192.168.202.1
host1.miauw.test.       IN      A               192.168.202.10

After performing the above steps, we can verify if the syntax in our configuration and zone files is correct before starting the service.

[jensd@master ~]$ sudo named-checkconf /etc/named.conf
[jensd@master ~]$ sudo named-checkzone blaat.test /var/named/data/db.blaat.test
/var/named/data/db.blaat.test:1: no TTL specified; using SOA MINTTL instead
zone blaat.test/IN: loaded serial 2014082201
OK
[jensd@master ~]$ sudo named-checkzone miauw.test /var/named/data/db.miauw.test
/var/named/data/db.miauw.test:1: no TTL specified; using SOA MINTTL instead
zone miauw.test/IN: loaded serial 2014082201
OK

In the above example, the syntax seems to be ok so we can start the master:

[jensd@master ~]$ sudo systemctl start named

When all goes well and we don’t receive any warning messages, we can test our freshly created master DNS-server:

[jensd@master ~]$ nslookup blaat.test localhost
Server: localhost
Address: 127.0.0.1#53
Name: blaat.test
Address: 192.168.202.1
[jensd@master ~]$ nslookup miauw.test localhost
Server: localhost
Address: 127.0.0.1#53
Name: miauw.test
Address: 192.168.202.1

Adding zone data

When information is changed for a certain zone, we can simply edit the relevant zone file and update the serial in that file. After checking the syntax, a reload makes sure that the server will reply with the newly provided information.

Edit /var/named/data/db.miauw.test to test updating. Don’t forget to update the serial on the second line:

@       IN SOA  ns.miauw.test admin.miauw.test. (
                                2014082202      ; serial
                                        1D      ; refresh
                                        1H      ; retry
                                        1W      ; expire
                                        3H )    ; minimum
@                       NS      ns.miauw.test.
ns                      IN      A               192.168.202.101
miauw.test.             IN      A               192.168.202.1
host1.miauw.test.       IN      A               192.168.202.10
host2.miauw.test.       IN      A               192.168.202.20

Now, check the syntax, reload the zone-information and test the newly added information:

[jensd@master ~]$ sudo vi /var/named/data/db.miauw.test
[jensd@master ~]$ sudo named-checkzone miauw.test /var/named/data/db.miauw.test
/var/named/data/db.miauw.test:1: no TTL specified; using SOA MINTTL instead
zone miauw.test/IN: loaded serial 2014082202
OK
[jensd@master ~]$ sudo systemctl reload named
[jensd@master ~]$ nslookup host2.miauw.test 192.168.202.101
Server: 192.168.202.101
Address: 192.168.202.101#53
Name: host2.miauw.test
Address: 192.168.202.20

Add a slave DNS to the master

Now that our master is fine, it’s time to add a slave DNS-server to the master.

The slave will be running on another machine. In our example, the slave’s IP address will be 192.168.202.102.

Like with the master, we need to install bind on the slave too:

[jensd@slave ~]$ sudo yum install bind bind-utils
 ...
 Complete!

After installation, we need to configure the slave’s /etc/named.conf to behave as a slave for the previously configured master.

/etc/named.conf on the slave:

options {
        listen-on port 53 { 127.0.0.1; 192.168.202.102;};
        #listen-on-v6 port 53 { ::1; };
        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     { any;};
        recursion no;
        dnssec-enable no;
        dnssec-validation no;
        dnssec-lookaside auto;
        bindkeys-file "/etc/named.iscdlv.key";
        managed-keys-directory "/var/named/dynamic";
        pid-file "/run/named/named.pid";
        session-keyfile "/run/named/session.key";
};
logging {
        channel default_debug {
                file "data/named.run";
                severity dynamic;
        };
};
zone "blaat.test" {
        type slave;
        file "/var/named/data/db.blaat.test";
        masters { 192.168.202.101; };
        check-names fail;
        allow-update { none; };
        allow-query { any; };
};
zone "miauw.test" {
        type slave;
        file "/var/named/data/db.miauw.test";
        masters { 192.168.202.101; };
        check-names fail;
        allow-update { none; };
        allow-query { any; };
};
zone "." IN {
        type hint;
        file "named.ca";
};
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";

As you can see, the configuration is quite similar in comparison with the master. The slave’s configuration also contains the same zones as on the master and they are configured as type slave. Zone files don’t need to be created since they should be replicated from the master.

In order to let the master notify the slave when a zone is updated and to allow the zone transfers, we need to add the following lines to the master’s /etc/named.conf in the options{}-section:

notify yes;
also-notify { 192.168.202.102; };
allow-transfer { 127.0.0.1; 192.168.202.102; };

The next step is to reload the configuration at the master, since we changed it and to start the slave:

[jensd@master ~]$ sudo named-checkconf /etc/named.conf
[jensd@master ~]$ sudo systemctl reload named
[jensd@slave ~]$ sudo systemctl start named

If all goes well, the slave should have replicated the zone information from the master and created it’s zone files on the location that was specified in /etc/named.conf on the slave.

[jensd@slave ~]$ sudo ls -l /var/named/data
 total 12
 -rw-r--r--. 1 named named 294 Aug 22 15:51 db.blaat.test
 -rw-r--r--. 1 named named 294 Aug 22 15:51 db.miauw.test
 -rw-r--r--. 1 named named 1074 Aug 22 15:51 named.run

By looking at /var/named/data/named.run on the slave, you can see that the data was transferred from the master.

[jensd@slave ~]$ sudo tail /var/named/data/named.run
 zone blaat.test/IN: Transfer started.
 transfer of 'blaat.test/IN' from 192.168.202.101#53: connected using 192.168.202.102#34291
 zone blaat.test/IN: transferred serial 2014082201
 transfer of 'blaat.test/IN' from 192.168.202.101#53: Transfer completed: 1 messages, 6 records, 191 bytes, 0.001 secs (191000 bytes/sec)
 zone blaat.test/IN: sending notifies (serial 2014082201)
 zone miauw.test/IN: Transfer started.
 transfer of 'miauw.test/IN' from 192.168.202.101#53: connected using 192.168.202.102#39349
 zone miauw.test/IN: transferred serial 2014082201
 transfer of 'miauw.test/IN' from 192.168.202.101#53: Transfer completed: 1 messages, 6 records, 191 bytes, 0.001 secs (191000 bytes/sec)
 zone miauw.test/IN: sending notifies (serial 2014082201)

To be sure that our slave DNS-server contains the correct data, we can do a query using that server:

[jensd@master ~]$ nslookup host1.blaat.test 192.168.202.101
 Server: 192.168.202.101
 Address: 192.168.202.101#53
 Name: host1.blaat.test
 Address: 192.168.202.10
 [jensd@master ~]$ nslookup host2.miauw.test 192.168.202.101
 Server: 192.168.202.101
 Address: 192.168.202.101#53
 Name: host2.miauw.test
 Address: 192.168.202.20

Test replication from the master to the slave

To be completely sure that data flows from the master to the slave and that the slave is notifies when a zone is updated on the master, we can simply update a zone and see if the data is available on the slave:

Update the zone in /var/named/data/db.blaat.test (don’t forget about the serial):

@       IN SOA  ns.blaat.test admin.blaat.test. (
                                2014082202      ; serial
                                        1D      ; refresh
                                        1H      ; retry
                                        1W      ; expire
                                        3H )    ; minimum
@                       NS      ns.blaat.test.
ns                      IN      A               192.168.202.101
blaat.test.             IN      A               192.168.202.1
host1.blaat.test.       IN      A               192.168.202.10
host2.blaat.test.       IN      A               192.168.202.20

Check the syntax and reload the zone configuration:

[jensd@master ~]$ sudo vi /var/named/data/db.blaat.test
[jensd@master ~]$ sudo named-checkzone blaat.test /var/named/data/db.blaat.test
/var/named/data/db.blaat.test:1: no TTL specified; using SOA MINTTL instead
zone blaat.test/IN: loaded serial 2014082202
OK
[jensd@master ~]$ sudo systemctl reload named

After reloading the zone on the master, check if the data is automatically transferred to the slave:

[jensd@slave ~]$ sudo tail /var/named/data/named.run
...
client 192.168.202.101#18736: received notify for zone 'blaat.test'
zone blaat.test/IN: Transfer started.
transfer of 'blaat.test/IN' from 192.168.202.101#53: connected using 192.168.202.102#50604
zone blaat.test/IN: transferred serial 2014082202
transfer of 'blaat.test/IN' from 192.168.202.101#53: Transfer completed: 1 messages, 7 records, 213 bytes, 0.001 secs (213000 bytes/sec)
zone blaat.test/IN: sending notifies (serial 2014082202)
[jensd@slave ~]$ nslookup host2.blaat.test 192.168.202.102
Server: 192.168.202.102
Address: 192.168.202.102#53
Name: host2.blaat.test
Address: 192.168.202.20

This should be all you need to create a simple master and slave DNS-server with Bind.

3 thoughts on “Basic master and slave DNS setup with Bind

  1. Pingback: Getting Started with the BIND DNS Server

Leave a Reply

Your email address will not be published. Required fields are marked *