Arch Linux-based Home Router, Part V (bind)
Having a caching Domain Name System (DNS) daemon is not a hard requirement for a home router, but most commercial routers have some caching DNS capabilities, as well as providing mappings between hostnames on the local network and their IP addresses. Arch Linux provides several options, but I went with the Berkeley Internet Name Daemon (BIND), as it is the one I'm most familiar with, and is also the most widely installed name server on the Internet (for UNIX/Linux systems). It comes with a BSD license, as do much of the software produced by the Internet Systems Consortium. The Arch wiki states a warning, that the ISC may not disclose zero-day vulnerabilities in BIND up to four days after they inform paying customers. Since this is for a home router, this shouldn't be too big of a deal (this will NOT be a guide for a public BIND server). If you are concerned, you can try one of the other DNS server options.
To begin, install the bind package. The actual daemon executable name is named
. Much of my configuration was created in Debian, so much of the comments in the primary files I have follow Debian instructions and follow Debian structure. Arch loads all of the configuration into one file, /etc/named.conf
. This can make it difficult to manage, but the named
binary expects the configuration to exist in this file (the systemd service file does not explicitly specify a configuration file, but it could be specified with the -c
option to ExecStart
). I merely created a symlink /etc/named.conf -> /etc/bind/named.conf
, and it has the following contents:
include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.local";
This allows for clean separation between named
global options and DNS zones (the top-level zones are in named.conf.local
).
named options
Here are the contents of the options file:
options {
directory "/var/cache/bind";
listen-on {
127.0.0.1;
10.20.30.254;
172.16.87.254;
10.11.12.254;
};
forwarders {
9.9.9.9;
8.8.8.8;
8.8.4.4;
1.1.1.1;
};
allow-query {
127.0.0.1;
10.20.30.0/24;
172.16.87.0/24;
10.11.12.0/24;
};
allow-recursion {
127.0.0.0/8;
10.20.30.0/24;
172.16.87.0/24;
10.11.12.0/24;
};
auth-nxdomain no; # conform to RFC1035
listen-on-v6 { none; };
};
logging {
category lame-servers { null; };
};
The directory
option is where named
stores its runtime files, the administrator should make sure this directory is owned and readable/writable by the named
user (the system user thenamed
daemon runs as by default in Arch). The listen-on
option is the list of IPv4 addresses corresponding to the interfaces named
should listen on (I've listed localhost
, my LAN, IoT, and WireGuard interfaces). The forwarders
are the list of DNS servers named
should consult when resolving names it doesn't know about. Logically, your ISP's name servers should be listed here by IPv4 address, you can get these from /etc/resolv.conf
when you've acquired network information from your ISP's DHCP server. When I originally set these up, Comcast/Xfinity was my ISP, and I didn't quite trust their name servers. I set up CloudFlare and Google DNS servers instead. This probably didn't shield my DNS queries from my ISP (I'd need to set up some form of encryption for my forwarded DNS queries), but it means that Comcast would have less of a say in whether I could reach their competitors (Comcast owns NBC Universal, and with Net Neutrality dying under the Trump administration, I didn't trust them serving my DNS needs). allow-query
and allow-recursion
sets the networks that can query or recurse through named
. The auth-nxdomain no;
option I don't fully understand, but it appears to be necessary to conform to RFC1035, which describes the Internet DNS protocols and data formats. Several other RFCs extend this RFC. The listen-on-v6
option is set to none;
, but if and when I decide to implement IPv6 on my home network I would change this (and other parts of this configuration).
named local configuration
The only other file referenced in the top-level named.conf
is /etc/bind/named.conf.local
, which has the following contents:
acl internals {
127.0.0.0/8;
10.20.30.0/24;
172.19.87.0/24;
10.11.12.0/24;
};
zone "ceti" {
type master;
file "/etc/bind/db.ceti";
allow-update { none; };
notify no;
};
zone "30.20.10.in-addr.arpa" {
type master;
notify no;
file "/etc/bind/db.10.20";
};
zone "87.19.172.in-addr.arpa" {
type master;
notify no9
file "/etc/bind/db.172.19";
};
The acl internals
lists the networks that are allowed to query, this should match the networks that are listed in allowed-query
in named.conf.options
. The rest are my actual DNS zones, which have their own files specified by the file
directives (I've set absolute paths to the zone files, but you can just have the basenames if the zone files are in /var/named/
according to the Arch wiki). Each file is listed as type master
, which sets this named
daemon as the master DNS name server for these domains. the *.in-addr.arpa
zones are special zones for reverse lookups, you'll notice that they have the first three octets of my LAN and IoT subnets in reverse dotted decimal order.
Zone files
Here is my /etc/bind/db.ceti
zone file:
;
; BIND data file for local loopback interface
;
$TTL 300
@ IN SOA localhost. barbican.ceti. (
20170729 ; Serial
300 ; Refresh
300 ; Retry
300 ; Expire
300 ) ; Negative Cache TTL
;
@ IN NS localhost.
@ IN A 10.20.30.254
@ IN AAAA ::1
barbican.ceti. IN A 10.20.30.254
sodium.ceti. IN A 10.20.30.11
lfce4.ceti. IN A 10.20.30.82
lfce3.ceti. IN A 10.20.30.83
lfce2.ceti. IN A 10.20.30.84
lfce.ceti. IN A 10.20.30.85
osmium.ceti. IN A 10.20.30.87
firetv.ceti. IN A 10.20.30.93
marantz.ceti. IN A 10.20.30.94
nasty.ceti. IN A 10.20.30.95
ansible.ceti. IN A 10.255.255.92
plumbum.ceti. IN A 10.255.255.93
ferrum.ceti. IN A 10.20.30.99
radon.ceti. IN A 10.20.30.77
tennessine.ceti. IN A 10.20.30.117
tennessine-idrac.ceti. IN A 10.20.30.199
I've set the default time-to-live ($TTL
) to 300 seconds (five minutes). This is probably unnecessary (the Arch wiki has this set to 7200 seconds/two hours). For the rest of the header, see the Zone file documentation. One important note: any time you edit this file, manually increment the serial number. This will tell slave named
servers to transfer the updated zone. I don't have slave DNS servers (probably unnecessary for a small home network).
This zone file I've only listed IPv4 A
records, but I could include CNAME
, MX
, TXT
, or any other valid zone record type. This zone file maps a fully-qualified domain name (FQDN) for hosts on my network, to specific IPv4 addresses. These should match the IPv4 addresses statically leased by the dhcpd
daemon (see the previous article).
The *.in-addr.arpa
zone files contain PTR
records for reverse IPv4 lookups. Rather than supplying an FQDN to tools like dig
or nslookup
, you can provide an IPv4 address and these tools will return the FQDN matching that hostname. These reverse mappings are set up in these zone files, like so:
; BIND reverse data file for local loopback interface
;
$TTL 300
@ IN SOA barbican.ceti. root.ceti. (
2017041502 ; Serial
300 ; Refresh
300 ; Retry
300 ; Expire
300 ) ; Negative Cache TTL
@ IN NS barbican.ceti.
254 IN PTR barbican.ceti.
82 IN PTR lfce4.ceti.
83 IN PTR lfce3.ceti.
84 IN PTR lfce2.ceti.
85 IN PTR lfce.ceti.
87 IN PTR osmium.ceti.
93 IN PTR firetv.ceti.
94 IN PTR marantz.ceti.
95 IN PTR nasty.ceti.
77 IN PTR radon.ceti.
117 IN PTR tennessine.ceti.
199 IN PTR tennessine-idrac.ceti.
11 IN PTR sodium.ceti.
Notice that these pointer records only have the last/fourth octect of the IPv4 addresses, these should match what's in the main domain zone file.
I also have a db.10
zone file, which looks like this:
;
; BIND reverse data file for local loopback interface
;
$TTL 7200
@ IN SOA ceti. root.ceti. (
2 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS ns.ceti.
20.30.254 IN PTR barbican
20.30.11 IN PTR sodium
20.30.77 IN PTR radon
20.30.82 IN PTR lfce4
20.30.83 IN PTR lfce3
20.30.84 IN PTR lfce2
20.30.85 IN PTR lfce
20.30.87 IN PTR osmium
20.30.93 IN PTR firetv
20.30.94 IN PTR marantz
20.30.95 IN PTR nasty
20.30.117 IN PTR tennessine
20.30.199 IN PTR tennessine-idrac
This one is probably unnecessary, since no zone in named.conf.local
refers to it. But it also shows how the DNS administrator can define the reverse lookups.
Check configuration
Before you enable and start the named
daemon, it's best to check to make sure your configuration is valid (otherwise the daemon won't start). First, check the configuration with named-checkconf
:
# named-checkconf
If this doesn't return anything (and the return code is 0
) then the configuration is OK. To check the individual zones:
# named-checkzone <zone> <zone file>
So, for my ceti
zone, I run the following:
# named-checkzone ceti /etc/bind/db.ceti
zone ceti/IN: loaded serial 20170729
OK
Likewise, I can check the reverse lookup zone:
# named-checkzone 30.20.10.in-addr.arpa /etc/bind/db.10.20
zone 30.20.10.in-addr.arpa/IN: loaded serial 2017041502
OK
Final thoughts
Again, this is only a minimal BIND configuration, that maps IPv4 addresses with predefined hosts on my network. BIND can get quite complicated, and there are the options for redundant DNS servers, delegates, and slaves, all of which are out of scope for this network. The administrator doesn't necessarily even need to install BIND on the gateway router, if they don't need to access local hosts by FQDN. However, having a local caching DNS server can speed up DNS requests tremendously for common domains, so requests don't need to go out to the wider Internet to resolve these domains after they are first queried.
NEXT STEPS
The following articles continue this series on setting up an Arch Linux-based Home Router:
- Arch Linux-based Home Router, Part I
- Arch Linux-based Home Router, Part II (systemd-networkd and sysctl/kernel)
- Arch Linux-based Home Router, Part III (firewalld configuration)
- Arch Linux-based Home Router, Part IV (dhcpd configuration)
- Arch Linux-based Home Router, Part V (bind) (This article)
- Arch Linux-based Home Router, Part VI (DDNS)
Comments ()