Photo credit: Michael Henzler
Wikimedia Commons, CC BY-SA 4.0
The Raspberry Pi for basic Networking services series:
In previous articles, I described how to set up a Raspberry Pi computer to act as a DHCP server and how to configure it to serve static IP addresses. But this is only half of the solution.
If all your computers are only used to access Internet sites, then you may not need anything more, but if you run any services on your LAN, you will want to access them as well as Internet services. Some examples of services I run on my LAN include:
- Printer. My printer has a built-in Ethernet interface and acts as a print server for the entire LAN.
- Web server. Two of my computers are running web server software. I use these for documents that I want to make available to all the computers in my home. In the past, when I ran an Internet-accessible web site, I used my local web servers as a staging area for changes before uploading them to the main server.
- File servers. Several of my computers have file sharing enabled so I can copy files between my computers.
- Remote access. Several computers run different remote access tools including SSH, VNC, X2Go and others. This lets me access everything from anywhere in the house.
When accessing a remote service, you need to provide the machine's name or IP address. If you've got a small LAN, you can just type in the address. Or you can set up hosts files on your computers to assign names to the addresses. But once you get more than a trivial number of devices on your network, those two solutions quickly become unmanageable. As I wrote in part 1, I've got 26 active devices on my LAN and many more that are rarely used. Using hosts files really doesn't work because every computer needs to be updated whenever the content changes and some devices (especially embedded devices like set-top boxes and mobile phones) don't even have a hosts file that can be managed.
But there is a well-established solution to the problem. The solution is to use the DNS protocol.
History of DNS
The problem with managing hosts files goes back to the earliest days of computer networking. Originally, the universities and research labs working on the ARPANET maintained a master hosts file mapping names to addresses. Whenever the file changed, it would be distributed to all of the attached computers. But the engineers at the time quickly realized that this is slow and difficult to manage, and the problem gets worse as the network gets bigger. An automated solution that doesn't require constant reconfiguring of computers was needed.
The solution was to design and implement a "name server". One computer that maintains a master list of hosts that all other computers can access. And a system allowing this server to distribute its database to other computers, so it doesn't become a bottleneck. The first such name server was developed in 1984 at Berkeley University. The software was called the Berkeley Internet Name Domain, usually abbreviated to BIND.
The protocol used by BIND was standardized and became an Internet standard known as the Domain Name System (DNS). Today, DNS is used almost universally throughout the Internet and is defined by a large set of RFC documents.
And the BIND server software is still being used today. It is up to its ninth major revision and is currently maintained by the Internet Systems Consortium. It is freely available using an open source license. Nearly all Unix distributions (including BSD and Linux) include BIND (sometimes in addition to other alternative DNS software packages), so it is usually easy to download and install.
Installing BIND
A I previously wrote, most Unix software distributions include BIND and Raspberry Pi's Raspbian distribution is no different. The following command will install all the necessary packages:
$ sudo apt install bind9 bind9-doc bind9-host bind9utils dnsutils
A brief description of these packages is:
Package | Description |
---|---|
bind9 | The BIND software (version 9), including a default configuration. The version distributed with Raspbian at this time is 9.11.5. |
bind9-doc | The documentation. Not strictly necessary, but you should keep it on hand in case you need to check something when you can't access the Internet. Which could happen if your DNS server isn't working properly. |
bind9-host | The host command for making DNS queries. A useful diagnostic tool. |
bind9utils | Various utilities for configuring and testing BIND |
dnsutils | Utilities for testing DNS, including the dig and nslookup commands. |
Configuring BIND - basic operation
In its default configuration, BIND runs as a simple caching name server. It accesses the Internet's root servers to resolve names and caches the results locally in order to improve performance for names that are frequently used. This is good even if you don't do anything else, because it means the computers on your network don't have to access your Internet service provider's DNS servers as often.
To make it do anything else requires configuration.
With the Raspbian distribution, all of BIND's configuration files are stored in the /etc/bind directory. For basic configuration, you want to edit the file /etc/bind/named.conf.options. The file I'm using looks a lot like:
options { directory "/var/cache/bind"; // If there is a firewall between you and nameservers you want // to talk to, you may need to fix the firewall to allow multiple // ports to talk. See http://www.kb.cert.org/vuls/id/800113 // If your ISP provided one or more IP addresses for stable // nameservers, you probably want to use them as forwarders. // Uncomment the following block, and insert the addresses replacing // the all-0's placeholder. forwarders { 8.8.8.8; 8.8.4.4; }; //======================================================================== // If BIND logs error messages about the root key being expired, // you will need to update your keys. See https://www.isc.org/bind-keys //======================================================================== dnssec-enable yes; dnssec-validation auto; auth-nxdomain no; # conform to RFC1035 listen-on-v6 { any; }; };
A description of the above follows:
Command | Description |
---|---|
directory | This command specifies the working directory where BIND runs. It is also the location where BIND stores its working files (e.g. the self-generated authentication keys used for DNS security.) The value I'm using /var/cache/bind is the default location. Don't change it unless you have an explicit need to do so. |
forwarders | By default, BIND resolves all names by accessing the Internet's well-known root servers and then using various lower-level servers as necessary. This works great and you can very successfully run BIND in this configuration. But doing so can create a significant load on BIND, especially if it sees a lot of activity or if the CPU is low powered (as is the case for a Raspberry Pi).
The forwarders command tells BIND to use a specific set of DNS servers to resolve addresses instead of accessing the root servers. Typically you will use the IP addresses of your Internet service provider's DNS servers. In this example, I am using Google's public DNS servers because I don't think my ISP's DNS servers are reliable enough. |
dnssec-enable | When set to "yes", BIND will return DNSSEC (secure DNS information) with all responses. Otherwise, it only returns this information when explicitly requested. Although the default is "yes", I explicitly configure it here as a reminder that it is enabled. |
dnssec-validation | This configures the use of DNSSEC data. DNSSEC validation allows BIND to detect and reject fake DNS responses (e.g. if an attacker is spoofing a public DNS server you need to access). Allowable values are:
|
auth-nxdomain | If set to "yes", BIND will report a NXDOMAIN (no such domain) error as authoratative even if the server generating the error is not authoratative. This should only be turned on if you need to be backward compatible with very old DNS applications that require it. The default is "no" and is typically part of the default configuration file. Do not set it to "yes" unless you know you need to. |
listen-on-v6 | This defines the interfaces and ports that BIND will use for clients that use IPv6 for DNS requests. The "any" keyword means it will use the standard port on any IPv6 interface. |
There are many more options, but I am not using them, which means BIND uses the default options for them (or they are overridden in the zone-configuration files, see below).
Adding a zone
So far, we have configured BIND, but we haven't added any information describing our network. This means it can only return responses that it gets from the Internet. But you want it to resolve the names of the computers on your LAN as well. For that, you need to define a zone.
Choosing a domain name
The first step in configuring a zone is knowing its domain name. Domain names used on the Internet must be publicly registered. If your domain is not going to be accessible from the Internet, then you can use any name you like, but care should be taken to ensure that your name is not used by anybody else on the Internet. If you pick a domain that is in use by someone else, you will probably be unable to access their network (e.g. their web pages) and may encounter other problems as well.
If you are going to pick your own domain name (and not register one), you may want to consult the list of top-level domains and pick something that is not on it (perhaps your name).
IMPORTANTYou should not use the "local" top-level domain name. Although officially designated for hostnames that are only accessible from the local network, Apple uses "local" for resolving multicast-DNS (via its Bonjour protocol). If you have Apple equipment on your LAN, use of "local" for any other purposes will create problems.
In this document I am using the domain "example.com". This is a domain guaranteed to never be used on the Internet. You should not use it either, however, since it is reserved for documentation purposes.
Creating the forward-DNS zone file
A forward-DNS zone file is used to map host names onto IP addresses. It is common practice to use a separate zone file for each domain, with the file name consisting of "db." followed by the domain name, located in the same directory where BIND's other configuration files are located (in my case, /etc/bind).
The first step in creating a zone is to add it to BIND's configuration file. You should edit the local configuration file (/etc/bind/named.conf.local) and add a zone object to it. For example:
zone "mynetwork.example.com" IN { type master; file "/etc/bind/db.mynetwork.example.com"; };
A description of the above follows:
Line | Description |
---|---|
zone | The first line indicates the start of a zone-description. The domain name (aka zone name) should appear between the double-quotes. The "IN" suffix indicates that this is an IP-based domain. (DNS can serve addresses for other kinds of networks, but that is an advanced usage you probably don't have to care about.) |
type | "master" indicates that this is the master copy of the Zone data. For a simple domain with only one DNS server, this is what you want to use. |
file | Location of the zone file. In this case, it is /etc/bind/db.mynetwork.example.com. |
Once we have told BIND about the zone and where to find the zone data, we need to create the zone data file itself. It contains some generic housekeeping data and information about all of the hosts that belong to the domain. For example:
; BIND data file for the mynetwork.example.com zone ; $TTL 604800 @ IN SOA pi.mynetwork.example.com. myemail.example.com. ( 2020092401 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL ; IN NS pi.mynetwork.example.com. ; myrouter IN A 192.168.1.1 IN TXT "My router and cable modem" phredd IN A 192.168.1.2 IN TXT "My Windows 10 system" pi IN A 192.168.1.3 IN TXT "My Raspberry Pi server" frankenstein IN A 192.168.1.4 IN TXT "My Linux server" lappy IN A 192.168.1.5 IN TXT "My Mac laptop" compy IN A 192.168.1.6 IN TXT "My Mac desktop" hplaserjet IN A 192.168.1.7 IN TXT "My printer" modem IN CNAME myrouter ns IN CNAME pi printer IN CNAME hplaserjet router IN CNAME myrouter www IN CNAME frankenstein
In the above examples, I have sorted the records by IP address. You may find it more convenient (especially when you need to read a large file) to sort them by host name. BIND does not care about the order the records appear (but configuration directives like $TTL only affect records that appear later in the file.)
A description of the above follows:
Line | Description |
---|---|
Comments | A semicolon (;) character indicates the start of a comment. It and all text following it on that line are ignored by the BIND software. |
$TTL | This line specifies the "time to live" for entries served by this zone file. The TTL is returned as a part of DNS response. It is the maximum amount of time (in seconds) that other hosts and DNS servers are allowed to cache the response before they must ask again. This example is using 604800 seconds, which is 7 days. The TTL can be configured on a per-host basis, but it is usually easier to just configure it once for the entire zone file. |
IN SOA | The "Statement Of Authority" record. It contains global information about the zone that can be used by hosts and other DNS servers. It consists of the following data (two strings and five numbers):
|
IN NS | A "Name Server" record. This specifies the host name of a DNS server that is authoritative for the zone's domain. In this case, "pi.mynetwork.example.com." is used. Again, be aware of the trailing period, meaning that this is a complete host name and should not have anything appended to it. There can be multiple NS records if there are multiple DNS servers for the domain. (Internet-accessible domains are required to have at least two authoritative DNS servers, but you only need one if it's just for your own use at home.)
Note that there is no name to the left of the IN NS record name. This means the name inherits from the previous record (the IN SOA record), which is "@" (the domain name, mynetwork.example.com). |
IN A | An IPv4 "Address" record. This consists of a host-name (to the left of the "IN A" text) and an IPv4 address. For example, the following line:
associates the IP address 192.168.1.1 with the host-name "myrouter.mynetwork.example.com".myrouter IN A 192.168.1.1 Note that the host name does not have a trailing period. This is because it is not a fully qualified host name. The zone's domain name will be appended to the name provided here. |
IN TXT | A "TeXT" record. This contains arbitrary text data associated with a host name. It is optional, but a convenient way to provide a description of each host name, so users can identify the host using DNS. It consists of a host-name (to the left of the "IN TXT" text) and a text string. If the host name is missing, then it will use the name of the most recently specified host. For example, in the following lines:
The first line (the "A" record) defines an IP address for the "myrouter" host. The second line (the "TXT" record) does not include a host name so it uses the last host name processed ("myrouter.mynetwork.example.com").myrouter IN A 192.168.1.1 IN TXT "My router and cable modem" By not specifying the host name for every line, it is very easy to see when a single host name has multiple records (in this case, an "A" record and a "TXT" record) associated with it. |
IN CNAME | A "Canonical NAME" record. This allows you to create host names that are actually aliases for other host names. For example, in the following line:
The host name "www.mynetwork.example.com" is an alias pointing to "frankenstein.mynetwork.example.com". The host names used do not have trailing periods, so the zone's domain name is appended to them.www IN CNAME frankenstein Use of CNAME records allows you to use function-specific names (e.g. "www", "printer", "router", "ns" (name server)), without that being the device's host name. This allows multi-function devices (e.g. a server that is running a DNS server, a web server and a mail server) to have multiple function-specific names. It also allows you to change the host to which these aliases refer without changing the host's actual configuration. |
There are many more types of records that a Zone file can include, but the ones presented here (SOA, NS, A, TXT and CNAME) are by far the most common. Some others that you might find useful (but will not be discussed here) include:
- AAAA. An IPv6 address record.
- MX. A "Mail eXchange: record. Identifies an e-mail server that should handle mail addressed to the host (or the zone's entire domain).
- PTR. A "Pointer" record. Points to another part of the domain's name-space. We will see these as a part of the reverse-DNS zone file (see below)
Creating the reverse-DNS zone file
At this point, if you stop here, you will have a working DNS server capable of returning the IP address associated with every host name in your domain's zone file, but you shouldn't stop here. There is also a "reverse DNS" feature where hosts (and DNS servers) can provide an IP address in order to receive the host-name associated with that address.
In addition to being convenient, some security software packages use Forward-confirmed Reverse DNS as a simple (and weak) way to validate a network host. A server, wishing to validate a host will request the reverse DNS record for its IP address (producing a host name) and then perform a forward DNS request on that host name to confirm that the same IP address is returned. If reverse DNS records are unavailable (or incorrect), then the validation will fail, often resulting in refused access to the server in question.
So, in addition to creating a forward-DNS zone for your host-names, you should also create a reverse-DNS zone for those hosts' IP addresses.
The procedure for creating a reverse-DNS zone is almost the same as a forward-DNS zone. You create a zone whose domain name consists of the network's IP subnet prefix (in reverse order) followed by the special domain, "in-addr.arpa".
In the example above, all of the host names belong to the 192.168.1 network. To create a reverse-DNS zone for this IP prefix, add the following lines to your BIND local configuration file (/etc/bind/named.conf.local):
zone "1.168.192.in-addr.apra" IN { type master; file "/etc/bind/db.192.168.1"; };
A description of the above follows:
Line | Description |
---|---|
zone | The first line indicates the start of a zone-description. This is almost the same as for a forward-DNS zone, but note the domain name used. The IP prefix is reversed (192.168.1 becomes 1.168.192) and it is suffixed with ".in-addr.arpa" |
type | Zone type. Just like with a forward-DNS zone, "master" indicates that this is the master copy of the Zone data. For a simple domain with only one DNS server, this is what you want to use. |
file | Location of the zone file. In this case, it is /etc/bind/db.192.168.1. |
Once we have told BIND about the reverse-DNS zone and where to find the zone data, we need to create the zone data file itself. It is very similar to the forward-DNS zone, but mainly consists of PTR records. For example:
; BIND reverse data file for the 192.168.1/24 subnet ; $TTL 604800 @ IN SOA pi.mynetwork.example.com. myemail.example.com. ( 2020092401 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL ; IN NS pi.mynetwork.example.com. ; 1 IN PTR myrouter.mynetwork.example.com. 2 IN PTR phredd.mynetwork.example.com. 3 IN PTR pi.mynetwork.example.com. 4 IN PTR frankenstein.mynetwork.example.com. 5 IN PTR lappy.mynetwork.example.com. 6 IN PTR compy.mynetwork.example.com. 7 IN PTR hplaserjet.mynetwork.example.com.
A description of the above follows:
Line | Description |
---|---|
Comments | A semicolon (;) character indicates the start of a comment. Same as with a forward-DNS zone file. |
$TTL | This line specifies the "time to live" for entries served by this zone file. Same as a forward-DNS zone file. |
IN SOA | The "Statement Of Authority" record. Same as a forward-DNS zone file.
Note that the "@" character in the leftmost column (the zone's domain name) inherits from the zone directive that loads this configuration file. In this example, it will resolve to the reverse-DNS zone name, 1.168.192.in-addr.apra. |
IN NS | A "Name Server" record. Same as a forward-DNS zone file. |
IN PTR | An "Pointer" record. This is a back-pointer from the IP address to the canonical host name for the computer at that address. It consists of a reverse-DNS style host name (to the left of the "IN PTR" text) and a fully-qualified host name. For example, the following line:
associates the reverse-DNS host name 1.1.168.192.in-addr.arpa (referring to the IP address 192.168.1.1) with the host-name "myrouter.mynetwork.example.com".1 IN PTR myrouter.mynetwork.example.com. Some important things to note here:
|
Starting the server and debugging
Validate the configuration
Once you have all of the configuration files completed, you should use the named-checkconf and named-checkzone utilities to quickly validate the syntax of the files. These tools won't prove that the files do what you want, but it will alert you to any syntax errors you may have introduced when creating/editing them:
$ named-checkconf $ named-checkconf -z zone mynetwork.example.com/IN: loaded serial 2020092401 zone 1.168.192.in-addr.arpa/IN: loaded serial 2020092401 zone localhost/IN: loaded serial 2 zone 127.in-addr.arpa/IN: loaded serial 1 zone 0.in-addr.arpa/IN: loaded serial 1 zone 255.in-addr.arpa/IN: loaded serial 1 $ named-checkzone mynetwork.example.com /etc/bind/db.mynetwork.example.com zone mynetwork.example.com/IN: loaded serial 2020092401 OK $ named-checkzone 1.168.192.in-addr.arpa /etc/bind/db.192.168.1 zone 1.168.192.in-addr.arpa/IN: loaded serial 2020092401 OK
In the above example:
- The first command (named-checkconf) performs a syntax check of BIND's configuration files. If no errors are found, you will not see any output.
- The second command (named-checkconf -z) tries to load all of the configured zones. You should confirm that the list of zones is correct (note the presence of the default "localhost" zone and the reverse-DNS zones for the 127 (loopback), 0 (null) and 255 (broadcast) networks).
- The third command (named-checkzone mynetwork.example.com /etc/bind/db.mynetwork.example.com) checks the forward-DNS zone file for the mynetwork.example.com domain.
- The fourth command (named-checkzone 1.168.192.in-addr.arpa /etc/bind/db.192.168.1) checks the reverse-DNS zone file for the 192.168.1 network.
In the future, when you need to change your configuration and zone files, it is a good idea to make copies of the files, edit the copies, and then validate them using these tools. Once you know they are valid, you can replace the running configuration files with them. This will ensure that if you accidentally introduced a syntax error, you won't halt the DNS service (which can break your network) while you fix the error.
Start (or restart) BIND, and enable it for auto-restart
With the configuration and zone file in place, you can start (or restart, if it's already running) the BIND service. On a Raspberry Pi, this is done with one of the following commands:
$ sudo systemctl start bind9 $ sudo systemctl restart bind9
If there are no errors, you will not see any output. BIND will just start running. If you would like confirmation, you can see its output in the system log:
$ cat /var/log/syslog ... Sep 24 13:38:12 pi systemd[1]: Starting BIND Domain Name Server... Sep 24 13:38:12 pi named[2004]: starting BIND 9.11.5-P4-5.1+deb10u2-Raspbian (Extended Support Version) <id:998753c> Sep 24 13:38:12 pi named[2004]: running on Linux armv7l 5.4.51-v7+ #1333 SMP Mon Aug 10 16:45:19 BST 2020 ... Sep 24 13:38:12 pi named[2004]: using default UDP/IPv4 port range: [32768, 60999] Sep 24 13:38:12 pi named[2004]: using default UDP/IPv6 port range: [32768, 60999] Sep 24 13:38:12 pi named[2004]: listening on IPv6 interfaces, port 53 Sep 24 13:38:12 pi named[2004]: listening on IPv4 interface lo, 127.0.0.1#53 Sep 24 13:38:12 pi named[2004]: listening on IPv4 interface eth0, 192.168.1.15#53 ... Sep 24 13:38:12 pi named[2004]: zone 0.in-addr.arpa/IN: loaded serial 1 Sep 24 13:38:12 pi named[2004]: zone localhost/IN: loaded serial 2 Sep 24 13:38:12 pi named[2004]: zone 127.in-addr.arpa/IN: loaded serial 1 Sep 24 13:38:12 pi named[2004]: zone 1.168.192.in-addr.arpa/IN: loaded serial 2020092401 Sep 24 13:38:12 pi named[2004]: zone 255.in-addr.arpa/IN: loaded serial 1 Sep 24 13:38:12 pi named[2004]: zone mynetwork.example.com/IN: loaded serial 2020092401 Sep 24 13:38:12 pi named[2004]: all zones loaded Sep 24 13:38:12 pi named[2004]: running Sep 24 13:38:12 pi systemd[1]: Started BIND Domain Name Server. ...
You can also confirm that is running with the systemctl status command:
$ systemctl status bind9 ● bind9.service - BIND Domain Name Server Loaded: loaded (/lib/systemd/system/bind9.service; enabled; vendor preset: enabled) Active: active (running) since Thu 2020-09-24 13:38:12 EDT; 16min ago Docs: man:named(8) Process: 2003 ExecStart=/usr/sbin/named $OPTIONS (code=exited, status=0/SUCCESS) Main PID: 2004 (named) Tasks: 7 (limit: 2065) CGroup: /system.slice/bind9.service └─2004 /usr/sbin/named -u bind Sep 24 13:53:14 pi.mynetwork.example.com named[2004]: resolver priming query complete ...
Finally, you want to enable BIND so it will automatically restart when you reboot your Raspberry Pi:
$ sudo systemctl enable bind9 Synchronizing state of bind9.service with SysV service script with /lib/systemd/systemd-sysv-install. Executing: /lib/systemd/systemd-sysv-install enable bind9 $ systemctl is-enabled bind9 enabled
And you're good to go. At this point, configure your computers to use your Raspberry Pi's IP address as the primary (or only) DNS server. For devices that get their configuration using DHCP, configure the DHCP server to distribute the Raspberry Pi's address as your network's primary (or only) DNS server,
References
The following web pages have been useful for me when installing and configuring BIND:
- The Urban Penguin: Raspberry Pi DNS Server. A basic getting-started tutorial.
- BIND 9 Administrator Reference Manual. This link is for version 9.11.5 - the version that Raspbian currently distributes.
- BIND 9.11.5 configuration reference
No comments:
Post a Comment