BIND9: from the start

BIND9 Installation

The system where this BIND9 service was installed is the same described in the article TinyCoreLinux Installation.
There is a BIND9 package for TinyCore, but jusk like almost everything else, the installation is very do-it-yourself1. We’re not provided with a firelighter, and not even matches; we really have to scratch a couple of sticks next to some kindling to get some fire.
The BIND9 extension can be installed from the browser tce-ab or directly by the installer tce-load

1
tce-load -wi bind


bind.tcz.dep OK
libcap.tcz.dep OK
Downloading: gcc_libs.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
saving to 'gcc_libs.tcz'
gcc_libs.tcz         100% |************************************************************************************| 1020k  0:00:00 ETA
'gcc_libs.tcz' saved
gcc_libs.tcz: OK
Downloading: attr.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
saving to 'attr.tcz'
attr.tcz             100% |************************************************************************************| 24576  0:00:00 ETA
'attr.tcz' saved
attr.tcz: OK
Downloading: libcap.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
saving to 'libcap.tcz'
libcap.tcz           100% |************************************************************************************| 24576  0:00:00 ETA
'libcap.tcz' saved
libcap.tcz: OK
Downloading: bind.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
saving to 'bind.tcz'
bind.tcz             100% |************************************************************************************| 1864k  0:00:00 ETA
'bind.tcz' saved
bind.tcz: OK


We then have to manually install some dependencies that for some reason aren’t automatically installed:

1
 tce-load -wi libuv.tcz

Downloading: libuv.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
saving to 'libuv.tcz'
libuv.tcz            100% |************************************************************************************| 65536  0:00:00 ETA
'libuv.tcz' saved
libuv.tcz: OK

Initial Setup

We’ll take advantage of the automatic backup (persistence) we’ve set up for /opt/, by placing BIND’s config files on /opt/local/etc/bind instead of the usual /usr/local/etc/bind

1
2
3
mkdir /opt/local/
mkdir /opt/local/etc/
mkdir /opt/local/etc/bind

We have to download two important pieces of information: the root zone and the bind keys

1
2
wget -O /opt/local/etc/bind/root.zone \
        http://www.internic.net/domain/root.zone

Connecting to www.internic.net (192.0.32.9:80)
saving to '/opt/local/etc/bind/root.zone'
root.zone            100% |************************************************************************************| 2113k  0:00:00 ETA
'/opt/local/etc/bind/root.zone' saved

1
2
wget -O /opt/local/etc/bind/bind.keys \
        https://downloads.isc.org/isc/bind9/keys/9.11/bind.keys.v9_11

Connecting to downloads.isc.org (151.101.2.217:443)
saving to '/opt/local/etc/bind/bind.keys'
bind.keys            100% |************************************************************************************|  3923  0:00:00 ETA
'/opt/local/etc/bind/bind.keys' saved

BIND9’s main configuration file is named.conf
Put the following in /opt/local/etc/bind/named.conf:

acl tjs  {
        localnets;          #zona dnstesteswan
        localhost;
        172.24.130.0/23;    #zona windows
        10.48.130.0/24;     #zona linux
};

options {
        directory "/opt/local/etc/bind";
        forwarders { 1.1.1.1; 8.8.8.8; };
        allow-query { tjs; };
        dnssec-validation auto;
        bindkeys-file "/opt/local/etc/bind/bind.keys";
        auth-nxdomain no;
        listen-on-v6 { none; };
};

  • directory: location for config files
  • forwarders: what BIND doesn’t know about, ask it to these
  • allow-query: networks that are allowed to make name resolution requests (an ACL was defined to hold them all)

In this file the zones are also declared. First, the root zone:

zone "." in {
        type hint;
        file "root.zone";
};

There are two configurations for each zone: the forward zone, which converts names to IP addresses, and the reverse zone, which converts IP addresses to names)
One direct zone for localhost2:

zone "localhost" in {
        type master;
        file "localhost.zone";
};

One reverse zone for localhost:

zone "localhost" in {
        type master;
        file "localhost.zone";
};

And for security reasons, specified on RFC1912, two other zones must be present:

zone "0.in-addr.arpa" in {
        type master;
        file "0.in-addr.arpa.zone";
};
zone "255.in-addr.arpa" in {
        type master;
        file "255.in-addr.arpa.zone";
};

Each zone declared on named.conf needs a zone file.
The forward zone file for localhost is /opt/local/etc/bind/localhost.zone:

; BIND data for LOCALHOST zone FWD
;
$ORIGIN localhost.
$TTL 3D
@   IN  SOA     @   root (
        2020090600  ; serial
                8H  ; refresh
                2H  ; retry
                7D  ; expiry
                1D  ; minimum
       )

@       IN  NS      @
        IN  A       127.0.0.1

The reverse zone for localhost is /opt/local/etc/bind/127.in-addr.arpa.zone:

; BIND data for LOCALHOST zone REV
;
$ORIGIN 127.in-addr.arpa.
$TTL    3D
@   IN  SOA localhost. admin.localhost. (
         2020090600 ; serial
                 8H ; refresh
                 2H ; retry
                 7D ; expire
                 1D ; negative caching TTL
         )

;NS
@           IN  NS  localhost.

;localhost
1           IN  PTR localhost.

On the Serial field I decided3 to use the date that the zone was changed, followed by two digits indicating if there were more than one revision on the same day. If the Serial of an altered zone file isn’t a bigger number than the Serial of the previously-loaded version, the new zone file won’t be loaded by the service.
The SOA times are close to the 24h, 2h, 30d, 4d recommended for external DNS servers, while 8h, 2h, 7d, 1d are recommended for internal DNS servers inside a LAN (source: RFC1912).
The zones demanded by RFC1912 only carry the SOA and NS blocks:
/opt/local/etc/bind/0.in-addr.arpa.zone

; BIND RFC1912 data
;
$ORIGIN 0.in-addr.arpa.
$TTL    3D
@   IN  SOA localhost. admin.localhost. (
         2020090600 ; serial
                 8H ; refresh
                 2H ; retry
                 7D ; expire
                 1D ; negative caching TTL
         )

;NS
@           IN  NS  localhost.

The 255.in-addr.arpa zone is identical, so we just copy it:

1
sudo cp -a /opt/local/etc/bind/0.in-addr.arpa.zone /opt/local/etc/bind/255.in-addr.arpa.zone

And all we need to change in the new file is $ORIGIN to 255.in-addr.arpa
Test if bind is operational with the command:4

1
sudo /usr/local/sbin/named -4g -c /opt/local/etc/bind/named.conf

05-Sep-2020 19:47:25.186 starting BIND 9.16.3 (Stable Release) 
05-Sep-2020 19:47:25.186 running on Linux i686 5.4.3-tinycore #2020 SMP Tue Dec 17 17:00:50 UTC 2019
05-Sep-2020 19:47:25.186 built with '--prefix=/usr/local' '--sysconfdir=/usr/local/etc' '--libdir=/usr/local/lib' '--localstatedir=/var' '--enable-shared' '--disable-static' '--with-libtool' '--with-openssl=/usr/local' '--with-readline=-lreadline -lncursesw' '--enable-full-report' '--enable-linux-caps' 'CC=gcc -flto -fuse-linker-plugin -march=i486 -mtune=i686 -Os -pipe'
05-Sep-2020 19:47:25.186 running as: named -4g -c /opt/local/etc/bind/named.conf
05-Sep-2020 19:47:25.186 compiled by GCC 9.2.0
05-Sep-2020 19:47:25.186 compiled with OpenSSL version: OpenSSL 1.1.1f  31 Mar 2020
05-Sep-2020 19:47:25.186 linked to OpenSSL version: OpenSSL 1.1.1f  31 Mar 2020
05-Sep-2020 19:47:25.186 compiled with zlib version: 1.2.11
05-Sep-2020 19:47:25.186 linked to zlib version: 1.2.11
05-Sep-2020 19:47:25.186 ----------------------------------------------------
05-Sep-2020 19:47:25.186 BIND 9 is maintained by Internet Systems Consortium,
05-Sep-2020 19:47:25.186 Inc. (ISC), a non-profit 501(c)(3) public-benefit
05-Sep-2020 19:47:25.186 corporation.  Support and training for BIND 9 are
05-Sep-2020 19:47:25.186 available at https://www.isc.org/support
05-Sep-2020 19:47:25.186 ----------------------------------------------------
05-Sep-2020 19:47:25.186 adjusted limit on open files from 4096 to 1048576
05-Sep-2020 19:47:25.186 found 1 CPU, using 1 worker thread
05-Sep-2020 19:47:25.186 using 1 UDP listener per interface
05-Sep-2020 19:47:25.186 using up to 21000 sockets
05-Sep-2020 19:47:25.206 loading configuration from '/opt/local/etc/bind/named.conf'
05-Sep-2020 19:47:25.209 reading built-in trust anchors from file '/opt/local/etc/bind/bind.keys'
05-Sep-2020 19:47:25.209 /opt/local/etc/bind/bind.keys:22: option 'managed-keys' is deprecated
05-Sep-2020 19:47:25.209 using default UDP/IPv4 port range: [32768, 60999]
05-Sep-2020 19:47:25.216 listening on IPv4 interface lo, 127.0.0.1#53
05-Sep-2020 19:47:25.219 listening on IPv4 interface eth0, 10.123.130.6#53
05-Sep-2020 19:47:25.219 generating session key for dynamic DNS
05-Sep-2020 19:47:25.219 sizing zone task pool based on 5 zones
05-Sep-2020 19:47:26.176 extra data in root hints 'root.zone'
05-Sep-2020 19:47:26.176 none:98: 'max-cache-size 90%' - setting to 162MB (out of 180MB)
05-Sep-2020 19:47:26.183 obtaining root key for view _default from '/opt/local/etc/bind/bind.keys'
05-Sep-2020 19:47:26.183 dnssec-validation auto: WARNING: root zone key not found
05-Sep-2020 19:47:26.183 using built-in root key for view _default
05-Sep-2020 19:47:26.183 set up managed keys zone for view _default, file 'managed-keys.bind'
[...]
05-Sep-2020 19:47:26.236 configuring command channel from '/usr/local/etc/rndc.key'
05-Sep-2020 19:47:26.236 couldn't add command channel 127.0.0.1#953: file not found
05-Sep-2020 19:47:26.239 not using config file logging statement for logging due to -g option
05-Sep-2020 19:47:26.239 managed-keys-zone: loaded serial 3
05-Sep-2020 19:47:26.243 zone 0.in-addr.arpa/IN: loaded serial 2020090600
05-Sep-2020 19:47:26.269 zone 127.in-addr.arpa/IN: loaded serial 2020090600
05-Sep-2020 19:47:26.273 zone 255.in-addr.arpa/IN: loaded serial 2020090600
05-Sep-2020 19:47:26.279 zone localhost/IN: loaded serial 2020090600
05-Sep-2020 19:47:26.279 all zones loaded
05-Sep-2020 19:47:26.283 running

Make the service start with the system:

1
2
echo "/usr/local/sbin/named -4 -c /opt/local/etc/bind/named.conf &" >> \
        /opt/bootlocal.sh && filetool.sh -b

And we must change the network configuration to use the service that was installed as the system DNS resolver. The change can be made immediately without needing to restart:

1
sudo echo "nameserver 127.0.0.1" > /etc/resolv.conf

But since we want to keep this setting after a restart, there’s another thing still to do: edit the /opt/eth0.sh file, comment the existing echo lines, and add the following:

echo "nameserver 127.0.0.1" > /etc/resolv.conf

Since the file is write-protected, the change needs to be forced with the vim command :w!. Finally, of course,

1
filetool.sh -b

Service Management

TinyCore has no pre-defined or configured service management process, it’s all by hand. If anything, BIND could be stopped with sudo killall named and turned on again with the command we’ve been using up to now.
But why sleep on the floor when we can get a mattress?
BIND, just like the OpenSSH we installed in the article about the initial installation of TinyCoreLinux (link at the top of this article), has a management script (in this case, bind) which the extension installs automatically in /usr/local/etc/init.d/

1
/usr/local/etc/init.d/bind

usage /usr/local/etc/init.d/bind start|stop|reload|restart|status

This way, we don’t need to write our own script5 nor try to adapt the OpenSSH script.
This script, due to the way the extensions are mounted on the system, can not be changed; to make the necessary changes, we need, first of all, to copy it to a permanent location where we have write permissions.
The original placement of the script is /usr/local/etc/init.d/, so a logical place would be /opt/local/etc/init.d/

1
mkdir /opt/local/etc/init.d

And we must copy while breaking the symlink that points to the real location where TinyCore has mounted the extension, cto make sure we copy the real file and not the symlink:

1
sudo cp -aL /usr/local/etc/init.d/bind /opt/local/etc/init.d/

Add the new script location to PATH, so we don’t have to write the full path when we want to run one of these scripts:

1
2
3
echo 'export PATH="$PATH:/opt/local/etc/init.d/" >> ~/.ashrc
sudo echo 'export PATH="$PATH:/opt/local/etc/init.d/" >> /root/.ashrc
filetool.sh -b

Now we can make changes to the script, namely the variable NAMED_OPTIONS. But first of all, we need to create, by hand, the user and group that will administer BIND (named), because a TCZ extension is just a set of files and won’t touch system files.

User and group for the service

The script expects a specific user, named, that is used specifically to run the service. To make it6:

1
2
sudo addgroup named -g 2000
sudo adduser named -u 2000 -G named -DH -s /bin/false

Set /etc/passwd and /etc/group as persistent:

1
2
echo "etc/passwd" >> /opt/.filetool.lst
echo "etc/group" >> /opt/.filetool.lst && filetool.sh -b

Change the permissions:

1
2
sudo chown -R named:named /opt/local/etc/bind
sudo chmod -R 660 /opt/local/etc/bind

When using the named user, BIND will try to use the folder /var/run/named, which doesn’t exist. We must make that folder and make it persistent:

1
2
3
mkdir /var/run/named
sudo chown named:named /var/run/named
echo "var/run/named" >> /opt/.filetool.lst && filetool.sh -b

On our current command that starts the service, our options were -4 (IPv4 only) and -c /opt/local/etc/bin/named.conf (path to the configuration file); those are the arguments that we must add to NAMED_OPTIONS on the /opt/local/etc/init.d/bind script (along with the nameduser):

NAMED_OPTIONS="-u named -4 -c /opt/local/etc/bind/named.conf"

Save and exit.

Setup BIND management with RNDC

However, the service startup script also depends on the control utility rndc7, which doesn’t come assembled and ready to run, because This… Is… CoreLinux…
Generate the key that authorizes rndc to give commands to bind and make it persistent

1
2
3
4
sudo rndc-confgen -a
sudo chown root:staff /usr/local/etc/rndc.key
sudo chmod 640 /usr/local/etc/rndc.key
echo "usr/local/etc/rndc.key" >> /opt/.filetool.lst && filetool.sh -b

Use this command to generate a template configuration:

1
2
sudo rndc-confgen > /opt/local/etc/bind/rndc.out
sudo cat /opt/local/etc/bind/rndc.out

# Start of rndc.conf
key "rndc-key" {
        algorithm hmac-sha256;
        secret "qXe5NgimFUmfztOZXJG4XXjkZJX0AdUEcTgci3+m38s=";
};

options {
        default-key "rndc-key";
        default-server 127.0.0.1;
        default-port 953;
};
# End of rndc.conf

# Use with the following in named.conf, adjusting the allow list as needed:
# key "rndc-key" {
#       algorithm hmac-sha256;
#       secret "qXe5NgimFUmfztOZXJG4XXjkZJX0AdUEcTgci3+m38s=";
# };
#
# controls {
#       inet 127.0.0.1 port 953
#               allow { 127.0.0.1; } keys { "rndc-key"; };
# };
# End of named.conf

On /opt/local/etc/bind/named.conf, add the instructions required to use rndc, following the example of the comment block above.
First, add the contents of rndc.key

1
sudo cat /usr/local/etc/rndc.key >> /opt/local/etc/bind/named.conf

Then add the following to /opt/local/etc/bind/named.conf

controls {
      inet 127.0.0.1 port 953
      allow { 127.0.0.1; } keys { "rndc-key"; };
};

To test,

1
2
3
sudo killall named
/usr/local/sbin/named -u named -4 -c /opt/local/etc/bind/named.conf &
rndc status

version: BIND 9.16.3 (Stable Release) 
running on verynice: Linux i686 5.4.3-tinycore #2020 SMP Tue Dec 17 17:00:50 UTC 2019
boot time: Sun, 06 Sep 2020 00:54:14 GMT
last configured: Sun, 06 Sep 2020 00:54:16 GMT
configuration file: /opt/local/etc/bind/named.conf
CPUs found: 1
worker threads: 1
UDP listeners per interface: 1
number of zones: 106 (97 automatic)
debug level: 0
xfers running: 0
xfers deferred: 0
soa queries in progress: 0
query logging is OFF
recursive clients: 0/900/1000
tcp clients: 0/150
TCP high-water: 0
server is up and running

Now we can go to /opt/bootlocal.sh and change the service activation line from the raw command to the init script. Comment the previous startup line (/usr/local/sbin/named -4 -c /opt/local/etc/bind/named.conf &) and add:

/opt/local/etc/init.d/bind start

Save and exit.
sudo reboot, to confirm that our settings survive a system restart.
After booting back, test the service:

1
rndc status

[...]
server is up and running

1
nslookup www.google.com


Server:         127.0.0.1
Address:        127.0.0.1#53

Non-authoritative answer:
Name:   www.google.com
Address: 216.58.211.228
Name:   www.google.com
Address: 2a00:1450:4003:805::2004


In a client that belongs to one of the networks authorized to ask for name resolution, change the DNS configuration to point to the IP of thi sserver (I’ve been using 10.123.130.6), check if DNS resolution is successful:

Creating DNS zones

Zones are configured in named.conf and the contents of each zone resides in zone files.
I think it’s good policy to separate zones by category under BIND’s config folder (/opt/local/etc/bind/), meaning that zone files should be placed in:

/opt/local/etc/bind/forward
/opt/local/etc/bind/reverse
/opt/local/etc/bind/slave
/opt/local/etc/bind/stub

In this case I won’t do this because we have very few zones, but even then making a folder for the zone files won’t let them get mixed with the BIND settings themselves:

1
sudo mkdir /opt/local/etc/bind/zones

The filename of a zone file is completely arbitrary, but some conventions are used; sometimes, the name declared on named.conf is used, adding a .zone extension; on Debian, zone file names are usually db.<zonename>.

Declaring zones on named.conf

Edit /opt/local/etc/bind/named.conf and add the following:
Forward zone of the DNS server:

zone "dnstests.wan" in {
        type master;
        file "zones/dnstests.wan.zone";
        # allow-update { };
        # allow-transfer { };
        # notify yes;
};

And its reverse zone:

zone "130.123.10.in-addr.arpa" in {
        type master;
        file "zones/130.123.10.in-addr.arpa.zone";
        # allow-update { };
        # allow-transfer { };
        # notify yes;
};
allow-update, allow transfer and notify are used when there are DNS servers that are only followers for this zone and should be updated by Zone Transfer when there are changes in the zone.

A regular forward zone is declared on /opt/local/etc/bind/named.conf like this:

zone "tjs.lan" in {
        type master;
        file "zones/tjs.lan.zone";
        # allow-update { };
        # allow-transfer { };
        # notify yes;
};

This network is a /23 and needs two reverse zones to be covered (AFAICT, reverse DNS zones are classful , that is, they can only be /8, /16, or /24), so I’ll declare these two zones:

zone "130.24.172.in-addr.arpa" in {
        type master;
        file "zones/130.24.172.in-addr.arpa.zone";
        # allow-update { };
        # allow-transfer { };
        # notify yes;
};
zone "131.24.172.in-addr.arpa" in {
        type master;
        file "zones/131.24.172.in-addr.arpa.zone";
        # allow-update { };
        # allow-transfer { };
        # notify yes;
};

Restart the service and check its status:

1
/opt/local/etc/init.d/bind restart

Stopping BIND:  /usr/local/sbin/rndc  stop
Using killall named on additional BIND processes...
Starting BIND:  /usr/local/sbin/named -u named -4 -c /opt/local/etc/bind/named.conf

1
rndc status

version: BIND 9.16.3 (Stable Release) 
running on verynice: Linux i686 5.4.3-tinycore #2020 SMP Tue Dec 17 17:00:50 UTC 2019
boot time: Sat, 19 Sep 2020 11:12:11 GMT
last configured: Sat, 19 Sep 2020 11:12:12 GMT
configuration file: /opt/local/etc/bind/named.conf
CPUs found: 1
worker threads: 1
UDP listeners per interface: 1
number of zones: 111 (97 automatic)
debug level: 0
xfers running: 0
xfers deferred: 0
soa queries in progress: 0
query logging is OFF
recursive clients: 0/900/1000
tcp clients: 0/150
TCP high-water: 0
server is up and running

1
tail -20 /var/log/messages | grep "named" | cut -d" " -f4-

verynice daemon.info named[1612]: none:98: 'max-cache-size 90%' - setting to 162MB (out of 180MB)
verynice daemon.notice named[1612]: command channel listening on 127.0.0.1#953
verynice daemon.info named[1612]: managed-keys-zone: loaded serial 58
verynice daemon.info named[1612]: zone 130.48.10.in-addr.arpa/IN: loaded serial 2020091600
verynice daemon.info named[1612]: zone 0.in-addr.arpa/IN: loaded serial 2020090600
verynice daemon.info named[1612]: zone 130.24.172.in-addr.arpa/IN: loaded serial 2020091710
verynice daemon.info named[1612]: zone dnstestes.wan/IN: loaded serial 2020091600
verynice daemon.info named[1612]: zone 127.in-addr.arpa/IN: loaded serial 2020090600
verynice daemon.info named[1612]: zone 131.24.172.in-addr.arpa/IN: loaded serial 2020091710
verynice daemon.info named[1612]: zone 255.in-addr.arpa/IN: loaded serial 2020090600
verynice daemon.info named[1612]: zone tjs.lan/IN: loaded serial 2020091710
verynice daemon.info named[1612]: zone localhost/IN: loaded serial 2020090600
verynice daemon.notice named[1612]: all zones loaded
verynice daemon.notice named[1612]: running
verynice daemon.info named[1612]: managed-keys-zone: Key 20326 for zone . is now trusted (acceptance timer complete)
verynice daemon.info named[1612]: resolver priming query complete

For testing, use the nslookup that’s already there or install dig. Nslookup still needs a dependence that wasn’t installed, though:

1
tce-load -wi readline.tcz

readline.tcz.dep OK
Downloading: readline.tcz
Connecting to repo.tinycorelinux.net (89.22.99.37:80)
saving to 'readline.tcz'
readline.tcz         100% |************************************************************************************|  128k  0:00:00 ETA
'readline.tcz' saved
readline.tcz: OK

The following resolutions were used for testing, based on zone files whose contents I leave for you to write based on the results on the screenshot (one zone file for each zone declared on /opt/local/etc/bind/named.conf), to be placed in /opt/local/etc/bind/zones according to the template shown in the zones requested by the RFC1912 (above):

1
2
3
4
nslookup dasilvat.tjs.lan
nslookup tjspc.tjs.lan
nslookup gateway.dnstestes.wan
nslookup verynice.dnstestes.wan

Since we left an MX record definition on the tjs.lan zone, its resolution was also tested (change the zone file according to the result on the screenshot below):

1
nslookup -type=MX tjs.lan

Final thoughts

Back when, I picked TinyCore Linux to implement this service because my host system had very little RAM to also implement the rest of the topology that I was going to use; however, TinyCore Linux needs RAM to load the system image and for applications, so the smallest working RAM size I was able to get away with was 256MB, because with just 128MB BIND9 didn’t have enough RAM to start.
However however, a Debian 10 system (Minimal Install), which runs the system from the mass storage and also has a pagefile, can run an SSH server, an NTP server (this TinyCore Linux only had a client) and BIND9 with the same 256MB so, unless running from RAM and being stateless is of paramount importance, my advice is to use Debian or another more mainstream Linux distro.