---
# System prepended metadata

title: DNS server on CentOS

---


# DNS server on CentOS

## Introduction

One of the important services that ensure the functioning of the modern Internet is a DNS, which can be used to resolve host names and IP addresses. This provides a central way to manage hostnames and IP addresses, which is indispensable when an environment expands to multiple hosts. 
In this work, we use BIND9 suite running on CentOS 7 to set up the public DNS server with its own zone of responsibility.


## Goal/Tasks of the Project

This project is aimed to help us:
 - understand and revise the basic concepts of DNS
 - get hands-on experience with CentOS, which uses Red Hat's source code to create a final product very similar to RHEL.
 - understand how to work with BIND (version 9)
 - create our own public DNS server, that is responsible for its own zone


## Steps of the solution

1. Get VPC instance from Amazon Web Services (AWS), choose **CentOS** as operating system (referred to as `dns-server`)
2. Get domain name `vavk.ga` and specify IP address of `dns-server` as a resolving DNS
3. Get another VPC instance from AWS that would act as `web-server` with our domain name
4. Install BIND on the `dns-server` and configure it (so that it would be able to handle queries about our domain and about other domains)
    - Open the port #53 to enable DNS queries
    - Set up DNS as recursive
    - Create localhost zone
    - Create forward resolution zone for `vavk.ga` and DNS server
    - Create reverse resolution for `vavk.ga` and DNS server
    - Create root resolution zone
5. Test that everything works as expected


## Prerequisites

### Configuring AWS instance

<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/SHJc3fQ.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
      Sign up to AWS service account and choose EC2 option.
</div> 
</div>

<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/LLHLGVM.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
      Choose launch instance, then specify the choice: launch instance from template.
</div> 
</div>

<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/kkrik3I.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
      In the first step search <i>centos.</i>
</div> 
</div>

<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/nEwvHIU.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
      We do not need to specify anything, just choose to proceed.
</div> 
</div>



<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/FlwE2xo.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
      At the second step choose t2.micro as an instance type, press next.
</div> 
</div>

<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/vImyd29.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
      The next one goes mainly as default, only choose to enable resource-based IPv4 DNS requests.
</div> 
</div>

<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/JU5dKPa.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
      The next half of the Instance Details Configuration is also left as the default. As well as the 4 Step - "Add Storage".
</div> 
</div>


<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/QYEIWHR.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
      THe 5th step is for configuring tags, we are not adding any of them.
</div> 
</div>

<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/aLokvTf.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
      The sixth step is for configuring Security Group. We are choosing to create the one that was recommended by AWS Marketplace: we are adding the rule for SSH (which grants access for all IPs).
</div> 
</div>

<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/F5YuDRI.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
    Later the rules were enhanced by adding the rule for DNS(UDP and TCP) access for all IPs on port 53. It can be done by sticking to the following path: <i>"EC2" -> "Network & Security" -> "Securiry Groups" -> Your Security Group -> "Edit inbound rules" </i>
</div> 
</div>

<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/diXBT6Y.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
      At the 7th step we are creating an RSA key pair called <i>VavkCentOS</i> in .pem format.
</div> 
</div>


<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/W90T0k1.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
    To find the public IPv4 address, follow the path: <i>"EC2" -> "Instances" -> Your Instance</i>
</div> 
</div>

### Getting domain

<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/R5Q9DpP.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
To get free domain we used <a href="http://www.dot.tk/ru/index.html?lang=ru">dot.tk</a> website.
</div> 
</div>

<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/XqsjYXz.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
We found some unused domain ("vavk.ga") name and took it.
</div> 
</div>

<div style="display: flex; flex-wrap: wrap;">
<div style="margin-right: 20px; margin-bottom: 20px; width: 350px;">
  <img src="https://i.imgur.com/Jz1bMpH.png" width="100%" />
</div>
  <div style="width: 350px; margin-bottom: 20px;">
The next step was to configure our domain. We chose "Use your own DNS" option. Then we filled in all the required fields including Nameserver and IP address.
</div> 
</div>

And, finally, we added our detailed information. However, this is done only to complete the registration and make an order for the domain, so we have indicated random facts in the fields.
<p  align="center">
  <img src="https://i.imgur.com/DCNAJIK.png" width="350" />
  <img src="https://i.imgur.com/u5E7ORi.png" width="350" /> 
</p>
</br>

### Connecting to AWS

Connecting to AWS is done differently on OSX and Windows.

#### OSX
On OSX one can simply use the command provided by AWS itself `ssh -i "<Your key, generated on step7>" <hostname>@<Public IP>`, in our case `ssh -i "VavkCentOS.pem" centos@3.23.129.27`. (AWS tutorial is available above)
<img src="https://i.imgur.com/Sl6XyxG.png" width="450" /> 

#### Windows

1. Open **PuTTY Key Generator** and load a downloaded`.pem` file. Save private key, which is now in `.ppk` format.

2. Use it in **PuTTY Configuration**. Enter public IP of the machine, go to `SSH -> Auth` section on the left side menu and add you `.ppk` key, press `open`.

<img src="https://i.imgur.com/WnIQxHV.jpg" width="350" /> 

<img src="https://i.imgur.com/hpNPnpZ.jpg" width="350" /> 

### Install BIND on DNS Servers with `yum`:
`sudo yum install bind bind-utils`

<p  align="center">
  <img src="https://i.imgur.com/Sm5HL4G.png" width="350" />
  <img src="https://i.imgur.com/gdfe0Av.png" width="350" /> 
</p>


## Configure Bind
After bind was installed successfully, open the `named.conf` file for editing:

`sudo nano /etc/named.conf`

**Note**: we are configuring DNS to be responsible for `vavk.ga` zone
 
Here is our configuration:
```
options {
	listen-on port 53 { any; };
        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";
        recursing-file  "/var/named/data/named.recursing";
        secroots-file   "/var/named/data/named.secroots";
        allow-query     { any; };
        allow-query-cache { any;};

	recursion yes;
        
        /* Path to ISC DLV key */
        bindkeys-file "/etc/named.root.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 "vavk.ga" IN {
        type master;
        file "db.vavk.ga.test";
};

zone "77.179.15.52.in-addr.arpa." IN {
        type master;
        file "db.52.15.179.77";
};

zone "27.129.23.3.in-addr.arpa." IN {
        type master;
        file "db.3.23.129.27";
};

zone "." IN {
	type hint;
        file "named.ca";
};


include "/etc/named.local";
include "/etc/named.root.key";
```
- `listen-on port 53 { any; };`

  Listen on port 53 from any interface (be it localhost or public IP)

- `directory	"/var/named";`

   Directory, where the zone configuration files are stored
   
- `recursion yes` 

   Allows recursion on DNS server

- `allow-query     { any; };`

   Allow any hosts to query our DNS about records it is responsible for. Not very secure, though.

- `allow-query-cache { any; };`

  allow-query-cache is used to specify access to the cache and recursive queries on the DNS. Now, all hosts can ask our dns for any record, not just the ones it is responsible for.

### Including zones
  
```
zone "vavk.ga" IN {
        type master;
        file "db.vavk.ga.test";
};
```
 `type master` tells that out DNS is a primary one for the zone `vavk.ga`, the zone file and configuration is stored in `db.vavk.ga.test` file.
 
```
zone "77.179.15.52.in-addr.arpa." IN {
        type master;
        file "db.52.15.179.77";
};

zone "27.129.23.3.in-addr.arpa." IN {
        type master;
        file "db.3.23.129.27";
};
```
Both are reverse zones that our DNS should be able to perform a translation `IP to Hostname`.
`52.15.179.77` is the IP of our web-app `vrubel.vavk.ga` and `3.23.129.27` is the IP of our DNS server and web-app `van.gogh.vavk.ga`.

**Note**:  It is worth pointing out, that globally Amazon is responsible for these IPs. And in order to take over this responsibility, we need to be the owner of an IP. Therefore, if we query not our server about reverse translation, we will get a canonical amazon name of the machine. If we ask our DNS directly, resolution is as intended.

![](https://i.imgur.com/jWajQcK.png)

```
zone "." IN {
	type hint;
        file "named.ca";
};
```


Root zone, which DNS server can get *hints* from to resolve any name. Bind9 is provisioned with `named.ca` file, which contains root dns and is updated occasionally.
```
include "/etc/named.local";
include "/etc/named.root.key";
```
With these lines we include local zone resolution file + a secure root key file.



### Localhost zonefile

```
zone "localhost"{   # forward zone
        type master;
        file "localhost.zone";
};

zone "0.0.127.in-addr.arpa" { # reverse zone
        type master;
        file "0.0.127.zone";
};
```
`0.0.127.zone` and `localhost.zone` are preloaded into Bind9. 
![](https://i.imgur.com/YyglNo4.png)

### `vavk.ga` zonefile

```
$TTL 86400 ; 1 day
vavk.ga. IN SOA ns1.vavk.ga. vovablink1@gmail.com. (
   57
   28800
   7200
   2419200
   86400
    )

vavk.ga. IN NS ns1.vavk.ga. ; Nameservers responsible for the zone
vavk.ga. IN NS ns2.vavk.ga.
ns1.vavk.ga. IN A 3.23.129.27 ; Namesevers' IPs
ns2.vavk.ga. IN A 3.23.129.27
vrubel.vavk.ga.  IN A 52.15.179.77 ; IPs of 2 web-application in vavk.ga zone
van.gogh.vavk.ga. IN A 3.23.129.27

```
The numbers represent the time parameter for a record. For example, `TTL 86400` means that the record is cached for 86400 seconds, which is 1 day. In another file, we will show how one can specify these time parameters in a user-friendly way (without calculating seconds).

`vavk.ga. IN SOA ns1.vavk.ga. vovablink1@gmail.com.` tells that name server with hostname `ns1.vavk.ga.` is responsible for this zone, and `vovablink1@gmail.com` is the admin's email of the zone.

### Reverse-zone files
Here, time is specified in user-friendly format.
**File: db.3.23.129.27**
```
$TTL 3H
@ IN SOA ns1.vavk.ga. vovablink1@gmail.com (
1
3H
1H
1W
1H )
  # blank space means same address as a zone
  
  IN NS ns1.vavk.ga. # responsible name servers
  IN NS ns2.vavk.ga. 
  PTR van.gogh.vavk.ga. # DomainName -> IP translation record (web-app name)
  PTR ns1.vavk.ga. # DomainName -> IP translation record (dns name)

```
**File: db.52.15.179.77**
```
$TTL 3H
@ IN SOA ns1.vavk.ga. vovablink1@gmail.com (
1
3H
1H
1W
1H )

  IN NS ns1.vavk.ga.
  IN NS ns2.vavk.ga.
  PTR vrubel.vavk.ga.

```
## Starting DNS

1) Check that higher level domain (.ga in our case) contains a record about our dns server and zone.

   Use this [webpage](https://www.iana.org/domains/root/db/ga.html) to find IP of higher level domain and execute  `nslookup -type=NS <your zone> <IP of higher level domain>` (in our case `nslookup -type=NS vavk.ga 185.21.171.49`)
   
   ![](https://i.imgur.com/8PNOLq9.png)


1) In order to start a DNS server, it is crucial to open port 53 not only on AWS, but inside the machine. This is done with commands:
```
firewall-cmd --permanent --zone=public --add-port=53/tcp 
firewall-cmd --permanent --zone=public --add-port=53/udp
firewall-cmd --reload
```
2) After that, check that the port is open with command `sudo nmap -sT -p 53 <IP of DNS>`

![](https://i.imgur.com/DoJ3IBy.png)


3) Check that code has no syntax errors: `sudo named-checkconf`. If no output is provided, then everything is ok
   
4) `sudo systemctl enable named` automatically starts `named` on boot

5) `sudo systemctl start named` starts `named` right now

In case of an error, use `sudo systemctl status named` to see the error trace.

If previous steps are successful, you should see the following:

![](https://i.imgur.com/OBR5kbx.png)

## Demo

[Video demo (Christmas special edition)](https://www.youtube.com/watch?v=yAeP0FDQOCY) 

## Testing

With `nslookup`, we ensured that our DNS server:
* responds to requests about its zone and about outer domains,
* performs caching (disabling and enabling recursion),
* replies on reverse DNS queries about its zone.

With `dig +trace`, we saw how the hierarchy of DNS servers works with the query about our domain name (`van.gogh.vavk.ga.` -> root servers, `.ga` servers, and our server `ns1.vavk.ga.`)

![](https://i.imgur.com/my2IvHh.png)

![](https://i.imgur.com/WYrfihv.png)

Refer to demo video for further details.

## Difficulties faced

* **Problem (Understanding what to do)**
  * At first we had to understand our goal and how to accomplish it.
* **Problem (How to find IP of a domain name)**
  * **Description:** We got a domain name, but we thought that IP had to come with it automatically. 
  * **Solution:** Only afterwards we understood that we had to get an additional VPC with public IP and assign domain name to it. (Domain name is just a domain name, not a runing web-application). 
* **Problem (BIND versions)**
  * We found several tutorials on setting DNS server with BIND, however, due to difference in versions of BIND, there were contradictory pieces, which we had to try in different combinations to get a working result.
* **Problem (Testing)**
    * **Evidence**: `nslookup <any outer domain name> 3.23.129.29` yields answers from both remote and local machines, `nslookup <our domain name> 3.23.129.29` doesn't work from remote machines, and works locally.
    * **Problem & Solution**: port 53 was blocked by firewall of the AWS inctance DNS ran on. From now, `nslookup <our domain name> 3.23.129.29` works from any machine, and our domain name is globally discoverable.
    * **Question**: before we opened the port, we still got answers to `nslookup <any outer domain name> 3.23.129.29`, why?
    * **Evidence**: `nslookup` always responds with non-authoritative answer; `nslookup <our domain name> <random IP address>` works (and behaviour of `dig` is analogous)
    * **Improvement**: Googling for "ways to ask *specific* DNS server" proposed the following: `nslookup -all -debug -type=ANY -class=ANY vavk.ga 3.23.129.27`.
    * **Evidence**: with the new way to send requests, our DNS server responded only to requests about our zone; requests about external domains resulted in `SERVFAIL`.
    * **Solution**: Enabling recursion and allowing-query-cache not only from `localhost`, but from `any` IP address. From now all forward requests to our DNS server were successful, and requests to arbitrary IP addresses were failing, as expected.
    * **Challenge**: then we had to come up with the query for reverse zones (those strange flags were cumbersome, and not using them made command disregard specified domain name).
    * **Deus ex machina**: we tried `nslookup` not only from WSL and OSX, but from VPC instance that acted as web-server (on Ubuntu), and discovered, that nslookup has different behavior there: 
    ![](https://i.imgur.com/nqy9NRR.png)
    ![](https://i.imgur.com/LsYnxm6.png)
    So, `nslookup` and `dig` work as expected on Ubuntu and provide **strange behavior** on other operating systems.
    * **Takeaway**: we continued to test our DNS server from the Ubuntu VPC instance.
* **Problem (Root hints file)**
   * **Description:** We had downloaded `root.list` file that contained information about root servers, but `named` would not start, giving an `Permission denied` error.
   * **Solution:** BIND 9 has a file`named.ca`, which contains root hints, so inculding it solved the issue.
* **Problem (DNS would not respond about external IPs)**
  * **Description:** When we learned how to ask our DNS server specifically (and truly), started to fail requests about external domain names ![](https://i.imgur.com/Hh8mz1m.png)
  * **Solution:** Recursion for external IPs trabslation was not allowed. `allow-query-cache { any; };` solved the issue. 
 
## Conclusion, contemplations and judgement

This project was indeed a challenge that took us a considerable amount of time to complete. However, it really helped to learn something new and to put the course knowledge into practice. We have done some pretty basic functionality and there are still many aspects of our work that can be implemented.

**Accomplished:**
 * Understood how to work with AWS
 * Created our own domains and run web-apps
 * Understood how to work with CentOS
 * Understood main features of BIND9
 * Created a public DNS server
 * Found out that `nslookup` and `dig` are not properly implemented on WSL and OSX
 
**Perspective:**
 * Create a slave dns server for zone transfer
 * Understand the reverse IP resolution in more detail

**Acknowledgements:** 
* We want to thank our mentor *Kirill Saltanov* for guiding and accompanying us during our project. Without his help and insight, we wouldn't be able to tackle this project.

## References

1. [Habr tutorial on DNS with BIND](https://habr.com/ru/sandbox/21144/)
1. [Tutorial on local DNS configuration](https://www.hostinger.com/tutorials/how-to-use-the-dig-command-in-linux/)
1. [BIND 9 documentation](https://bind9.readthedocs.io/en/latest/)
1. [BIND manual](https://docstore.mik.ua/orelly/networking_2ndEd/dns/ch04_01.htm) 
1. [Oracle documentation](https://docs.oracle.com/en/operating-systems/oracle-linux/6/admin/about-resource-records.html#)
1. [Where to find root dns IPs for `.ga`](https://www.iana.org/domains/root/db/ga.html)
1. [DNS Troubleshooting](https://tools.ietf.org/id/draft-ietf-dnsop-no-response-issue-12.html)


## Appendix <a id="appendix"></a>
### Useful commands
We found several commands useful:

`nslookup -all -debug -type=ANY -class=ANY vavk.ga 3.23.129.27` — ask *particular* nameserver about some domain name. Note that `dig @3.23.129.27 vavk.ga` somehow ignores provided ip address of the nameserver, and successfully performs a lookup, even if ip address of the nameserver is modified.

`sudo nmap -sT -p 53 185.21.171.49` — check status of particular port of machine with the given ip adddress

`nslookup -type=soa vavk.ga` — find an authoritative server for the given zone

`dig vavk.ga NS` or `nslookup -type=ns vavk.ga` — find NS entries for some domain name. We could also specify nameserver responsible for .ga, however it seems to be ignored.

`host 3.23.129.27`, `dig -x 3.23.129.27`, `nslookup 3.23.129.27` or `nslookup -type=ptr 3.23.129.27` for reverse DNS queries
