24/7/365 Support

Setting up an authoritative-only DNS server on CentOS

In this process, we will learn how to create an authoritative-only DNS server, which can give answers to queries about domains under their control themselves instead of redirecting the query to other DNS servers (such as our caching-only DNS server from the previous process). We will create a DNS server to resolve all our own hostnames and services in our own private local network.
 
As said before, while Unbound should be your first choice when needing a caching-only DNS server as it is the most secure DNS server solution available, it has only limited authoritative capabilities which often is not enough for professional DNS server usage. Here, instead of name lookup of our local servers, we will use the popular authoritative BIND DNS server package and configure a new DNS zone to provide highly customizable name resolution. Technically speaking, we will be writing both a forward and reverse zone file for our domain. Zone files are text files that contain the actual domain name to IP address mappings or the other way around, that is, IP address mappings to domain name mappings. While most queries to any DNS server will be the translation of names to IP addresses, the reverse part is also important to set up if you need the correct domain name for any given IP address. We will configure BIND to be authoritative-only, which means that the server will only answer queries it is authoritative for (has the matching records in its zones), so if the DNS server cannot resolve a requested domain, it will stop the request and not contact other DNS servers using recursive requests to fetch and construct the correct answer.

To Start With: What Do You Need?

To complete this process, you will require a working installation of the CentOS 7 operating system with root privileges, a static IP address, and a console-based text editor of your choice. An Internet connection will be required to download additional packages. In this example, our DNS server runs in the private network with the network address 192.168.1.0/24. Our DNS server should manage a local private domain we decide to be centos7.home (in the form domain.toplevel-domain). The IP address of the new DNS server will be 192.168.1.7 and should get the hostname ns1, leading to the Fully Qualified Domain Name (FQDN) ns1.centos7.home. (Refer to the Setting your hostname and resolving the network process in Chapter division 2 , Configuring the System to learn more about FQDNs). Our configured zone will have an administrative e-mail address with the name admin@centos7.home, and for simplicity, all the other computers in this network will get hostnames such as client1, client2, client3, and so on. We will also have some mail, web, and FTP servers in our own network, each running on separate dedicated servers. We will be using the port 8053 for our BIND service as we already have Unbound running on the same server using the default DNS port 53.

The Process

For security reasons, we will allow BIND to resolve internal LAN names only (authoritative-only) and only allow localhost to make DNS queries; no other clients in our network can connect to it:
  1. To begin with, log in as root on your Unbound DNS server and install the required BIND package and enable the DNS server on boot:
    yum install bind && systemctl enable named
  2. The actual name of the DNS server in the BIND package is called named, so let’s open its main configuration file to make some adjustments after creating a backup copy of it first:
    cp /etc/named.conf /etc/named.conf.BAK; vi /etc/named.conf
  3. First, find the line listen-on port 53 { 127.0.0.1; }; and then change the port number to the custom port 8053, so it reads as follows:
    listen-on port 8053 { 127.0.0.1; };
  4. Next, find the line listen-on-v6 port 53 { ::1; } and change it to:
    listen-on-v6 port 8053 { none; };
  5. Next, since we are configuring an authoritative-only server, we will disable contacting other remote DNS servers, find the line that reads recursion yes; and change it to:
    recursion no;
  6. Save and close the file, and then validate the syntax of our config changes (no output means no errors!):
    named-checkconf
  7. Now tell SELinux about the changed named DNS port (this needs package policycoreutils-python):
    semanage port -a -t dns_port_t -p tcp 8053
  8. Now type the following command in order to create your forward zone file. Name the file after the domain whose resource records it will contain:
    vi /var/named/<domain>.<top-level domain>.db
  9. In our example, for our centos7.home domain, this will be:
    vi /var/named/centos7.home.db
  10. Now simply add the following lines (be careful not to forget typing the tailing dots in the domain names). We will start with the Start of Authority (SOA) block:
    $TTL 3h
    @ IN SOA ns1.centos7.home. admin.centos7.home.(
    2015082400 ; Serial yyyymmddnn
    3h ; Refresh After 3 hours
    1h ; Retry Retry after 1 hour
    1w ; Expire after 1 week
    1h) ; Minimum negative caching
  11. Afterwards, add the rest of the file’s content:
    ; add your name servers here for your domain
              IN            NS           ns1.centos7.home.
    ; add your mail server here for the domain
              IN            MX     10    mailhost.centos7.home.
    ; now follows the actual domain name to IP
    ; address mappings:

    ; first add all referenced hostnames from above
    ns1              IN             A             192.168.1.7
    mailhost         IN             A             192.168.1.8
    ;  add all accessible domain to ip mappings here
    router          IN              A              192.168.1.0
    www             IN              A              192.168.1.9
    ftp             IN              A              192.168.1.10
    ;  add all the private clients on the Lan here
    client1         IN              A              192.168.1.11
    client2         IN              A              192.168.1.12
    client3         IN              A              192.168.1.13
    ;   finally we can define some aliases for
    ;   existing domain name mappings
    webserver IN CNAME www
    johnny IN CNAME client2

  12. When you have finished, simply save and close the file before proceeding to create the reverse zone file for our private subnetwork used by our domain (the C-Class are the first three numbers (octets) which are separated by dots: XXX.XXX.XXX. For example, for the 192.168.1.0/24 subnet the C-Class is 192.168.1:
    vi /var/named/db.<C-Class of our search IP in reverse order>
  13. In our example, a reverse zone file resolving our centos7.home's 192.168.1 C-Class subnet will be:
    vi /var/named/db.1.168.192
  14. First put in the exact same SOA as in step 10, and then append the following content to the end of the file:
    ;    add your name servers for your domain
                           IN          NS          ns1.centos7.home.
    ; here add the actual IP octet to
    ; subdomain mappings:
    7           IN          PTR       ns1.centos7.home.
    8           IN          PTR       mailhost.centos7.home.
    9           IN          PTR       www.centos7.home.
    10          IN          PTR       ftp.centos7.home.
    11          IN          PTR       client1.centos7.home.
    12          IN          PTR       client2.centos7.home.
    13          IN          PTR       client3.centos7.home.
  15. Save and close the file, and then add our new zone pair to the named configuration. To do this, open named.conf again:
    vi /etc/named.conf
  16. Now locate the line including "/etc/named.rfc1912.zones";. Immediately following this line, create a space for your work and add the appropriate zone statement to enable your reverse zone, as follows (substitute XXX.XXX.XXX with the reversed C-Class of your reverse zone file name, in our example 1.168.192):
    zone "XXX.XXX.XXX.in-addr.arpa." IN {
    type master;
    file "/var/named/db.XXX.XXX.XXX";
    update-policy local;
    };
  17.  Having done this, you can now proceed to add a zone statement for your forward zone right afterwards, as follows (replacing <domain>.<top-level domain>.db with your forward zone file name, in our example centos7.home):

    zone "<domain>.<top-level domain>." IN {
    type master;
    file "/var/named/<domain>.<top-level domain>.db";
    update-policy local;
    };

  18. When you have finished, simply save and close the file, and then restart the bind service using:
    named-checkconf && systemctl restart named

How Does It Work?

All DNS servers are configured to perform caching functions, but where a caching-only server is restricted in its ability to answer queries from remote DNS servers only, an authoritative nameserver is a DNS server that maintains the master zone for a particular record.
 
So what have we learned from this experience?
 
The purpose of this process was to setup an authoritative-only BIND DNS server and provide a new zone for it. A DNS zone defines all the available resources (hostnames and services) under a single domain. Any DNS zone should always consist of both a forward and reverse zone file. To understand zone configurations, we need to discuss DNS hierarchy first. For example, take a DNS domain from the example in this process client1.centos7.home. Every computer in our private network has a hostname (for example, client1 or www) and is a member of a domain. A domain consists of the Second-level Domain (SLD) (for example, centos7) and a Top-level Domain name (TLD) (for example, home, org, com, and so on). On top of that TLD is the root domain (written . dot) which often is neglected when working with other programs or configurations. However, when working or defining FQDN in zone configurations, it is very important to never forget to add this dot . after the TLD. For example, a DNS domain for our client1 computer would be client1.centos7.home., whereas an FQDN for the /etc/hosts file is often written in the format client1.centos7.home (technically this is incorrect but most of the time sufficient). The root domain is very important because it contains the root DNS servers which will be queried first if an authoritative DNS server cannot find an existing entry for a requested domain in its own records (zones) or cache. But we have DNS servers in all the other domain hierarchies as well and this is how a DNS server makes its recursive requests. A root DNS server, as any other DNS server, resolves all its subdomains (defined in its zone files) which are the TLDs. These TLDs themselves can resolve all the SLDs (also defined in their zone files). The second-level domains resolve all their hostnames (which are special subdomains as they refer to individual computer or services on your network). So any DNS request traverses through the different DNS server hierarchies from the root DNS over the TLD DNS to the SLD DNS server. The root and the TLD DNS servers cannot fully resolve full domain DNS queries such as www.centos7.home and instead will resolve the correct address of the next DNS hierarchy. This system ensures that the root DNS will always find the correct TLD DNS server address and the TLD DNS server will always send the request to the right SLD DNS which has the correct zone file and is finally able to answer the requested DNS query.
 
So what did we learn from this experience?
 
As we have learned, a zone file is a simple text file that consists of directives and resource records and can look quite complicated as it contains a lot of two-letter abbreviations. Remember, you need to set up a zone file pair (forward and reverse) on a base domain level (for example, centos7.home) for all the hostnames and services running under it (for example, www, host1, api, and so on). After installing the named DNS server (which is part of the Berkeley Internet Name Domain (BIND) package), we made a copy of the original main configuration file and changed the default listening port from 53 to 8053 (as unbound is already listening on port 53) but kept it listening to localhost only, and disabled IPv6 to keep compatibility with the other major DNS servers (as IPv6 support is still limited on the Internet). Also, here we disabled recursion because our BIND DNS server had to be authoritative-only, which means that it is not allowed to forward DNS requests to other remote DNS servers when it could not resolve the query from its own zone records.
 
Then we began creating and customizing our own forward DNS zone file with the filename convention /var/named/<domain>.<top-level domain>.db. This file is opened with the $TTL control statement, which stands for Time to Live and which provides other nameservers with a time value that determines how long they can cache the records from this zone. This directive, as many others, is defined using seconds as the default time unit, but you can also use other units using BIND specific short forms to indicate minutes (m), hours (h), days (d), and weeks (w), as we did in our example (3h). Following this, we then provided a Start of Authority (SOA) record. This record contains specific information about the zone as a whole. This begins with the zone name (@), a specification of the zone class (IN), the FQDN of this nameserver in the format hostname.domain.TLD., and an e-mail address of the zone administrator. This latter value is typically in the form hostmaster.hostname.domain.TLD. and it is formed by replacing the typical @ symbol with a dot (.). Having done this, it was then a matter of opening the brackets to assign the zone’s serial number, refresh value, retry value, expire value, and negative caching timeto-live value. These directives can be summarized as follows:
  • The serial-number value is a numeric value, typically taking the form of the date in reverse (YYYYMMDD) with an additional value (VV), which is incremented every time the zone file is modified or updated, in order to indicate that it is time for the named service to reload the zone. The value VV typically starts at 00, and the next time you modify this file, simply increment it to 01, 02, 03, and so on.
  • The time-to-refresh value determines how frequently the secondary or slave nameservers will ask the primary nameserver if any changes have been made to the zone.
  • The time-to-retry value determines how frequently the secondary or slave nameservers should check the primary server after the serial number has failed. If a failure has occurred during the time frame specified by the time-to-expire value elapses, the secondary nameservers will stop responding as an authority for requests.
  • The minimum-TTL value determines how long the other nameservers can cache negative responses.
Having completed this section and having closed the corresponding bracket, we then proceeded to add the authoritative nameserver information (NS) with the IN NS <FQDN of the nameserver> definition. Typically speaking, you will have at least two, if not three, nameservers (put each nameserver’s FQDN in a new IN NS line). However, it is possible to set only one nameserver, which is particularly useful if you are running the server in an office or a home environment and would like to enjoy the benefit of local name resolution, such as .home, .lan, or .dev. The next stage then required us to include a reference for the Mail eXchanger (MX) records in order for us to specify a mail server for the zone. The format is IN MX <priority> <FQDN of your mailserver>. The priority becomes important if you define more than one mail server (each in its separate IN MX line)—the lower the number, the higher the priority. In this respect, a secondary mail server should have a higher value.
 
Note
In the SOA, NS and MX lines we already referenced hostnames which aren’t defined as an IP mapping yet (A record). We could do this because the zone file is not processed sequentially. But do not forget to create corresponding A lines for each hostname later.
 
Depending on your needs, you may also intend to use your name server as your mail server (then you would write instead MX 10 ns1.centos7.home.), although you may have another server dedicated to that role as shown in the example.
 
Following this, it was then a matter of creating the appropriate A records (A for address) and assigning the appropriate IP address to the values shown. This is the heart of any domain name resolution requests to the server. An A record is used for linking an FQDN to an IP address, but much of the preceding settings will be based on your exact needs. Here you can define all the local host names you want to map in your network. As we have already used and referenced some domain names before in the zone file such as the nameserver or mailserver we would begin with these. Afterwards, we defined all the hostnames to IP address mappings for all public available and afterwards our internal clients. Remember that when using the A records you can have multiple mappings of the same IP address to different hostnames. For example, if you do not have dedicated servers for every service in your network but rather one server running all your DNS, mail, web, and ftp services, you can write the following lines instead:
 
ns1             IN A 192.168.1.7
mailhost    IN A 192.168.1.7
www IN A 192.168.1.7
ftp             IN A 192.168.1.7
 
You can also use a canonical name (CNAME) record for this task, which is used to assign an alias to an existing A record. Arguably, the CNAME value makes your DNS data easier to manage by pointing back to an A record. So if you ever consider the need to change the IP address of the A record, all your CNAME records pointed to that record automatically. However, and as this process has tried to show, the alternative solution is to have multiple A records, which implies the need for multiple updates in order to change the IP address.
 
At this stage of the process, we then turned our attention towards the reverse DNS zone. As with the forward zone file, the reverse zone files also have a special naming convention /var/named/db.<C-Class of our search IP in reverse order>. Naming your reverse zone file like db.1.168.192 can look strange first but makes sense when you look at how reverse lookup works. It starts from the highest node (in our example 192, which corresponds to the root domain in the forward zone file) and traverses its way down from it. As you see, the content we put in this file has some similarities between the directives and the resources used in the forward zone file. However, it is important to remember that reverse DNS is wholly separate and distinct from forward DNS.
 
The reverse DNS zone is designed to assist in the conversion of an IP address to a domain name. This can be done by using the Pointer Resource Record (PTR) which assigns unique IP addresses to one or more host names. For this reason, you must ensure that a unique PTR record exists for every A record. Every reverse zone file collects IP to hostname translations for a complete Class C address range (the first three dotted numbers, for example, 192.168.1). The last octets of such an IP range are all the hostnames which can be defined within such a file. Remember, the IP address value for the first column in a PTR record should only show this last octet. For example, the line 9 IN PTR www.centos7.home. in the reverse zone file db.1.168.192 will be able to resolve any reverse IP address requests of 192.168.1.9 to the domain value www.centos7.home.
 
Having created our forward and reverse zone files in this process, we then completed the configuration of the named service by adding our new zones to our BIND server in order to start our own domain name service resolving local domain names of our network. In these new appended forward and reverse zone definition blocks, we defined that we are the master zone holder and also specified update-policy local; because this is needed if we want to use the nsupdate command to update our zones dynamically from the localhost (see later). You may add unlimited zone pairs, but remember that each forward or reverse zone definition must be given a single zone entry in curly brackets.
 
In summary, we can say that forward and reverse zone files are defined on a single base domain name basis, one base domain gets one forward zone file. For reverse zone files, it’s a bit different because we are working with IP addresses. We create one zone file based on the Class C address range of the network address of our domain and here the last octet is called the hostname, for which we define our mappings in such a specific file.
 
BIND is a big subject and there is a lot more to learn as this process has only served to introduce you to the subject. In most cases, you may even find that your initial learning period will become known as a process of trial and error, but it will improve. Remember, practice makes perfect and if you do create additional forward zones, always reference them in the reverse zone file.
 

There's more…

 
Having created and added your zones to your BIND server, you are now able to test your configuration. To do this, you can use the host, dig or nslookup command to resolve internal hostnames from localhost only. For example, for testing forward DNS resolution we can use the dig command by specifying that our DNS server is running on localhost with port 8053: dig -p 8053 @127.0.0.1 client2.centos7.home. This should finish DNS lookup successfully and return the following line (output is truncated):
;; ANSWER SECTION:
client2.centos7.home. 10800 IN A 192.168.1.12

 
For reverse lookup, you will use an IP address instead (in this instance, the IP address used should correspond to a domain for which you have configured reverse DNS): nslookup port=8053 192.168.1.12 127.0.0.1. As we have configured BIND as an authoritative-only DNS server, any DNS request which is outside the local records of our zone should not be able to get fully resolved. To test this use dig -p 8053 @127.0.0.1 www.google.com which should return the status REFUSED and WARNING: recursion requested but not available message.
 
For security reasons, we restricted our BIND server to localhost only and did not allow it to connect to other DNS servers. Therefore you cannot use it as your only DNS solution for your private network. Instead, in the next process, we will learn how to combine Unbound with BIND to create an integrated and very secure all-in-one DNS server solution. But if you don’t want to do this and use BIND as your single and full authoritative DNS server solution (which is not recommended on CentOS 7 anymore), you can do this by disabling or uninstalling Unbound, restoring the original named.conf.BAK configuration file, and enabling the following directives in the BIND configuration file: allow-query {localhost;192.168.1.0/24;}; (which enables the complete 192.168.1.0/24 network to make DNS requests), listen-on port 53 {any;}; (listen for requests on any network), listen-on-v6 port 8053 { none; }; (for disabling IPv6). If you want BIND to be forwarding everything, which it is not authoritative for, instead of using recursion to find out the answer, add the following directives as well (in this example we use the official Google DNS servers for any forwarding requests, but you can change this to fit your needs): forwarders { 8.8.8.8;};forward only;. Then restart the bind service.
 

Help Category:

What Our Clients Say