Create A Most Simple Root DNS Server Mirror for Hobby and Private Use

In this article you will learn how to run a simple root server mirror for your own use. The process is simple and straight forward. You can run it directly on your own computer, LAN or a server on the internet. This article is under step wise refinement writing. I’ll update the detail when I have time. In my case I use CentOS 8 on Linode, which is cost effective, reliable, simple and my location over the world. 1 core/1g VPS priced as low as 5 USD per month.

The link above is a affiliate referral link. Buy through this link will cost you the same. Linode will pay me a bit for the referral. And it will open a new browser tab or window.

Prerequisites:

  • GNU/Linux or any Unix-like system configuration ability: ssh, install software, start stop service service, edit config file, set the firewall rules.
  • Basic understanding to domain names: domain name, tld, dns record types
  • Bind and tools: zone ,dig, ping
  • VPS or local computer with Internet connection

Step 1: Setup up an VPS and login to it

$ ssh root@root-servers.cloud
The authenticity of host 'root-servers.cloud (172.104.253.39)' can't be established.
ECDSA key fingerprint is SHA256:/PNjt1Z2R3GLmO6FyjTkt/6Yb4UbLbVKYvbF2ZM7iuM.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'root-servers.cloud' (ECDSA) to the list of known hosts.
Last login: Fri Mar 27 04:34:36 2020 from 123.181.121.166

[root@root-servers ~]# cat /etc/redhat-release 
CentOS Linux release 8.1.1911 (Core) 

Step 2: System update to

[root@root-servers ~]# yum update -y

Step 3: Install Bind and Bind Utils

[root@root-servers ~]# yum install bind bind-chroot bind-utils
Last metadata expiration check: 0:02:03 ago on Fri 27 Mar 2020 04:38:14 AM UTC.
Dependencies resolved.
============================================================================================================================================================
 Package                                Architecture                   Version                                      Repository                         Size
============================================================================================================================================================
Installing:
 bind                                   x86_64                         32:9.11.4-26.P2.el8                          AppStream                         2.1 M
 bind-chroot                            x86_64                         32:9.11.4-26.P2.el8                          AppStream                         101 k
 bind-utils                             x86_64                         32:9.11.4-26.P2.el8                          AppStream                         436 k
Installing dependencies:
 bind-libs                              x86_64                         32:9.11.4-26.P2.el8                          AppStream                         170 k
 bind-libs-lite                         x86_64                         32:9.11.4-26.P2.el8                          AppStream                         1.1 M
 bind-license                           noarch                         32:9.11.4-26.P2.el8                          AppStream                          99 k
 python3-bind                           noarch                         32:9.11.4-26.P2.el8                          AppStream                         146 k

Transaction Summary
============================================================================================================================================================
Install  7 Packages

Total download size: 4.2 M
Installed size: 10 M
Is this ok [y/N]: y

Step 4: Config bind as root zone authoritative dns server and recursive resolver (The real trick magic here.)

The basic thoughts here lie in here is to understand what a root dns server is and set our bind service that way.

Step 4.1: Understand the dns resolve process

The root server is an authoritative for the root zone of all kinds top level domain names. Root zone can be express with a dot mark “.”. Using our domain name root-servers.cloud as an example to tell you this.

root-servers.cloud is actually root-servers.cloud. (be careful with the ending dot mark) for short. When using domain names we often omit the ending dot. If you visit our website your these will happen:

  1. Your dns client builtin with your OS or browser will ask the recursive dns server, which is the dns server your computer use, the ip address of this website “root-servers.cloud.”
  2. The recursive dns server will find an ip address of one of the 13 root servers (“.”) from its local predefined root hints.
  3. The recursive dns server will query the root server’s ip address find in last step who is responsible for top level domain cloud and get authoritative dns servers for tld cloud. Answer: “a.nic.cloud.” , “b.nic.cloud.” , and “c.nic.tld.” This is what the root servers really do. They hold the authoritative name servers names and address of each tld.
  4. The recursive dns will ask server “a.nic.cloud.”, “b.nic.cloud.” or “c.nic.cloud.” that are authoritative name servers for tld cloud the authoritative name servers for “root-servers.cloud.”. It will get answer “ns1.linode.com.” to “ns5.linode.com.”.
  5. The recursive will dns server then ask server ns1 to ns5.linode.com. for the ip address of “root-servers.cloud.”.
  6. The recursive give the ip address to you for visiting website.

Step 4.2: Plan and Configure Bind

Conclude what to do according the the process described in Step 4.1 and do it. For simple we will set this single instance of bind as both authoritative server for the root zone “.” and a recursive dns server for general domain name resolving. We need set bind to serve the root zone and set bind to use it self as root server when do recursive resolving.

Step 4.2.1: Let BIND serve the root zone.

Backup up named.conf before change it #cp /etc/named.conf /etc/named.conf.orig

Edit named.conf option section

  • Add address to bind. By default it will only listen to local loop back 127.0.0.1. Remember the ending “;”.
    listen-on port 53 { 127.0.0.1; xxx.xxx.xxx.xxx; };
    listen-on-v6 port 53 { ::1; xxxx:xxxx::xxxx; };
    //xxx can be set to your WAN or LAN IPv4/v6 address according to your needs.
  • recursion yes;
  • dnssec-enable yes;
  • dnssec-validation yes;
  • preferred-glue A;
  • fetch-glue yes;

Add two zones fake root-servers.net and root-servers.zone into named.conf

// The address of root servers
zone "root-servers.net." IN {
    type master;
    file "root-servers.net.zone";
    notify no;
};
//
// The master definition for the root zone
//
zone "." IN {
    type master;
    file "root.zone";
    notify no;
};

Add allow-query, or your query will be REFUSED.

It is wise to set your own ISP’s ip blocks not any ip as 0.0.0.0/0. Or you server may be under heavy load or bandwidth consumption.

allow-query     { localhost;
                0.0.0.0/0;
         };
// It is wise to set your own ISP's ip blocks not any ip as 0.0.0.0/0

4.2.2: Get the root zone file and setup root-servers.net.zone

TODO: Some explanation to be added.

Download a copy of root zone file into /var/named

[root@root-servers named]# cd /var/named
[root@root-servers named]# wget https://www.internic.net/domain/root.zone

Create root-servers.net.zone file in /var/named and add corresponding dns records. If you want to separate the recursive resolving and root zone authoritative name service or you need to setup multiple root server mirrors you may need to change the following A records and AAAA records to something you needs

root-servers.net.               86400   IN      SOA     a.root-servers.net. webmaster@root-servers.cloud. 2020032701     1800 900 604800 86400
root-servers.net.       518400  IN      NS      a.root-servers.net.
root-servers.net.       518400  IN      NS      b.root-servers.net.
root-servers.net.       518400  IN      NS      c.root-servers.net.
root-servers.net.       518400  IN      NS      d.root-servers.net.
root-servers.net.       518400  IN      NS      e.root-servers.net.
root-servers.net.       518400  IN      NS      f.root-servers.net.
root-servers.net.       518400  IN      NS      g.root-servers.net.
root-servers.net.       518400  IN      NS      h.root-servers.net.
root-servers.net.       518400  IN      NS      i.root-servers.net.
root-servers.net.       518400  IN      NS      j.root-servers.net.
root-servers.net.       518400  IN      NS      k.root-servers.net.
root-servers.net.       518400  IN      NS      l.root-servers.net.
root-servers.net.       518400  IN      NS      m.root-servers.net.
root-servers.net.       518400  IN      RRSIG   NS 8 0 518400 20190330170000 20190317160000 16749 . rrhD3nwsZoildW0wLNX4KdSlW6hrW6aQeEdeep/2dkmRW/78AN1fREY6iy9E2YYit5ojb3pQHXWcR8PCqnznWQhVRo7foOyu6YOHtdTuFf6j7CWYnB7eGtbF/z4+l2K7R/U63JCCnssokcf4CaooL/tW38gsqi7KmVd82eXnRS3PyHvNrfPFDIHRBmTK75V2g+XZp1CqecnODRsZuHGD8BRhtoIvWYHmplY/VxHVVr4/3g+V2Ch7S+hp/qk7aJfRiRhnmOx6cmTeQdeMP2SscWlv8FBtMM7lxHOdhw4jKZhwGUYSPZbRKQjAejln4M9DHf+rXaLgDI6QeNxn4ZHOxQ==
root-servers.net.       86400   IN      NSEC    aaa. NS SOA RRSIG NSEC DNSKEY
root-servers.net.       86400   IN      RRSIG   NSEC 8 0 86400 20190330170000 20190317160000 16749 . L/w/cqZbkts3u9RzNV4LsiTHPRrk6MaeKkuOs3eNfla7nD0H2aAvQHO8GjIGMgxtdcVeHh1knMiK/SUApk4O8FNEn6SU+ZmWVA8QfP8osO5wtl1nimZtsuTTMw2uU2Uj3IkJYqbdKYNPfIu7yEXA6p8EFxc3XVxYZenq/XTeboZqYVGd0urJ4VZnaYU/5d9ATcvElvitCUSo5+MP/uYbHUR85NdEVIrUeCiLtbcnTUNkJ1pgSrGQ8tnCOYs8UnDoqiO4QnVj9wnLPEbqCozWos685wtOiUhXDIe6sWMCdJ4flVWn/BmDExIjpD3pcm75Tt1KsQCUMpqPjdsGGVNDAA==
root-servers.net.       172800  IN      DNSKEY  256 3 8 AwEAAcH+axCdUOsTc9o+jmyVq5rsGTh1EcatSumPqEfsPBT+whyj0/UhD7cWeixV9Wqzj/cnqs8iWELqhdzGX41ZtaNQUfWNfOriASnWmX2D9m/EunplHu8nMSlDnDcT7+llE9tjk5HI1Sr7d9N16ZTIrbVALf65VB2ABbBG39dyAb7tz21PICJbSp2cd77UF7NFqEVkqohl/LkDw+7Apalmp0qAQT1Mgwi2cVxZMKUiciA6EqS+KNajf0A6olO2oEhZnGGY6b1LTg34/YfHdiIIZQqAfqbieruCGHRiSscC2ZE7iNreL/76f4JyIEUNkt6bQA29JsegxorLzQkpF7NKqZc=
root-servers.net.       172800  IN      DNSKEY  257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU=
root-servers.net.       172800  IN      DNSKEY  385 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjFFVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoXbfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaDX6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpzW5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relSQageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulqQxA+Uk1ihz0=
root-servers.net.       172800  IN      RRSIG   DNSKEY 8 0 172800 20190402000000 20190312000000 19164 . Nsqc9FdurKoopW8LBqJ3meWY2eOl162PzphTelIEpA6tuK6MitZL22bb8kYjZUxcQmV3tY0GZzA8Z9xpUIaRUZAQzgwFlSx8crGVRanL0lAsPhu7wj8P+TSa1bOIUZFqPheEasLhyX+02EORskJ3XhtZNB/sddtkFnyKwmj5h8w2btqsGurpgvga8yflFUPIMMAaQYtewhmW8AcQ5R9uOIMWimwrLaB+9WA+yLqzRao6Wl7YqY4fRk/hFCW3V9pDJdakNPYPlt65hFkxkBTYean3V7QIrBg/EegCWnMd3B6xZJCuKcEhRxpJTUmkjYiGShBqNNH9PvLwy27U1YpnJA==
root-servers.net.       172800  IN      RRSIG   DNSKEY 8 0 172800 20190402000000 20190312000000 20326 . A76nZ8WVsD+pLAKJh9ujKxxRDWfJf8SxayOkq3Gq9TX4BStpQM1e/KuX8am4FrVRCGQvLlhiYFNqm+PtevGGJAO0lTFLSiIuavknlkSiI3HMkrMDqSV+YlIQPk1C720khNpWy70WjjNvkq4sBU1GTkVPeFkM3gQI53pCHW+VobCPXZz70J+PnSOq7SmjrwXgU8E9iSXkI3yfhGIup2c54Sf9w0Bw10opvxXMT+1ALgWY1TnV1/gRixIUZp1K86iR8VeX9K/4UTqEa5bYux+aeIcQ2/4Qqyo3Ocb2RrbUvDNzU2lB4b1r/oHqsd6C0SiGmdo0A8R44djKMHVaD/JmLg==

a.root-servers.net.    518400  IN      AAAA    ::1
b.root-servers.net.     518400  IN      A       127.0.0.1
b.root-servers.net.    518400  IN      AAAA    ::1
c.root-servers.net.     518400  IN      A       127.0.0.1
c.root-servers.net.    518400  IN      AAAA    ::1
d.root-servers.net.     518400  IN      A       127.0.0.1
d.root-servers.net.    518400  IN      AAAA    ::1
e.root-servers.net.     518400  IN      A       127.0.0.1
e.root-servers.net.    518400  IN      AAAA    ::1
f.root-servers.net.     518400  IN      A       127.0.0.1
f.root-servers.net.    518400  IN      AAAA    ::1
g.root-servers.net.     518400  IN      A       127.0.0.1
g.root-servers.net.    518400  IN      AAAA    ::1
h.root-servers.net.     518400  IN      A       127.0.0.1
h.root-servers.net.    518400  IN      AAAA    ::1
i.root-servers.net.     518400  IN      A       127.0.0.1
i.root-servers.net.    518400  IN      AAAA    ::1
j.root-servers.net.     518400  IN      A       127.0.0.1
j.root-servers.net.    518400  IN      AAAA    ::1
k.root-servers.net.     518400  IN      A       127.0.0.1
k.root-servers.net.    518400  IN      AAAA    ::1
l.root-servers.net.     518400  IN      A       127.0.0.1
l.root-servers.net.    518400  IN      AAAA    ::1
m.root-servers.net.     518400  IN      A       127.0.0.1
m.root-servers.net.    518400  IN      AAAA    ::1

Step 5: Start service and test

[root@root-servers named]# systemctl start named-chroot

Test 1: Dig locahost for root zone we can see that ipv6 local address ::1 returns the right result and the address is our local bind.

[root@root-servers named]# dig . ns @localhost

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> . ns @localhost
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20774
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 27

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 63b77c677a40827301787fbc5e7da4ea05505701fa388c03 (good)
;; QUESTION SECTION:
;.				IN	NS

;; ANSWER SECTION:
.			518400	IN	NS	f.root-servers.net.
.			518400	IN	NS	a.root-servers.net.
.			518400	IN	NS	e.root-servers.net.
.			518400	IN	NS	b.root-servers.net.
.			518400	IN	NS	l.root-servers.net.
.			518400	IN	NS	c.root-servers.net.
.			518400	IN	NS	i.root-servers.net.
.			518400	IN	NS	k.root-servers.net.
.			518400	IN	NS	m.root-servers.net.
.			518400	IN	NS	d.root-servers.net.
.			518400	IN	NS	g.root-servers.net.
.			518400	IN	NS	h.root-servers.net.
.			518400	IN	NS	j.root-servers.net.

;; ADDITIONAL SECTION:
a.root-servers.net.	518400	IN	AAAA	::1
b.root-servers.net.	518400	IN	AAAA	::1
c.root-servers.net.	518400	IN	AAAA	::1
d.root-servers.net.	518400	IN	AAAA	::1
e.root-servers.net.	518400	IN	AAAA	::1
f.root-servers.net.	518400	IN	AAAA	::1
g.root-servers.net.	518400	IN	AAAA	::1
h.root-servers.net.	518400	IN	AAAA	::1
i.root-servers.net.	518400	IN	AAAA	::1
j.root-servers.net.	518400	IN	AAAA	::1
k.root-servers.net.	518400	IN	AAAA	::1
l.root-servers.net.	518400	IN	AAAA	::1
m.root-servers.net.	518400	IN	AAAA	::1
a.root-servers.net.	518400	IN	A	127.0.0.1
b.root-servers.net.	518400	IN	A	127.0.0.1
c.root-servers.net.	518400	IN	A	127.0.0.1
d.root-servers.net.	518400	IN	A	127.0.0.1
e.root-servers.net.	518400	IN	A	127.0.0.1
f.root-servers.net.	518400	IN	A	127.0.0.1
g.root-servers.net.	518400	IN	A	127.0.0.1
h.root-servers.net.	518400	IN	A	127.0.0.1
i.root-servers.net.	518400	IN	A	127.0.0.1
j.root-servers.net.	518400	IN	A	127.0.0.1
k.root-servers.net.	518400	IN	A	127.0.0.1
l.root-servers.net.	518400	IN	A	127.0.0.1
m.root-servers.net.	518400	IN	A	127.0.0.1

;; Query time: 0 msec
;; SERVER: ::1#53(::1)
;; WHEN: Fri Mar 27 07:02:02 UTC 2020
;; MSG SIZE  rcvd: 839

Test 2: Dig a domain name like root-servers.cloud get the right result

[root@root-servers named]# dig root-servers.cloud a @localhost

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> root-servers.cloud a @localhost
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35186
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 5, ADDITIONAL: 11

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 94870f4831abfb9d3c8d45095e7da70badfa62b449c17bc9 (good)
;; QUESTION SECTION:
;root-servers.cloud.		IN	A

;; ANSWER SECTION:
root-servers.cloud.	282	IN	A	172.104.253.39

;; AUTHORITY SECTION:
root-servers.cloud.	3581	IN	NS	ns4.linode.com.
root-servers.cloud.	3581	IN	NS	ns2.linode.com.
root-servers.cloud.	3581	IN	NS	ns1.linode.com.
root-servers.cloud.	3581	IN	NS	ns5.linode.com.
root-servers.cloud.	3581	IN	NS	ns3.linode.com.

;; ADDITIONAL SECTION:
ns5.linode.com.		172781	IN	AAAA	2400:cb00:2049:1::a29f:1819
ns4.linode.com.		172781	IN	AAAA	2400:cb00:2049:1::a29f:1b48
ns2.linode.com.		172781	IN	AAAA	2400:cb00:2049:1::a29f:1827
ns1.linode.com.		172781	IN	AAAA	2400:cb00:2049:1::a29f:1a63
ns3.linode.com.		172781	IN	AAAA	2400:cb00:2049:1::a29f:1981
ns5.linode.com.		172781	IN	A	162.159.24.25
ns4.linode.com.		172781	IN	A	162.159.26.99
ns2.linode.com.		172781	IN	A	162.159.24.39
ns1.linode.com.		172781	IN	A	162.159.27.72
ns3.linode.com.		172781	IN	A	162.159.25.129

;; Query time: 0 msec
;; SERVER: ::1#53(::1)
;; WHEN: Fri Mar 27 07:11:07 UTC 2020
;; MSG SIZE  rcvd: 411

Step 6: Finishing: Make BIND run on boot, config the firewall and change this servers default name servers

Step 6.1 Make BIND run on system boot

[root@root-servers named]# systemctl enable named-chroot
Created symlink /etc/systemd/system/multi-user.target.wants/named-chroot.service → /usr/lib/systemd/system/named-chroot.service.

Step 6.2 Open firewall wall TCP/UDP port 53

It’s better to open both TCP and UDP port 53. Though BIND can run on only UDP pot 53. In CentOS 8 firewalld is simple, just allow dns service will do.

[root@root-servers named]# systemctl enable named-chroot
[root@root-servers named]# firewall-cmd --permanent --add-service=dns

Step 6.3 Let your CentOS using your own dns

Some final words:

Update the root.zone at least monthly. Sometimes some tlds changes their signing key. It will bring in a tld unresolvable. Though a tld almost do not change authoritative name server address for their zone. But it is allow and may happen. So you still need to update your root.zone file.