Set Up a Local Authoritative DNS Server on Linux

CompTIABeginner
Practice Now

Introduction

In this lab, you will learn how to set up and configure a local authoritative DNS server on a Linux system using bind9. You will gain hands-on experience with one of the most widely used DNS software packages, transforming a standard Linux machine into a server capable of resolving custom domain names for a local network.

The process begins with installing the necessary packages and configuring global server options, such as setting up forwarders. You will then define both a forward lookup zone to resolve a domain name to an IP address and a reverse lookup zone for the corresponding IP range. To conclude the lab, you will validate the bind9 configuration files for correct syntax and use the dig utility to test your new DNS server, ensuring it correctly resolves queries for your local domain.

Install bind9 and Configure Global Options

In this step, you will begin by setting up the core components for your DNS server. This involves installing bind9, which is one of the most widely used DNS server software packages on Linux. You will also configure its global options, which control the server's general behavior, such as how it handles requests for domains it is not authoritative for.

First, it's a standard practice to update your system's package list to ensure you are getting the latest available versions of software.

Execute the following command in your terminal:

sudo apt-get update

Next, install the bind9 package, which contains the DNS server daemon, and bind9-utils, which provides helpful command-line tools like dig and named-checkconf that you will use later for testing and validation.

sudo apt-get install bind9 bind9-utils

You will be prompted to confirm the installation. Type Y and press Enter. The output will look similar to this:

Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  bind9-libs bind9-doc dns-root-data
...
After this operation, XX.X MB of additional disk space will be used.
Do you want to continue? [Y/n] Y
...
Setting up bind9 (1:9.18.X-XubuntuX.X) ...

With bind9 installed, the next task is to configure its global behavior. The main configuration is split into several files, all located in the /etc/bind/ directory. For global options, you will edit the named.conf.options file.

Open the file using the nano editor:

sudo nano /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;
        // };

        //========================================================================
        // If BIND logs error messages about the root key being expired,
        // you will need to update your keys.  See https://www.isc.org/bind-keys
        //========================================================================
        dnssec-validation auto;

        listen-on-v6 { any; };
};

Inside this file, you will see an options { ... }; block. You need to ensure this block contains directives for forwarding, query permissions, and listening interfaces. Forwarding allows your DNS server to resolve external domains (like google.com) by asking other public DNS servers.

Modify the options block to match the following. You can add the lines that are missing or uncomment and change the ones that already exist.

Tips: You can clean the file content by pressing Ctrl+K, then paste the following content.

options {
        directory "/var/cache/bind";

        // If there is not a line with forwarders, add the following section.
        // If there is one, ensure it's not commented out and has valid IPs.
        forwarders {
                8.8.8.8;
                8.8.4.4;
        };

        // Add or uncomment this line to allow queries from any host
        allow-query { any; };
        recursion yes;

        listen-on { any; }; // Listen on all interfaces
        listen-on-v6 { any; }; // Listen on all IPv6 interfaces

        dnssec-validation auto;
        auth-nxdomain no;    ## conform to RFC1035
};

Once you have finished editing, save the file by pressing Ctrl+O, then press Enter to confirm the filename. Exit the nano editor by pressing Ctrl+X. You have now successfully installed bind9 and set its initial configuration.

Define and Create a Forward Lookup Zone for mylocaldomain.net

In this step, you will configure your bind9 server to be authoritative for a custom domain. This is achieved by creating a forward lookup zone, which is a database that maps human-readable hostnames (like webserver.mylocaldomain.net) to their corresponding IP addresses. You will first define the zone in the bind9 configuration and then create the actual zone file containing the DNS records.

First, you need to tell bind9 about your new zone. Local zone configurations are kept in /etc/bind/named.conf.local to separate them from the default package configuration. This file will be fully configured in the next step.

Now that the zone is defined, you must create the corresponding zone file. This file contains the actual DNS records.

First, copy a template file to ensure the new file has the correct permissions and ownership:

sudo cp /etc/bind/db.local /etc/bind/db.mylocaldomain.net

Next, instead of manually editing the file, you can run a single command block that will:

  1. Get your VM's IP address.
  2. Generate a dynamic serial number using the current date.
  3. Write the complete and correct zone file content into /etc/bind/db.mylocaldomain.net.

First, capture your VM's IP address into a variable that will be used in the zone file:

VM_IP=$(ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')

This command extracts the IPv4 address from your primary network interface (eth0) using a regular expression. The IP address will be automatically inserted into your DNS records.

Now, create the zone file with the appropriate DNS records. This single command will generate the complete zone file:

sudo bash -c "cat > /etc/bind/db.mylocaldomain.net" << EOF
\$TTL    604800
@       IN      SOA     ns1.mylocaldomain.net. admin.mylocaldomain.net. (
                     $(date +%Y%m%d)01 ; Serial (YYYYMMDDNN)
                             604800     ; Refresh
                              86400     ; Retry
                            2419200     ; Expire
                             604800 )   ; Negative Cache TTL
;
@       IN      NS      ns1.mylocaldomain.net.
ns1     IN      A       ${VM_IP}
webserver IN    A       ${VM_IP}
fileserver IN   A       ${VM_IP}
www     IN      CNAME   webserver
EOF

This command automatically populates the zone file with the correct records, using your VM's specific IP address. You can verify the content with cat /etc/bind/db.mylocaldomain.net. You have now successfully defined and created your first forward lookup zone.

Define and Create All Local Zones

In this step, you will create all the necessary local zone configurations. Instead of editing files manually, you will use a script to perform the following actions, ensuring an accurate setup:

  1. Create /etc/bind/named.conf.local: This file will be generated from scratch to contain the definitions for both your forward (mylocaldomain.net) and reverse lookup zones.
  2. Create the Reverse Zone File: The script will also create the db.REVERSE_IP file with the necessary PTR records.

First, gather the IP address information needed for both zone configurations:

IP=$(ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
REV_IP=$(echo $IP | awk -F. '{print $3"."$2"."$1}')
LAST_OCTET=$(echo $IP | awk -F. '{print $4}')

This extracts your VM's IP address and processes it to create the reverse IP format needed for the reverse lookup zone. For example, if your IP is 172.16.50.100, the REV_IP becomes 50.16.172 and LAST_OCTET becomes 100.

Next, create the main zone configuration file that tells bind9 about both zones:

sudo bash -c "cat > /etc/bind/named.conf.local" << EOF
//
// Do any local configuration here
//

// Forward lookup zone
zone "mylocaldomain.net" {
    type master;
    file "/etc/bind/db.mylocaldomain.net";
};

// Reverse lookup zone
zone "${REV_IP}.in-addr.arpa" {
    type master;
    file "/etc/bind/db.${REV_IP}";
};
EOF

This file defines both the forward lookup zone (for resolving names to IPs) and the reverse lookup zone (for resolving IPs back to names). The type master indicates that this server is authoritative for these zones.

Finally, create the reverse zone file with the PTR records:

sudo bash -c "cat > /etc/bind/db.${REV_IP}" << EOF
\$TTL    604800
@       IN      SOA     ns1.mylocaldomain.net. admin.mylocaldomain.net. (
                     $(date +%Y%m%d)01 ; Serial
                             604800     ; Refresh
                              86400     ; Retry
                            2419200     ; Expire
                             604800 )   ; Negative Cache TTL
;
@       IN      NS      ns1.mylocaldomain.net.
;
${LAST_OCTET}     IN      PTR     ns1.mylocaldomain.net.
${LAST_OCTET}     IN      PTR     webserver.mylocaldomain.net.
${LAST_OCTET}     IN      PTR     fileserver.mylocaldomain.net.
EOF

This automated approach ensures that both zones are defined correctly in named.conf.local and that the reverse zone file is properly created, preventing common configuration errors.

Validate bind9 Configuration and Test DNS Resolution with dig

In this step, you will validate all the configuration changes you've made and then test your live DNS server. It is a critical practice to check configuration files for syntax errors before applying them, as a mistake could prevent the bind9 service from starting correctly. Once validated, you will use the dig utility to perform DNS lookups and confirm your server is working as expected.

First, check the main bind9 configuration files for any syntax errors using named-checkconf. If this command produces no output, it means your configuration files (named.conf.local, named.conf.options, etc.) are valid.

sudo named-checkconf

Next, validate the syntax of your forward lookup zone file. The named-checkzone command checks a zone file for correctness.

sudo named-checkzone mylocaldomain.net /etc/bind/db.mylocaldomain.net

A successful check will return an "OK" status and show the serial number from your zone file:

zone mylocaldomain.net/IN: loaded serial 2024052001
OK

Now, validate the reverse lookup zone. Since the zone name and filename depend on your VM's IP address, first gather the necessary information:

IP=$(ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
REV_IP=$(echo $IP | awk -F. '{print $3"."$2"."$1}')
ZONE_NAME="${REV_IP}.in-addr.arpa"
ZONE_FILE="/etc/bind/db.${REV_IP}"

This creates the proper zone name and file path based on your VM's actual IP address.

Now run the zone validation command:

echo "Checking reverse zone: ${ZONE_NAME}"
sudo named-checkzone ${ZONE_NAME} ${ZONE_FILE}

A successful check will again show an "OK" status:

zone 50.16.172.in-addr.arpa/IN: loaded serial 2024052001
OK

Before restarting, ensure the bind9 service can read the zone files by setting the correct ownership. First, get the IP information again to construct the correct file path:

IP=$(ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
REV_IP=$(echo $IP | awk -F. '{print $3"."$2"."$1}')

Now set the proper ownership for both zone files:

sudo chown root:bind /etc/bind/db.mylocaldomain.net "/etc/bind/db.${REV_IP}"

With all configurations validated, it's time to restart the bind9 service to apply the changes:

sudo systemctl restart bind9

To be certain the service started correctly and loaded your new zones, check the system logs.

sudo grep named /var/log/syslog | grep "loaded serial"

You should see output confirming both of your zones were loaded successfully. The output will also include default zones like localhost, which is normal. The key is to find the lines for mylocaldomain.net and your reverse zone.

... named[...]: zone mylocaldomain.net/IN: loaded serial 2025071401
... named[...]: zone 50.16.172.in-addr.arpa/IN: loaded serial 2025071401
...

If you do not see these lines, review the output of sudo systemctl status bind9 for errors.

Finally, test your DNS server using dig. You will query your local server directly by specifying @127.0.0.1.

First, test the forward lookup for webserver.mylocaldomain.net:

dig @127.0.0.1 webserver.mylocaldomain.net

In the output, look for the ANSWER SECTION. It should show the A record pointing to your VM's IP address, and importantly, the flags in the header should include aa for an authoritative answer. The SERVER line at the bottom should show 127.0.0.1.

...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ...
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; QUESTION SECTION:
;webserver.mylocaldomain.net. IN A

;; ANSWER SECTION:
webserver.mylocaldomain.net. 604800 IN A <your_vm_ip>

;; SERVER: 127.0.0.1#53(127.0.0.1)
...

Next, test the reverse lookup using the -x flag. First, capture your VM's IP address:

IP=$(ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')

Now perform the reverse lookup test:

dig @127.0.0.1 -x $IP

The ANSWER SECTION should now show the PTR records you created, mapping your IP back to the hostnames, and the flags should again include aa.

...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: ...
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 1, ADDITIONAL: 1

;; QUESTION SECTION:
;<last_octet>.<your_reversed_network_part>.in-addr.arpa. IN PTR

;; ANSWER SECTION:
<last_octet>.<your_reversed_network_part>.in-addr.arpa. 604800 IN PTR fileserver.mylocaldomain.net.
<last_octet>.<your_reversed_network_part>.in-addr.arpa. 604800 IN PTR ns1.mylocaldomain.net.
<last_octet>.<your_reversed_network_part>.in-addr.arpa. 604800 IN PTR webserver.mylocaldomain.net.

;; SERVER: 127.0.0.1#53(127.0.0.1)
...

Congratulations! You have successfully configured, validated, and tested a basic authoritative DNS server.

Summary

In this lab, you configured a local authoritative DNS server on Linux using the bind9 software package. The process began with installing bind9 and its essential utilities, followed by editing the global named.conf.options file to define server-wide behaviors, such as setting up forwarders to resolve external domain queries.

You then created the primary DNS zones: a forward lookup zone for mylocaldomain.net to map hostnames to IP addresses using A records, and a corresponding reverse lookup zone to map IP addresses back to hostnames with PTR records. The lab concluded with the crucial validation phase, where you used named-checkconf and named-checkzone to check for configuration errors, and then tested the live server's functionality by performing successful forward and reverse lookups with the dig utility.