Having a decent DNS system is mandatory if you have more than a few devices on your network. If WINS fails (or becomes obsolete), or Bonjour doesn’t fit your needs, try this out on your Linux server!

Table of Contents

  1. Required Software
  2. Generate secure key for updates
  3. Setup DHCP
  4. Setup BIND9 with Dynamic DNS
    1. /etc/bind/named.conf
    2. /etc/bind/named.conf.options
    3. /etc/bind/named.conf.local
    4. Check your configuration
    5. /etc/bind/zones/cool.lan
    6. /etc/bind/zones/168.192.in-addr.arpa
    7. Check your zones
  5. Let’s put it all together!
  6. Troubleshooting

First off, this blog post contains instructions which may interrupt your users, so plan on either doing this in down time, or have an ample backup system in place.

Warning: This works on Debian Stretch (9.x) and earlier! Changes are needed to selinux on Buster and higher.

Required Software / Prep work

In our environment, we are assuming you have control of your network (i.e. DHCP, DNS, switches, etc), and know how your network is currently laid out. We will pretend we are on a single 192.168.0.0/24 subnet, with a default gateway of 192.168.0.254.

Assuming you are on a Debian-based or Ubuntu-based distribution, you can install all the software you need with:

apt update
apt upgrade
apt install isc-dhcp-server bind9 bind9utils

Don’t worry about messing with your network yet; the servers do not activate automatically!

Next, map out how you want your network laid out. An example is below, and will show what is needed vs optional:

Total Network Range: 192.168.0.0/24 (.1 to .254)
Total static-IPs: Default Gateway (.254), Wireless Access Point (.253), Managed Switch (.252), Linux File Server (.251), Linux VM A (.250), Linux VM B (.249), Network Printer A (.248), Network Printer B (.247), Linux DHCP/DNS (.1) (this server)
DHCP Range: 192.168.0.2 to 192.168.0.200 (Note: always leave a few extra static IPs)
Default Gateway IP: 192.168.0.254
ISP / External DNS Servers: 1.2.3.4, 1.2.3.5
Internal DNS name: COOL.LAN.

Generate secure key for updates

For updates to happen securely, we need to generate a random key for both BIND and ISC-DHCP-Server to use.

rndc-confgen

Copy the top section into a new file called /etc/bind/rndc.conf. You’ll need to include this file in your DHCP and DNS config.

Now change the file permissions so only root and bind can read it, and create a link for DHCP:

chmod 660 /etc/bind/rndc.conf
chown root:bind /etc/bind/rndc.conf
cd /etc/dhcp
ln -s /etc/bind/rndc.conf

Setup DHCP

Now we will start with DHCP. We know what our range is, so we’ll copy that range while the current DHCP server is running (i.e. from your SOHO router). We’ll take a backup of the current config file that installs from apt, then work on a blank file.

mv /etc/dhcp/dhcpd.conf{,.ORIG}
nano /etc/dhcp/dhcpd.conf

With your blank file, let’s get to work. Remember that after every “statement”, you need a semicolon (;). We’ll check the configurations before ever starting it. Paste the following block into this blank file, then save it:

authoritative;
default-lease-time    14400;
max-lease-time        18000;
log-facility          local7;

ddns-domainname "cool.lan.";
ddns-rev-domainname "in-addr.arpa.";
ddns-update-style interim;
ignore client-updates;
update-static-leases on;
use-host-decl-names on;
option domain-name "cool.lan.";
include "/etc/dhcp/rndc.key";
update-optimization off;
update-conflict-detection off;
include /etc/dhcp/rndc.conf;
zone cool.lan. {
        primary 192.168.0.1;
        key rndc-key;
}
zone 168.192.in-addr.arpa. {
        primary 192.168.0.1;
        key rndc-key;
}


subnet 192.168.0.0 netmask 255.255.255.0 {
  range                      192.168.0.2 192.168.0.200;
  option subnet-mask         255.255.255.0;
  option routers             192.168.0.254;
  option domain-name-servers 192.168.0.1;

  host gateway {
    hardware ethernet 00:11:22:33:44:55;
    fixed-address 192.168.0.254;
  }
  host wireless {
    hardware ethernet 22:33:44:55:66:77;
    fixed-address 192.168.0.253;
  }
.....
  host dhcpdns {
    hardware ethernet aa:bb:cc:dd:ee:ff;
    fixed-address 192.168.0.1;
  }
}

(A quick reference as to why the reverse zone is in-addr.arpa, as opposed to 168.192.in-addr.arpa)

Add in any extra static-IP’d devices, following the format above, that you have in your network. When adding MAC addresses, I’ve always used lowercase characters, and it works for me.

Once you are satisfied, close and save the file (Ctrl+X, Y, Enter).

Check your configuration

A bad DHCPd configuration will prevent the service from starting. You can check it anytime by running dhcpd -t. As long as you don’t see any warnings, then the syntax check has passed, and the service can start when you tell it. We’ll keep it turned off for now.

Setup BIND9 with Dynamic DNS

The BIND configuration files are split up into many different, smaller files. We’ll keep it like this so that package upgrades do not wipe out our configurations. We’ll look at each file, one at a time:

/etc/bind/named.conf

acl internals { 127.0.0.0/8; 192.168.0.0/24; };
include "/etc/bind/named.conf.options";
controls {
        inet 127.0.0.1 port 953 allow { 127.0.0.1; };
};

include "/etc/bind/named.conf.local";
include "/etc/bind/named.conf.default-zones";

The above file creates an ACL, which allows us to specify what different networks or computers can do with our DNS server. Then, we include the extra files that we make our regular changes to.

/etc/bind/named.conf.options

options {
  directory "/var/cache/bind";
  query-source address * port *;
  forwarders {
    1.2.3.4;
    1.2.3.5;
  }
  dnssec-validation auto;
  auth-nxdomain no;
  listen-on-v6 { none; ); # You can edit this if you want IPv6 DNS services
  listen-on { 127.0.0.1; 192.168.0.1; };
  allow-transfer { none; }; # Used if you want multiple DNS servers
  allow-recursion { internals; }; # Specifies the LAN can recursively ask
  version none; # Hide version number in replies
};

Pay attention to the forwarders {} section - that needs to be changed to your external DNS servers, either from your ISP or a third party service. Save this file.

/etc/bind/named.conf.local

include /etc/bind/rndc.conf;
controls {
  inet 127.0.0.1 port 953 allow {
    127.0.0.1;
    192.168.0.1;
  } keys { "rndc-key"; }; #We can now refer to the key with this variable
};

zone "cool.lan" {
  type master;
  file "/etc/bind/zones/cool.lan";
  allow-update { key rndc-key; };
};
zone "168.192.in-addr.arpa" {
  type master;
  notify no;
  file "/etc/bind/zones/168.192.in-addr.arpa";
  allow-update { key rndc-key; };
};

Save and close this file.

Check your BIND config

To check your configuration, run the following command:

named-checkconf

If it states the configuration is OK, we can move on. If not, you’ll need to check for typos, then ask Google. After each change to the configuration files, re-run the check to make sure BIND will start when you want it.

If you get warnings about missing zone files, you can safely ignore them for now - we’ll create them next.

/etc/bind/zones/cool.lan

$ORIGIN .
$TTL 604800     ; 1 week
cool.lan         IN SOA  cool.lan. root.cool.lan. (
                                1          ; serial
                                604800     ; refresh (1 week)
                                86400      ; retry (1 day)
                                2419200    ; expire (4 weeks)
                                604800     ; minimum (1 week)
                                )
                        NS      cool.lan.
                        NS      localhost.
                        A       192.168.0.1
$ORIGIN cool.lan.
$TTL 3600       ; 1 hour
DHCPDNS            A       192.168.0.1
GATEWAY            A       192.168.0.254
WIRELESS           A       192.168.0.253
SWITCH             A       192.168.0.252
LINUXFS            A       192.168.0.251
LINUX-VM-A         A       192.168.0.250
LINUX-VM-B         A       192.168.0.249
PRINTER-A          A       192.168.0.248
PRINTER-B          A       192.168.0.247

We can add other types of records, such as CNAME, TXT, or even AAAA records (IPv6 only). This will also let us reorganize our network on a whim! Save and close this file.

/etc/bind/zones/168.192.in-addr.arpa

$ORIGIN .
$TTL 604800     ; 1 week
168.192.in-addr.arpa    IN SOA  cool.lan. root.cool.lan. (
                                1          ; serial
                                604800     ; refresh (1 week)
                                86400      ; retry (1 day)
                                2419200    ; expire (4 weeks)
                                604800     ; minimum (1 week)
                                )
                        NS      dhcpdns.
                        A       192.168.0.1
$ORIGIN 0.168.192.in-addr.arpa.
$TTL 3600       ; 1 hour
1                       PTR     dhcpdns.cool.lan.
254                     PTR     gateway.cool.lan.
253                     PTR     wireless.cool.lan.
252                     PTR     switch.cool.lan.
251                     PTR     linuxfs.cool.lan.
250                     PTR     linux-vm-a.cool.lan.
249                     PTR     linux-vm-b.cool.lan.
248                     PTR     printer-a.cool.lan.
247                     PTR     printer-b.cool.lan.

In this file, we are adding the reverse-lookup zone - if I were to query 192.168.0.254, I expect the answer to be gateway.cool.lan. when this is done.

PTR records are Pointer records for your DNS server. Ideally, they should match your forward-lookup zone records, albeit backwards.

You can also use the file if you have a network larger than a /24 - for example, a /23 would have addresses from 192.168.0.1 through to 192.168.1.254, inclusive, for 510 possible addresses/names. Let’s save this file, then do some checks.

Check your zone configuration

To check your zones, we need to run a command against each zone, and the zone file:

named-checkzone cool.lan /etc/bind/zones/cool.lan
named-checkzone 168.192.in-addr.arpa /etc/bind/zones/168.192.in-addr.arpa

Each time you make edits, you’ll want to increase the serial number by at least 1. The “standard” method, once it’s running, is to include the date with your serial number - for example, YYYYMMDDxx.

If you want to make changes after starting the DNS server, you’ll need to freeze your zone temporarily:

rndc freeze cool.lan
rndc freeze 168.192.in-addr.arpa

Make your changes, increment your Serial number by 1, then reload and thaw the zones to allow dynamic updates again:

rndc reload cool.lan
rndc thaw cool.lan
rndc reload 168.192.in-addr.arpa
rndc thaw 168.192.in-addr.arpa

Let’s put it all together

So we’re going to attempt to start the services. I am assuming you ran your configuration checks (see above), and errors have been dealt with.

To start your DHCP server, you need to edit /etc/default/isc-dhcp-server. Search for the line INTERFACES= and add your network interface to the list. You can find the name by running ip a, and looking at your active network card’s name.

Once that’s saved, let’s start BIND and verify it works correctly:

systemctl start bind9.service
nslookup www.talk-about-it.ca 192.168.0.1
# Looks up this blog's IP address at the DNS server 192.168.0.1
nslookup 192.168.0.254 192.168.0.1
# Looks up the name for 192.168.0.254 at the DNS server 192.168.0.1

If you get answers back, then BIND is able to recursively lookup your queries. If not, then troubleshoot why before continuing!

Next, try from another computer, with the same command (either on Windows, Linux or Mac). Make sure that test succeeds before forcing all your clients to use this server.

Once you are satisfied, you’ll need to turn off your existing DHCP server - likely on your router. If not, you’ll get conflicts that make dealing with issues very difficult. Instructions for each router or DHCP server are different. The point is, get the DHCP service turned off on your network before turning on your new DHCP server.

To start the DHCP server, execute:

systemctl start isc-dhcp-server.service

Your client computers should start getting new IP addresses and the new DNS server assigned to them as their existing leases expire… Or as you manually attempt to renew their leases. Verify that your computers can access websites outside your network, and that you can access your internal devices by their DNS name.

Troubleshooting

The hard part is figuring out where errors are when the Internet doesn’t appear to work. That is why I made this post so long - so you can refer to it when things don’t work.

First off, breath. Nothing gets solved by panicking.

Check your logs - typically located at /var/log/syslog. You can sort it by only DHCPd and BIND messages by running:

grep -E \(dhcpd\|named\) /var/log/syslog

As systems get their new DHCP leases, you should see DHCPACK and DHCPINFORM messages appearing for that system; then you should see the clients updating zone records in both the 168.192.in-addr.arpa. zone as well as cool.lan. zone. If the computers are not working, read the lines that show either warnings or failures, and check why.