Distroname and release: Debian Squeeze

Chrooted DNS Server with BIND9


In this setup we will go through the setup of a DNS server using BIND9, which we will chroot for security enhancements.

We will in this setup create a lookup zone file for the domain test.local.
Debiantest1 has an IP of
Debiantest2 has an IP of (Needed only for the master / slave setup described at the bottom of the guide)

DNS lookups/queries runs on port 53 UDP, and zone transfers runs on port 53 TCP. Normally port 53 UDP should be enough to forward for external access, but if you have replicating DNS servers and zone transfers you should open for 53 TCP between the replication DNS partners as well.

Chroot BIND9 Guide reference:

According to the /etc/init.d/bind9 file a chrooted environment should be running in /var/lib/named and not /chroot/named as this howto is showing.
Please feel free to change it, if you find the need to do so. This setup works as well without any issues I have occoured.


The installation is very simple, like almost all debian applications.
#aptitude install bind9
Doing install, the installation process will create a user called bind, which should be used to run bind with. Some directories, will therefor need to have permissions for the bind as user or group after the chroot change.

Next, stop the bind9 service, since we will "move" it to chroot jail.
#/etc/init.d/bind9 stop
That was it, so far so good, now lets configure the chroot environment.

Creating the chroot environment

As mentioned ealier, make sure that the bind9 service is stopped before proceeding!

Let us create the directories that the chroot BIND9 should run in.
#mkdir -p /chroot/named/etc/namedb/slave
#mkdir -p /chroot/named/var/cache/bind
#mkdir /chroot/named/var/run
#mkdir /chroot/named/dev
Create character special file, since BIND will be unable to access /dev/*, or any other files or directories outside the chroot jail actually.
This check should not be needed, but you can check the major, and minor device numbers of /dev/random and /dev/null by using the ls command.
# ls -l /dev/random
crw-rw-rw- 1 root root 1, 8 Feb 16  2009 /dev/random
ls -l /dev/null
crw-rw-rw- 1 root root 1, 3 Feb 16  2009 /dev/null
Now create the two character special file devices, with the numbers from the output above, and set permissions on the files.
#mknod /chroot/named/dev/random c 1 8
#mknod /chroot/named/dev/null c 1 3
#chmod 666 /chroot/named/dev/*
Now we will need to move the existing files in /etc/bind to the new chroot directory, since it will be unable to leave the chroot and access the files in /etc/bind
We will also create a symlink, so other applications still will have access through the original location
#mv /etc/bind /chroot/named/etc
#ln -s /chroot/named/etc/bind/ /etc/bind 
Allow bind to access the directories and files in the chroot environment.
#chown bind:bind /chroot/named -R
Now change bind, so it will run in our new created chroot environment.
This is done by changing the /etc/default/bind9 file.
You can also see that it will run with the bind user in this file.
# run resolvconf?

#startup options for the server
OPTIONS="-u bind -t /chroot/named"

Configuration of bind and its chroot

We are also required to change the logging options, because we are still in chroot jail. If we do not do this, our logging will fail! Create a new file called /etc/rsyslog.d/bind-chroot.conf, and add the following line to the file. Check the syslog for errors if it does not create the /chroot/named/dev/log file
$AddUnixListenSocket /chroot/named/dev/log
Restart bind, and rsyslog to see if it is working.
#/etc/init.d/bind9 start
#/etc/init.d/rsyslog restart
Executing the rsyslog after a restart should create the /chroot/named/dev/log file.
#ls /chroot/named/dev
log  null  random
OK, that should be it, and the configuration of bind in chroot should be done. Next we will continue with the configuration of bind itself.

Configuration of bind

Get your DNS servers from your ISP (or use OpenDNS or something similar), and add them to the named.conf.options file. Most likely it is needed to uncomment the lines.
forwarders {
Make sure that we use our own newly installed named servers locally on the system.
#echo "search test.local" > /etc/resolv.conf
#echo "nameserver" >> /etc/resolv.conf
The named.conf.local is the file where we are going to add our zone files that we wants to act as a an DNS resolver for.
Note the link we are using is NOT /chroot/named/etc/bind/zonefiles/, but /etc/bind/zonefiles , since bind does not have access to the /chroot/ folder and can therefor not access the file.
Remember we created a symlink earlier that points /etc/bind to /chroot/named/etc/bind!
zone "test.local" {
type master;
file "/etc/bind/zonefiles/test.local"
Since we specified that the zonefile to use is an directory called zonefiles we need to create it,and set rights on the directory and zonefile. Remeber to set the rights for the zonefiles when new ones are created! Create directory for the zonefiles.
#mkdir /etc/bind/zonefiles
HINT: When creating new zone files, it is possible use the db.local as a template, and then edit it to your needs (Much editing needed though!).
The zonefiles can be named anything you want, but remember it must match the defined filename in the name.conf.local file.
#cp /etc/bind/db.local /etc/bind/zonefiles/test.local
After creating the directory and the zonefile, set bind as userowner and groupowner of the zonefile directory and its file. If you create a new zonefile, remember to set its rights, and else bind cannot read it!
#chown bind:bind /etc/bind/zonefiles/ -R

Bind Zone File Explanation

  • Serial
    A unique serial number that must be on each zone file. A good idea is to set this to a date and revision number (YYYYMMDDNN).
    Note that if the serial number is not changed after a change in the zone file, BIND will not update the slave server(s) since the serial stays the same, so remember to change it.

    An exampel could be 2010062505, which is year 2010, month 6, date 25th, and revision number 05 (5th change that day). If you change the file another day, start from 01.

  • Refresh
    The time for the slave server(s) to poll the master(s) zone file differences.
    If the serial is different it will update on the master / slave server(s).

  • Retry
    The time between retry attemps a slave server tries to contact the master server. If the master server cannot be contacted for a longer time that the retry value is set, the name server will stop working as resolver for that name.

  • Expire
    If a master server crashes, the slave server will take over, and continue to work as an resolver, but only in the time the expire is set. So it should be enough to get the master server up and running.

  • Minimum Cache TTL
    The time that other name servers caches the information for the current zone.

Zone files uses seconds as default, which might give a bad overview.
Therefor it is possible to use minutes, hours, days, weeks instead.

minutes = M
hours = h
days = d
weeks = w

30M; Refresh timeout of 30 minutes specified in minutes instead of seconds

Example of the edited zone file for our test.local domain. Note, must have a dot (.) in the end of both domain and e-mail!
$ORIGIN test.local.
$TTL 3600
@     IN     SOA    ns1.test.local.     hostmaster.test.local. (
                    2010070701; Serial
                           30M; Refresh
                            1h; Retry
                            2w; Expire
                            3h; Minimum Cache TTL
@     IN     NS     ns1; same as ns1.test.local.
@     IN     NS     ns2.test.local. ;same as ns2

@     IN     A; test.local.
ns1   IN     A
ns2   IN     A
test  600 IN A; sets a TTL of 600 seconds, instead of the default 3600
Ok that should be it, now restart bind and test. Please check the syslog for errors when doing so, since it will write here if it cannot load the zonefile for whatever reason. (A typo, security issues or similar)!
#/etc/init.d/bind restart
If you just make changes to the zone file already created you can update the current zone file, or all zone files without reloading or restarting the whole bind daemon. Remember to append the serial, or else the zonefile will not be updated on the slave servers.

To reload all zonefiles.
#rndc reload
To reload only a specific zonefile
#rndc reload test.local
Multiple zone files can be specified by just using a whitespace.
#rndc reload test.local test2.local
Lets make a ping test to see if the lookup is working, and the server is running (hopefully it will, since it is it own server!
#ping ns1.test.local
PING ns1.test.local ( 56(84) bytes of data.
64 bytes from icmp_seq=1 ttl=64 time=0.081 ms
64 bytes from icmp_seq=2 ttl=64 time=0.165 ms

Advanced configurations

In the below section, we will continue with the setup of the bind server. The below steps are not needed at all, but could be useful. If you do not need it, there is no need to continue reading.


In case that you will need to replicate zone files between servers, install the ntp package on all servers, to be completely sure that the time is the same on the servers or it could cause problems for loading zone files or replicate them to the master/slave server.

If ntp is not installed and configured on both servers, it is very likely you will start to get errors like these when bind tries to transfer the zonefiles, or update them.

example error from the master:
named[1873]: client request has invalid signature: TSIG 
transfer: tsig verify failure (BADTIME)
example error from the slave:
named[1873]: zone test.local/IN:refresh: failure trying master 
(source clocks are unsynchronized
If you need info about the ntp, please take a look here! http://www.linuxlasse.net/linux/howtos/22

As soon as the NTP is setup and running, continue with the Master / slave configuration below.

Master / slave configuration

It is possible to create slaves to replicate the zone files to. Unfortunately it is not possible through bind to replicate the named.conf.local file, so when a new domain is added the same change is needed to be done on the slave server as well. But ALL changes to zone files will be replicated!

To secure the transfer between the two servers we will use TSIG (Transaction Signatures). This can de done by using dnssec.
Enable dnssec on both master AND slave by adding "dnssec-enable=yes" in the { } section.
options {
Next we will create the TSIG encryption file on the master server (ONLY).
#dnssec-keygen -a hmac-md5 -b 128 -n host Debiantest1
When the key is created we will need to use the hash value in the Key: Section of the .private file. The file is in this example named: Kdebiantest1.+157+60049.private
#cat Kdebiantest1.+157+60049.private
Private-key-frmat: v1.2
Algorithm: 157 (HMAC_MD5)
Key: tASE3yatYigo7Phs+vAG8Q==
Bits: AAA=
On both master AND slave add the hash value to named.conf.
key "TRANSFER" {
        algorithm hmac-md5;
        secret "tASE3yatYigo7Phs+vAG8Q==";
On the master server only we will tell the IP of the slave server to replicate data between.
server {
      keys {
And on the slave server only the IP of the master.
server {
      keys {
Restart bind on all both master and slave server(s) after the changes.
#/etc/init.d/bind restart
You bind server, should now be up and running, and your zone files should be updated automatically after changes/updates to the zone files when running:
#rndc reload
If not, check the logs! :)

Useful informations

That are some useful reading here on the following links:
Do not trust the authors words! POC, tests and experience is key

Copyright LinuxLasse.net 2009 - 2024 All Rights Reserved.

Valid HTML 4.01 Strict Valid CSS!