How to set up own internal Linux DNS server

Posted by paul on 2014.04.05

Why set up a private DNS server

2014.04.05 Sat

After typing in 192.168.11.20 just one too many times to reach my dev web server in home LAN, I finally decided to set up my private DNS server at home. With my own private DNS server, I can reach my dev web server by simply using 'web01' or any other easy to remember hostname. I chose to use CentOS 6.5 to run DNS servers. I am running DNS servers inside of chroot environment.

Assumptions

  1. ns01.home.loc - Master DNS server 192.168.11.8
  2. ns02.home.loc - Slave DNS server 192.168.11.9
  3. test03 - DNS client tester 192.168.11.93
  4. web1 - local web server 192.168.11.50. Optional.
  5. All of the 3 machines are CentOS 6.5 64bit. The DNS client tester could be a Mac or a Windows computer but because of the way DNS client caching on Mac/Windows, I recommend using a Linux computer to use as as DNS tester.

Prep servers

  1. Install ns01 and ns02 and assign static IPs.
  2. Yum update the servers.
  3. Both CentOS servers should have normal IP configs that are currently in use in your LAN, including DNS server settings (/etc/resolv.conf).

ns01: /etc/hosts

  1. On ns01, edit /etc/hosts and append following line:
  2. 192.168.11.8 ns01.home.loc ns01
                        

ns02: /etc/hosts

  1. On ns02, edit /etc/hosts and append following line:
  2. 192.168.11.9 ns02.home.loc ns02
                                

Install Master DNS on ns01

Install rpms in ns01 and copy initial config files

  1. Install packages.
  2. [[email protected] ~]# yum install bind bind-chroot
                        
  3. Copy config files.
  4. [[email protected] ~]# cp /usr/share/doc/bind-9.8.2/sample/etc/named.rfc1912.zones /var/named/chroot/etc/named.conf
    [[email protected] ~]# cp -r /usr/share/doc/bind-9.8.2/sample/var/named/* /var/named/chroot/var/named/
    [[email protected] ~]# chown -R named:named /var/named/chroot/var/named/
                        

Edit /var/named/chroot/etc/named.conf and add following content.

  1. On ns01, edit /var/named/chroot/etc/named.conf so it looks like below. Basically you are APPENDing everything below ####### Append the below at bottom of named.conf ######## to the end of named.conf.
  2. // named.rfc1912.zones:
    //
    // Provided by Red Hat caching-nameserver package
    //
    // ISC BIND named zone configuration for zones recommended by
    // RFC 1912 section 4.1 : localhost TLDs and address zones
    // and http://www.ietf.org/internet-drafts/draft-ietf-dnsop-default-local-zones-02.txt
    // (c)2007 R W Franks
    //
    // See /usr/share/doc/bind*/sample/ for example named configuration files.
    //
    zone "localhost.localdomain" IN {
        type master;
        file "named.localhost";
        allow-update { none; };
    };
    zone "localhost" IN {
        type master;
        file "named.localhost";
        allow-update { none; };
    };
    zone "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" IN {
        type master;
        file "named.loopback";
        allow-update { none; };
    };
    zone "1.0.0.127.in-addr.arpa" IN {
        type master;
        file "named.loopback";
        allow-update { none; };
    };
    zone "0.in-addr.arpa" IN {
        type master;
        file "named.empty";
        allow-update { none; };
    };
    ####### Append the below at bottom of named.conf ########
    include "/etc/rndc.key";
    options {
            directory "/var/named"; // the path of the zone files
            forwarders {8.8.8.8; }; // in case of DNS query failure, the IP of the next DNS Server where the queries would be forwarded
        allow-query { localhost; any; };
        allow-query-cache    { localhost; any; };
        recursion yes;
        allow-recursion { 127.0.0.1; 192.168.11.0/24; };
    };
    
    // declaration of the forward zone
    zone "home.loc" IN {
            type master;
            file "data/home.loc-fz"; // file stored in /var/named/chroot/var/named/data/
            allow-update { none; };
            allow-transfer { 192.168.11.9; };  //IP of Slave DNS server
    };
    // declaration of reverse zone
    zone "11.168.192.in-addr.arpa" IN {
            type master;
            file "data/home.loc-rz"; // file stored in /var/named/chroot/var/named/data/
            allow-update { none; };
            allow-transfer { 192.168.11.9; };  //IP of Slave DNS server
    };
                    
  3. Details of named.conf
    1. line 41: Note 8.8.8.8 is Google's public DNS server. You could use IP of your WiFi Router (such as 192.168.11.1 in my case or usually 192.168.1.1). In case of DNS query failure, the IP of the next DNS Server where the queries would be forwarded to would be 8.8.8.8.
    2. line 53, 60: allows transferring DNS data with the Slave DNS server at 192.168.11.9.
    3. line 51: The Forward Zone file that will be created in next step.
    4. line 58: The Reverse Zone file that will be created in next step.

ns01: Forward Zone File for home.loc domain

  1. On ns01, create /var/named/chroot/var/named/data/home.loc-fz with following content.
  2. ; Comment: this is the forward zone file
    ; IMPORTANT every FQDN has a trailing dot '.'
    $TTL 1D
    ;Comment: FORMAT
    ;Comment: @      IN SOA  FQDN email (user.domain.tld) (
    @       IN SOA  ns01.home.loc. admin.home.loc. (
                2014040301  ; serial
                1D      ; refresh
                1H      ; retry
                1W      ; expire
                3H )    ; minimum
          IN NS           ns01.home.loc.
          IN A            192.168.11.8
    ns01   IN A            192.168.11.8
    ns02   IN A            192.168.11.9
    web01   IN A            192.168.11.50
                    
  3. Details of home.loc-fz
    1. line 7: This serial needs to increase every time a change is made in DNS records. I use YYYYMMDD + 2 digit numbers format. So if I made a change on 2014-Apr-03, the serial would be 2014040301 (2014 04 03 plus 01). If I made another change on the same day, the serial would go up to 2014040301 (2014 04 03 plus 02). If I made yet another change on 2014-apr-05, the serial would be 2014040501 (2014 04 05 plus 01). Using this numbering helps me see when a change was last made. Obviously you could use git to keep track but for a personal home system, this numbering works fine for me.
    2. line 12: The period after ns01.home.loc is required.

ns01: Reverse Zone File for home.loc domain

  1. On ns01, create /var/named/chroot/var/named/data/home.loc-rz with following content.
  2. ; Comment: this is the reverse zone file
    ; IMPORTANT every FQDN has a trailing dot '.'
    $TTL 1D
    ;FORMAT
    ;@      IN SOA  FQDN email (user.domain.tld) (
    @       IN SOA  ns01.home.loc. admin.home.loc. (
                2014040301      ; serial
                1D      ; refresh
                1H      ; retry
                1W      ; expire
                3H )    ; minimum
           NS      ns01.home.loc.
    8      IN PTR ns01.home.loc.
    9      IN PTR ns02.home.loc.
    50     IN PTR web01.home.loc.
    
  3. Details of home.loc-rz
    1. line 7: this serial needs to be in sync with the serial in the Forward zone file (home.loc-fz).
    2. line 13: Note 8 is the 4th part of the IP of ns01.home.loc, which in this case is 192.168.11.8
    3. line 14: Note 9 is the 4th part of the IP of ns02.home.loc, which in this case is 192.168.11.9
    4. line 15: Note 50 is the 4th part of the IP of web01.home.loc, which in this case is 192.168.11.50

ns01: Set permission on Forward and Reverse Zone files

[[email protected] ~]# chown -R named:named /var/named/chroot/var/named/data/

ns01: Start DNS service on Master DNS server

Set 'named' service to start upon reboot. In Linux, DNS is called 'named'. So you start/stop a service called 'named'. The first time you start named service, it will take a minute or two to start as it generates rndc.key.

[[email protected] ~]# chkconfig named on

[[email protected] ~]# service named start
Generating /etc/rndc.key:                    [  OK  ]
Starting named:                          [  OK  ]

ns01: Test Master DNS server with ns01

  1. On ns01.home.loc, edit /etc/resolv.conf as shown below. It will use 192.168.11.8 as the only DNS server for now. Do not include any other DNS server here. By making this change, ns01 will send DNS requests first to 192.168.11.8. If DNS server on ns01 cannot resolve a name for ns01, it will forward the request to 8.8.8.8, which is the Google's public DNS server. This was added in named.conf earlier. Of course when your DNS query is forwarded to 8.8.8.8, you will not get a valid response for ns01 or ns01.home.loc or any other internal server on your LAN. But 8.8.8.8 DNS server will happily and correctly resolve names such as google.com or ucla.edu.
  2. search home.loc
    nameserver 192.168.11.8
                    
  3. While on ns01, test looking up ns02.home.loc. Line 2 shows the DNS server that was used. Line 5-6 show that you got a result back, which is ns02 at 192.168.11.9.
  4. [[email protected] ~]# nslookup ns02.home.loc
    Server:     192.168.11.8
    Address:    192.168.11.8#53
    
    Name:   ns02.home.loc
    Address: 192.168.11.9
                    
  5. Test looking up ns02. Here just looking up 'ns02' works because /etc/resolv.conf includes search home.loc
  6. [[email protected] ~]# nslookup ns02
    Server:     192.168.11.8
    Address:    192.168.11.8#53
    
    Name:   ns02.home.loc
    Address: 192.168.11.9
                    
  7. Test looking up a nonexistent host and you will get following return. This shows ns01 is working correctly. It's just that noname.home.loc doesn't exist as far as the local DNS servers are concerned.
  8. [[email protected] ~]# nslookup noname.home.loc
    Server:     192.168.11.8
    Address:    192.168.11.8#53
    
    ** server can't find noname.home.loc: NXDOMAIN
                    

Install Slave DNS on ns02

Install rpms in ns02 and copy initial config files

  1. Install packages.
  2. [[email protected] ~]# yum install bind bind-chroot
                        
  3. Copy config files.
  4. [[email protected] ~]# cp /usr/share/doc/bind-9.8.2/sample/etc/named.rfc1912.zones /var/named/chroot/etc/named.conf
    [[email protected] ~]# cp -r /usr/share/doc/bind-9.8.2/sample/var/named/* /var/named/chroot/var/named/
    [[email protected] ~]# chown -R named:named /var/named/chroot/var/named/
            

ns02: Edit named.conf

  1. Edit /var/named/chroot/etc/named.conf so it looks as below. Basically APPEND what is below ####### Append the below given contents ######## to the file.
  2. // named.rfc1912.zones:
    //
    // Provided by Red Hat caching-nameserver package
    //
    // ISC BIND named zone configuration for zones recommended by
    // RFC 1912 section 4.1 : localhost TLDs and address zones
    // and http://www.ietf.org/internet-drafts/draft-ietf-dnsop-default-local-zones-02.txt
    // (c)2007 R W Franks
    //
    // See /usr/share/doc/bind*/sample/ for example named configuration files.
    //
    zone "localhost.localdomain" IN {
    	type master;
    	file "named.localhost";
    	allow-update { none; };
    };
    zone "localhost" IN {
    	type master;
    	file "named.localhost";
    	allow-update { none; };
    };
    zone "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" IN {
    	type master;
    	file "named.loopback";
    	allow-update { none; };
    };
    zone "1.0.0.127.in-addr.arpa" IN {
    	type master;
    	file "named.loopback";
    	allow-update { none; };
    };
    zone "0.in-addr.arpa" IN {
    	type master;
    	file "named.empty";
    	allow-update { none; };
    };
    ####### Append the below given contents ########
    options {
            directory "/var/named"; // Zone files path i.e /var/named/chroot/var/named .
            forwarders {8.8.8.8; }; // In case the DNS query fails,
    //request will go to next available DNS server
    dump-file "named_dump.db";
    };
    //Forward zone section for example.com
    zone "home.loc" IN {
            type slave; // Declaring as DNS Slave Server
            file "data/home.loc-fz"; // stored in /var/named/chroot/var/named/data/
          allow-transfer {192.168.11.8; }; //IP of Master DNS server
          masters {192.168.11.8; }; //IP of Master DNS server
    };
    // Reverse Zone Section for example.com
    zone "11.168.192.in-addr.arpa" IN {    //Part of reversed IP of Master DNS server
            type slave; // Declaring as DNS Slave Server
            file "data/home.loc-rz"; // stored in /var/named/chroot/var/named/data/
          allow-transfer {192.168.11.8; };  //IP of Master DNS server
          masters {192.168.11.8; };   //IP of Master DNS server
    };
            
    1. lines 12, 13, 19, 20 all should have correct IP of the Master DNS server, which in this case is 192.168.11.8.
    2. line 52: You get "11.168.192.in-addr.arpa" because of the IP 192.168.11.0. If you were to be working with IP 192.168.10.0 in your home LAN, the line should change to "10.168.192.in-addr.arpa".

Start DNS service on ns02, Slave DNS server

  1. Set 'named' service to start upon reboot. Start the service for first time. The first time you start named service, it will take a minute or two to start as it generates rndc.key.
  2. [[email protected] ~]# chkconfig named on
    
    [[email protected] ~]# service named start
    Generating /etc/rndc.key:                                  [  OK  ]
    Starting named:                                            [  OK  ]
                

Using test03.home.loc, test DNS with both Master and Slave DNS services running

  1. On both ns01 and ns02, edit /etc/resolv.conf as shown below. Note 192.168.11.8 is your Master DNS server. And 192.168.11.9 is the Slave DNS server.
  2. search home.loc
    nameserver 192.168.11.8
    nameserver 192.168.11.9
                
  3. Next log into test03 (192.168.11.93) and edit /etc/resolv.conf to look as below, same as the 2 DNS servers
  4. search home.loc
    nameserver 192.168.11.8
    nameserver 192.168.11.9
                
  5. On test03, test looking up ns02.home.loc. Line 2 shows the DNS server being used.
  6. [[email protected] ~]# nslookup ns02
    Server:     192.168.11.8
    Address:    192.168.11.8#53
    
    Name:   ns02.home.loc
    Address: 192.168.11.9
                

Using test03.home.loc, test DNS with just Slave DNS service running

  1. Stop dns (aka named) service on Master DNS
  2. [[email protected] ~]# service named stop
    Stopping named: .                                          [  OK  ]
                
  3. On test03, try nslookup commmand. The DNS query is answered successfully but the Slave DNS server is used, not the Master DNS server, which is expected. Line 2 shows that the DNS server on 192.168.11.9 was used, which is the Slave DNS server.
  4. [[email protected] ~]# nslookup ns01
    Server:     192.168.11.9
    Address:    192.168.11.9#53
    
    Name:   ns01.home.loc
    Address: 192.168.11.8
                
  5. After finishing the test, make sure to start dns service on ns01.

Add new DNS record

So you've worked with 3 hosts in the DNS record: ns01, ns02, and web01. Let's try adding a new DNS entry for a new host. Let's assume it's a Synology NAS device at 192.168.11.30 and it will be called nas.home.loc. Following changes will be made.

  1. Two files on ns01 will be edited.
  2. 1st file to edit: /var/named/chroot/var/named/data/home.loc-fz
  3. 2nd file to edit: /var/named/chroot/var/named/data/home.loc-rz
  4. Both file will have 1 line added.
  5. Both file will have 1 line modified.
  6. Restart 'named' service on ns01.

Note all changes are made only on ns01 (Master Slave server). You do not need to touch ns02 as the changes made on ns01 will be transferred over to ns02 automatically.

Edit home.loc-fz

On ns01, edit /var/named/chroot/var/named/data/home.loc-fz

  1. Before
  2. ; Comment: this is the forward zone file
    ; IMPORTANT every FQDN has a trailing dot '.'
    $TTL 1D
    ;Comment: FORMAT
    ;Comment: @      IN SOA  FQDN email (user.domain.tld) (
    @       IN SOA  ns01.home.loc. admin.home.loc. (
            2014040301      ; serial
            1D      ; refresh
            1H      ; retry
            1W      ; expire
            3H )    ; minimum
                    IN NS           ns01.home.loc.
                    IN A            192.168.11.8
    ns01            IN A            192.168.11.8
    ns02            IN A            192.168.11.9
    web01           IN A            192.168.11.50
    
  3. After
  4. ; Comment: this is the forward zone file
    ; IMPORTANT every FQDN has a trailing dot '.'
    $TTL 1D
    ;Comment: FORMAT
    ;Comment: @      IN SOA  FQDN email (user.domain.tld) (
    @       IN SOA  ns01.home.loc. admin.home.loc. (
            2014040302      ; serial
            1D      ; refresh
            1H      ; retry
            1W      ; expire
            3H )    ; minimum
                    IN NS           ns01.home.loc.
                    IN A            192.168.11.8
    ns01            IN A            192.168.11.8
    ns02            IN A            192.168.11.9
    nas             IN A            192.168.11.30
    web01           IN A            192.168.11.50
    
  5. What changed in new file
    1. line 7: 2014040301 changed to 2014040302
    2. line 16: nas with IP 192.168.11.30 was added.

Edit home.loc-rz

On ns01, edit /var/named/chroot/var/named/data/home.loc-rz

  1. Before
  2. ; Comment: this is the reverse zone file
    ; IMPORTANT every FQDN has a trailing dot '.'
    $TTL 1D
    ;FORMAT
    ;@      IN SOA  FQDN email (user.domain.tld) (
    @       IN SOA  ns01.home.loc. admin.home.loc. (
            2014040301      ; serial
            1D      ; refresh
            1H      ; retry
            1W      ; expire
            3H )    ; minimum
           NS      ns01.home.loc.
    8      IN PTR ns01.home.loc.
    9      IN PTR ns02.home.loc.
    50     IN PTR web01.home.loc.
    
  3. After
  4. ; Comment: this is the reverse zone file
    ; IMPORTANT every FQDN has a trailing dot '.'
    $TTL 1D
    ;FORMAT
    ;@      IN SOA  FQDN email (user.domain.tld) (
    @       IN SOA  ns01.home.loc. admin.home.loc. (
            2014040302      ; serial
            1D      ; refresh
            1H      ; retry
            1W      ; expire
            3H )    ; minimum
           NS      ns01.home.loc.
    8      IN PTR ns01.home.loc.
    9      IN PTR ns02.home.loc.
    30     IN PTR nas.home.loc.
    50     IN PTR web01.home.loc.
    
  5. What changed in new file
    1. line 7: 2014040301 changed to 2014040302
    2. line 15: line added for nas (which would be nas.home.loc).

Restart DNS service on ns01

  1. Restart named service on ns01 for the new changes to take effect.
  2. Test nslookup for 'nas' on test03 and you will see that it tests successfully.
  3. [[email protected] ~]# nslookup nas
    Server:     192.168.11.8
    Address:    192.168.11.8#53
    
    Name:   nas.home.loc
    Address: 192.168.11.30
    

Rollout

Rolling out the new DNS servers basically requires changing DNS server settings on all your computers in your home. If you have computers with static IPs, you would need to change DNS server setting in all of them. For computers that use a DHCP server (usually running on your home WiFi router) for IP assignment, you will just need to change the DNS setting that the DHCP server gives out. And then release/renew IP or simply reboot. In my case, my WiFi router had a bug that assigned only 192.168.11.1 as the DNS server. I found a workaround for this which is explained below.

Backup

Before you roll out the new DNS servers into production, make sure you have a script to backup /var/named/chroot/ from both Master and Slave DNS servers off to another server/storage regularly. You definitely want to keep copies of /var/named/chroot/etc/named.conf from both Master and Slave. And you want to keep backup of /var/named/chroot/var/named/data/ from Master DNS. With those files, restoring/reinstalling replacement DNS servers should be a cinch.

DNS client settings on static IP computers

Linux desktop

On all the Linux machines like CentOS in your LAN, you'd replace content of /etc/resolv.conf with following lines to start using the new DNS servers. The DNS servers will be tried in sequence from the top. By adding 8.8.8.8 (public DNS server by Google) at bottom, you will at least be able to resolve names of server out in the internet even when both of your internal DNS servers are down. This will allow you to surf internet seamlessly even if you can't reach servers in your LAN by hostname.

search home.loc
nameserver 192.168.11.8
nameserver 192.168.11.9
nameserver 8.8.8.8

Mac and Windows

Change IP settings on all Mac/Windows computers that are NOT portable to use static IP and use the new DNS servers. Mac and Windows computers have different ways of including domain (home.loc in this tutorial) but it's easy to figure out.

DNS client settings on DHCP clients

This part turned out to a bit of challenge as I wanted to continue using the DHCP server on my home WiFi Router. You probably have at least one or two laptops in your home/office LAN that get its IP from a mini DHCP server running on your home Wireless Router. Because of limited functionality in the DHCP server on some home WiFi router, you may have issues with configuring the stripped down DHCP server to send out necessary, custom IP settings. On my WiFi router, I could not configure the DHCP server to send out new DNS server IPs (like replacing 192.168.11.1 with 192.168.11.8 & 192.168.11.9) to DHCP clients. I could change it but would not stick. And I could not change the domain to search (home.loc in this tutorial).

I thought about setting up own full fledged DHCP server on ns01 and disable the DHCP server on the WiFi Router. But the convenience of running a DHCP server on a WiFi router with so few moving parts was something I wanted to keep. The stripped down DHCP server on the WiFi router will keep running even if my home server suddenly died. To get around this limitation and still force my laptops to use the new DNS servers, I did the following.

Mac laptop

Following config allows my mac laptop to use the private DNS servers and reach local and internet machines using hostname. Most importantly, following config allows my mac laptop to seamlessly switch to other WiFi networks without having to change any IP setting manually.

  1. On my 10.9.2 Macbook, in System Preferences > Network, I created a new location 'HomeWifi' using Wi-Fi.
  2. I then went into Advanced
  3. Under DNS > DNS Servers, I added following 3 manually.
    1. 192.168.11.8
    2. 192.168.11.9
    3. 8.8.8.8
  4. Under DNS > Search Domains, I added following
    1. home.loc
  5. Under TCP/IP, I keep 'Using DHCP' for 'Configure IPv4.

Windows laptop

Don't have Win machine handy to test. But if someone requests it in the comment section below I will try to finish this.