In this guide, we shall demonstrate how to install and configure BIND DNS server running in a chroot environment. BIND, also known as named, is the widely used DNS server application across the internet. BIND however when installed with the default method poses some vulnerabilities such as exposing the root filesystem in case of a security breach. In this guide, we shall be configuring BIND in a chroot environment. What this mechanism does is to "Jail" a program to a certain path and configure it as the root directory. This in turn prevents the process to access other parts of the system. This makes running the application more secure since you are assured that in case of a breach, the attacker will not be able to access the rest of the filesystem.
Lab Environment
I am using my existing Rocky Linux setup to demonstrate this article. Following are the details of my Lab Environment:
Server Machine:
OS: Rocky Linux release 8.4 (Green Obsidian)
Hostname: rockylinux
IP Address: 172.29.10.4/24
Client Machine:
OS: Rocky Linux release 8.4 (Green Obsidian)
Hostname: rockylinux-lab
IP Address: 172.29.10.6/24
Step-1: Install BIND Chroot
On Rocky Linux 8, install BIND Chroot with the command below:
[root@rockylinux ~]# dnf install -y bind bind-chroot
Rocky Linux 8 - AppStream 3.5 kB/s | 4.8 kB 00:01
Rocky Linux 8 - AppStream 262 kB/s | 8.2 MB 00:31
Rocky Linux 8 - BaseOS 3.8 kB/s | 4.3 kB 00:01
Rocky Linux 8 - BaseOS 2.2 MB/s | 4.5 MB 00:01
Rocky Linux 8 - Extras 2.5 kB/s | 3.1 kB 00:01
Rocky Linux 8 - Extras 2.7 kB/s | 3.8 kB 00:01
...
Total download size: 2.2 M
Installed size: 4.5 M
Downloading Packages:
(1/2): bind-chroot-9.11.26-4.el8_4.x86_64.rpm 48 kB/s | 103 kB 00:02
(2/2): bind-9.11.26-4.el8_4.x86_64.rpm 419 kB/s | 2.1 MB 00:05
...
Installed:
bind-32:9.11.26-4.el8_4.x86_64 bind-chroot-32:9.11.26-4.el8_4.x86_64
Complete!
Verify that the packages are installed as below:
[root@rockylinux ~]# rpm -qa| grep bind
bind-9.11.26-4.el8_4.x86_64
bind-export-libs-9.11.26-4.el8_4.x86_64
bind-libs-lite-9.11.26-4.el8_4.x86_64
bind-chroot-9.11.26-4.el8_4.x86_64
bind-libs-9.11.26-4.el8_4.x86_64
bind-license-9.11.26-4.el8_4.noarch
python3-bind-9.11.26-4.el8_4.noarch
bind-utils-9.11.26-4.el8_4.x86_64
The directories below will be created once you successfully install bind-chroot
[root@rockylinux ~]# ls -l /var/named/chroot/
total 0
drwxr-x---. 2 root named 6 Jun 11 03:18 dev
drwxr-x---. 5 root named 53 Aug 7 10:35 etc
drwxr-x---. 3 root named 19 Aug 7 10:35 run
drwxr-xr-x. 4 root root 32 Aug 7 10:35 usr
drwxr-x---. 5 root named 52 Aug 7 10:35 var
Step-2: Turn ON BIND-Chroot Environment
To turn on the bind-chroot environment, run the initialization script located at /usr/libexec/setup-named-chroot.sh
then specify the directory at which you want to jail the bind process.
[root@rockylinux ~]# /usr/libexec/setup-named-chroot.sh /var/named/chroot on
The command above mounts all BIND configuration files into the chroot location. This means that you don't have to copy any files to the chroot directory.
Verify that the paths have been mounted at /var/named/chroot
[root@rockylinux-lab network-scripts]# mount | grep chroot /dev/mapper/rl-root on /var/named/chroot/etc/localtime type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/named.root.key type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/named.conf type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/named.rfc1912.zones type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/crypto-policies/back-ends/bind.config type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/protocols type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/services type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/named type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/usr/lib64/bind type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/usr/share/GeoIP type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) tmpfs on /var/named/chroot/run/named type tmpfs (rw,nosuid,nodev,seclabel,mode=755) /dev/mapper/rl-root on /var/named/chroot/var/named type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
Step-3: Configure DNS Server (named.conf)
Edit the /etc/named.conf
file and make the changes at listen-on port 53, allow-query and allow-query-cache.
This is as shown below:
[root@rockylinux ~]# cat /etc/named.conf options { listen-on port 53 { 127.0.0.1; any; }; listen-on-v6 port 53 { ::1; }; 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 { localhost; any; }; allow-query-cache { localhost; any; }; recursion yes; dnssec-enable yes; dnssec-validation yes; /* Path to ISC DLV key */ bindkeys-file "/etc/named.iscdlv.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 "." IN { type hint; file "named.ca"; }; include "/etc/named.rfc1912.zones"; include "/etc/named.root.key";
named.rfc1912.zones
as this is where we will define our zones
Step-4: Configure DNS Zones
We need to create the DNS zone for our environment. To achieve this, we need to edit the file /etc/named.rfc1912.zones.
Before that, we need to check our network interfaces and choose the IP that will be used for the DNS configuration. This IP should be a static IP.
In my setup, I'll use 172.29.10.4/24
IP for DNS configuration.
To check the IP, run the ip a
command and obtain the output.
Add the following lines to /etc/named.rfc1912.zones
to create the forward and reverse zones;
zone "example.com" IN { type master; file "example.com.zone"; allow-update { none; }; }; zone "10.29.172.in-addr.arpa" IN { type master; file "example.com.rzone"; allow-update { none; }; };
In the above config, example.com
is the forward zone while 10.29.172.in-addr.arpa
is the reverse zone. Make sure you use the correct details for your environment.
172.29.10.4
, we have used 10.29.172.in-addr.arpa.
Make sure you use the same syntax in your environment. For example, if your IP is 192.168.100.X
, you should use 100.168.192.in-addr.arpa
. If you have more than one subnets then create multiple entries for individual subnet and respective reverse zone files.
4.1: Configure Forward DNS Zone File
We need to configure the forward DNS zone files where we shall be adding entries for domain name resolution.
Navigate to /var/named/.
This is where we shall configure the DNS zone files.
[root@rockylinux ~]# cd /var/named
Verify that there exist DNS files;
# ls -l total 16 drwxr-x---. 7 root named 61 Aug 7 10:35 chroot drwxrwx---. 2 named named 6 Jun 11 03:18 data drwxrwx---. 2 named named 6 Jun 11 03:18 dynamic -rw-r-----. 1 root named 2253 Jun 11 03:18 named.ca -rw-r-----. 1 root named 152 Jun 11 03:18 named.empty -rw-r-----. 1 root named 152 Jun 11 03:18 named.localhost -rw-r-----. 1 root named 168 Jun 11 03:18 named.loopback drwxrwx---. 2 named named 6 Jun 11 03:18 slaves
Copy the named.loopback
contents to example.com.zone
to create the forward zone file.
[root@rockylinux named]# cp named.loopback example.com.zone
Assign the correct permissions to the file created
[root@rockylinux named]# chmod 644 example.com.zone [root@rockylinux named]# chown root:named example.com.zone
Edit the forward zone file created to update the entries. In this file, you will be required to configure the A records for your hosts and their respective IPs. A sample entry as shown below:
[root@rockylinux named]# cat example.com.zone
$TTL 1D
@ IN SOA example.com root (
0 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
IN NS localhost
localhost IN A 127.0.0.1
ns-master IN A 172.29.10.4
server1 IN A 172.29.10.5
server2 IN A 172.29.10.6
4.2: Create a Reverse DNS Zone File
Create a reverse DNS zone file using named.localhost
[root@rockylinux named]# cat named.localhost > example.com.rzone
Grant the correct permissions to the file created
[root@rockylinux named]# chmod 644 example.com.rzone [root@rockylinux named]# chown root:named example.com.rzone
Update the reverse DNS records, increasing the serial number for every record you modify this file. Provide the last octet or the last number of the IP address as shown the below examples. In the below file I have added PTR record for 172.29.10.4, 172.29.10.5, 172.29.10.6 which will resolve to ns-master.example.com, server1.example.com and server2.example.com respectively.
[root@rockylinux named]# cat example.com.rzone
$TTL 1D
@ IN SOA example.com. root.example.com. (
20220521 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
IN NS localhost.
4 IN PTR ns-master.example.com.
5 IN PTR server1.example.com.
6 IN PTR server2.example.com.
Step-5: Verify BIND chroot configuration
Verify bind configuration and confirm that you don't have any syntax errors.
[root@rockylinux named]# named-checkconf -t /var/named/ /etc/named.conf
If you get an error such as open: /etc/named.conf: file not found,
verify that the contents of /var/named
are mounted at /var/named/chroot.
Below is how to check and the sample output;
[root@rockylinux named]# mount | grep chroot /dev/mapper/rl-root on /var/named/chroot/etc/named type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/usr/lib64/bind type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) tmpfs on /var/named/chroot/run/named type tmpfs (rw,nosuid,nodev,mode=755) /dev/mapper/rl-root on /var/named/chroot/etc/localtime type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/named.root.key type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/named.conf type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/named.rfc1912.zones type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/rndc.key type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/crypto-policies/back-ends/bind.config type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/protocols type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/services type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/etc/named type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/usr/lib64/bind type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/mapper/rl-root on /var/named/chroot/usr/share/GeoIP type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota) tmpfs on /var/named/chroot/run/named type tmpfs (rw,nosuid,nodev,mode=755) /dev/mapper/rl-root on /var/named/chroot/var/named type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)
If you can't see any path mounted, re-run the named-chroot script as shown below:
[root@rockylinux ~]# /usr/libexec/setup-named-chroot.sh /var/named/chroot off [root@rockylinux ~]# /usr/libexec/setup-named-chroot.sh /var/named/chroot on
Step-6: Start named-chroot service
The named-chroot service runs the same way as the default bind service, other than the fact that it is running in a jail environment. Before we can start the named-chroot service, make sure you have stopped and disabled the default named service.
[root@rockylinux named]# systemctl stop named [root@rockylinux named]# systemctl disable named
Start and enable the named-chroot service
[root@rockylinux named]# systemctl enable --now named-chroot
Verify that the service has started successfully and is running
[root@rockylinux-lab named]# systemctl status named-chroot
● named-chroot.service - Berkeley Internet Name Domain (DNS)
Loaded: loaded (/usr/lib/systemd/system/named-chroot.service; disabled; vendor preset: disabled)
Active: active (running) since Sat 2021-08-07 12:53:50 EAT; 14s ago
Process: 1726 ExecStart=/usr/sbin/named -u named -c ${NAMEDCONF} -t /var/named/chroot $OPTIONS (code=exited, status=0/SUCCESS)
Process: 1721 ExecStartPre=/bin/bash -c if [ ! "$DISABLE_ZONE_CHECKING" == "yes" ]; then /usr/sbin/named-checkconf -t /var/named/chroot -z "$NAMEDCONF"; else echo "Checking of zone files is disabled"; fi (cod>
Main PID: 1728 (named)
Tasks: 7 (limit: 4942)
Memory: 64.0M
CGroup: /system.slice/named-chroot.service
└─1728 /usr/sbin/named -u named -c /etc/named.conf -t /var/named/chroot
Aug 07 12:53:50 rockylinux-lab named[1728]: network unreachable resolving './NS/IN': 2001:7fd::1#53
Aug 07 12:53:50 rockylinux-lab named[1728]: network unreachable resolving './DNSKEY/IN': 2001:503:c27::2:30#53
Aug 07 12:53:50 rockylinux-lab named[1728]: network unreachable resolving './NS/IN': 2001:503:c27::2:30#53
Aug 07 12:53:50 rockylinux-lab named[1728]: network unreachable resolving './DNSKEY/IN': 2001:500:9f::42#53
Aug 07 12:53:50 rockylinux-lab named[1728]: network unreachable resolving './NS/IN': 2001:500:9f::42#53
Aug 07 12:53:50 rockylinux-lab named[1728]: network unreachable resolving './DNSKEY/IN': 2001:500:a8::e#53
Aug 07 12:53:50 rockylinux-lab named[1728]: network unreachable resolving './DNSKEY/IN': 2001:dc3::35#53
Aug 07 12:53:50 rockylinux-lab named[1728]: managed-keys-zone: Key 20326 for zone . acceptance timer complete: key now trusted
Aug 07 12:53:50 rockylinux-lab named[1728]: network unreachable resolving './DNSKEY/IN': 2001:500:9f::42#53
Aug 07 12:53:51 rockylinux-lab named[1728]: resolver priming query complete
Step-7: Configure Rocky Linux 8 node as DNS Client
We can now configure the DNS server on Rocky Linux to verify domain resolution.
Add a DNS entry to /etc/resolv.conf
file as shown below:
[root@rockylinux-lab named]# echo "nameserver 172.29.10.4" >> /etc/resolv.conf
Remember to use the IP of the DNS server we configured above.
Step-8: Test DNS Configuration from Client
8.1: Test forward lookup zone
We can now check domain name resolution using our DNS server as shown below:
[root@rockylinux-lab named]# nslookup server1.example.com
Server: 172.29.10.4
Address: 172.29.10.4#53
Name: server1.example.com
Address: 172.29.10.5
8.2: Test reverse lookup zone
We can also use the dig
command to get the reverse DNS information about the domain name as below. Look out for ANSWER
section:
[root@rockylinux-lab ~]# dig -x 172.29.10.5
; <<>> DiG 9.11.26-RedHat-9.11.26-4.el8_4 <<>> -x 172.29.10.5
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23231
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 3
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: db03e58df53932aa0d10867c610e842dd8934fbd301f97fb (good)
;; QUESTION SECTION:
;5.10.29.172.in-addr.arpa. IN PTR
;; ANSWER SECTION:
5.10.29.172.in-addr.arpa. 86400 IN PTR server1.example.com.
;; AUTHORITY SECTION:
10.29.172.in-addr.arpa. 86400 IN NS localhost.
;; ADDITIONAL SECTION:
localhost. 86400 IN A 127.0.0.1
localhost. 86400 IN AAAA ::1
;; Query time: 1 msec
;; SERVER: 172.29.10.4#53(172.29.10.4)
;; WHEN: Sat Aug 07 16:01:33 EAT 2021
;; MSG SIZE rcvd: 181
As we can see, name resolution is working, just as expected.
Conclusion
I hope this article has been elaborate enough, we have configured bind-chroot DNS server on Rocky Linux 8. Feel free to reach out in the comment section in case you have any concerns.
how to put the bind dns cache to prometheus
You could have saved me hours of googling if you used the words “last octet” or last number of the IP address” instead of “serial” in step 4.2. Although to be fair, after said hours and hundreds of pages of blogs and other versions of this procedure, not one soul until user1686 on superuser dot com hinted me into finally realizing it is the last octet!
If you could add a little diagram or strong indicator that that number is not a serial number nor index, but rather the last part of the IP address corresponding directly to the named server IP and that it alone belongs squarely in the left column of the reverse lookup zone file, this procedure will be 100% perfect.
My sleep deprived self thought the parenthesis line counted as one line and thus the line with the 4 was the 4th entry and it was just a counting correlating to some serial mandated by a config rule…until I saw others counting with different numbers, although always incrementing by one from where they started. As I found out, the numbers can be completely out of order, which frankly, one example of that would have clued me in too.
Seriously, usually I see you have a procedure for something I want to do and I’m like “yes, this will make my life easier.”
Oh, also Step 5 should be named-checkconf -t /var/named/ /etc/named.conf. Minor, but I did confirm with the man pages. Threw me for a much smaller loop, that one. Take care!
Thank you for your detailed feedback and I am sorry for the extra effort you had to spent. I completely agree that I should have added those hints but somehow I missed. I have updated the article based on your feedback and added some hints in other places as well. Let me know if anything further can be added to improvise the quality here.
You’re very welcome!
Update: I think this is what is needed at the beginning of step 5:
or
Works with our without the trailing ‘
/
‘ on/var/named/chroot
.Kind Regards!