Based on Debian 11 "Bullseye" environment.
In short, I don't recommend managing the servers for the production environment.
DNS servers were expected to return IP addresses for the domain names when the internet life was simple. Now it should provide much more complicated information using TXT records. Furthermore, Let's Encrypt with DNS challenge automatically updates records (to be machine-readable), and DNSSEC key rotation can be (should be) automated, too. (See the automatically updated zone file example at the bottom of this article.)
The simple DNS system used to require only one or two servers, but the current system requires at least four servers.
This requirement is too much, and DNSSEC involves other servers that are working in production mode.
Now we have many simple, reasonable, and stable services for DNS with full functionality. For the production environment, I recommend using those kinds of reliable services for safety and security.
Still, learning the basics of DNS should help you understand how the internet works. So I preserve this article to explain how I set up the simple DNS servers.
I hope this will help somebody in the future.
There are some options for DNS servers. BIND is one of the most popular with the full of functionalities, which is too much for me.
NSD and Knot DNS are simple, lightweight, and reliable alternatives for BIND. Since Knot DNS can automatically take care of DNSSEC keys and signings, I chose Knot DNS this time.
(I used NSD for several years without any problem. If you want, NSD can be a good option, too.)
Here I used the official Debian package, version 3.0.5. The official documents are available here.
(Official stable releases are available from Knot official page.)
# apt install knot
Open ports for DNS service.
# firewall-cmd --add-service=dns --permanent
# firewall-cmd --reload
Update the config file: /etc/knot/knot.conf
server: rundir: "/run/knot" user: knot:knot listen: [ 0.0.0.0@53, ::@53 ] # Change IP address to accept the query from any * snip * template: - id: default storage: "/var/lib/knot" semantic-checks: on # Add this line for extra checks file: "%s.zone"
Add domain names to the "zone:" area. The default settings are already set in the "template:" section, only the domain line is required.
zone: - domain: example.jp
The zone file example is on the Knot GitLab. Set up your domain zone files according to this example.
(Please refer to the other websites for the explanation of each line.)
$ORIGIN example.com. $TTL 3600 @ SOA dns1.example.com. hostmaster.example.com. ( 2010111213 ; serial 6h ; refresh 1h ; retry 1w ; expire 1d ) ; minimum NS dns1 NS dns2 MX 10 mail dns1 A 192.0.2.1 AAAA 2001:DB8::1 dns2 A 192.0.2.2 AAAA 2001:DB8::2 mail A 192.0.2.3 AAAA 2001:DB8::3
After knot.conf and zone files are ready, restart Knot.
# systemctl restart knot
When bind-address (listen address) is changed, Knot has to be restarted.
In other cases, knotc command will work.
For the configuration (knot.conf) and zone files reload,
# knotc reload
For the zone files reload only,
# knotc zone-reload
For more information, please refer to the Knotc command help.
Check if Knot will answer the query as expected.
$ dig example.jp @localhost ; <<>> DiG 9.16.15-Debian <<>> example.jp @localhost ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27190 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ;; QUESTION SECTION: ;example.jp. IN A ;; ANSWER SECTION: example.jp. 86400 IN A 192.0.2.3 ;; Query time: 0 msec ;; SERVER: ::1#53(::1) ;; WHEN: Sat Aug 28 23:57:51 JST 2021 ;; MSG SIZE rcvd: 60
They used to be called "Master and Slave" before. Whatever the names are, it's easy to set one as the primary server that you update the zone files, the other one as the secondary server that automatically receives the updates from the primary server.
If you want more robust DNS servers, multiple secondary servers can be set up. If all of them automatically receive the latest data from the primary, you don't have to update zone files here and there.
It is possible to configure "Server A to be primary for example.com, Server B to be primary for example.net, and they send the latest zone information to each other" but you will have to struggle with automatically updated zone files in a machine-readable format.
TSIG key can be generated by the keymgr command.
# keymgr -t key01
Then you will get the generated key and configuration on the screen.
# hmac-sha256:key01:799BI/gZTo1wvoX1D8PRGcKbfQ22N4HvQeq5rpmmZV8= key: - id: key01 algorithm: hmac-sha256 secret: 799BI/gZTo1wvoX1D8PRGcKbfQ22N4HvQeq5rpmmZV8=
Copy and paste the configuration to the knot.conf.
Note: Never use the secret displayed here. Please generate your own key.
Knot will run as a primary server unless explicitly configured to be a secondary server. Add remote (secondary) server address, key, acl sections and modify template section to use them.
/etc/knot/knot.conf
key: - id: key01 algorithm: hmac-sha256 secret: 799BI/gZTo1wvoX1D8PRGcKbfQ22N4HvQeq5rpmmZV8= remote: - id: secondary address: [ 2001:db8::1, 192.168.2.0 ] key: key01 acl: - id: allow_transfer address: [ 2001:db8::1, 192.168.2.0 ] key: key01 action: transfer template: - id: default storage: "/var/lib/knot" semantic-checks: on file: "%s.zone" notify: secondary # Specify the server to send out notifications acl: allow_transfer # Apply ACL
Reload knot to apply the new configuration.
# knotc reload
After installation, zone files are not required for the secondary servers because they can get information from the primary server.
The TSIG key secret must be exactly the same as the primary.
key: - id: key01 algorithm: hmac-sha256 secret: 799BI/gZTo1wvoX1D8PRGcKbfQ22N4HvQeq5rpmmZV8= remote: - id: primary address: [ 2001:db8::1, 192.168.2.0 ] key: key01 acl: - id: allow_notify address: [ 2001:db8::1, 192.168.2.0 ] key: key01 action: notify template: - id: default storage: "/var/lib/knot" file: "%s.zone" master: primary # Specify the master server acl: allow_notify # Apply ACL
If secondary servers don't get the latest information, force retransfer.
# knotc zone-retransfer
Here are how zone files look different on the primary server and the secondary server.
On the primary server, it looks as the example introduced above.
$ORIGIN example.com. $TTL 3600 @ SOA dns1.example.com. hostmaster.example.com. ( 2010111213 ; serial 6h ; refresh 1h ; retry 1w ; expire 1d ) ; minimum NS dns1 NS dns2 MX 10 mail dns1 A 192.0.2.1 AAAA 2001:DB8::1 dns2 A 192.0.2.2 AAAA 2001:DB8::2 mail A 192.0.2.3 AAAA 2001:DB8::3
On the secondary server, each record will have complete information.
example.com. 3600 SOA dns1.example.com. hostmaster.example.com. 2010111213 21600 3600 604800 86400 example.com. 3600 NS dns1.example.com. example.com. 3600 NS dns2.example.com. example.com. 3600 MX 10 mail.example.com. dns1.example.com. 3600 A 192.0.2.1 dns1.example.com. 3600 AAAA 2001:DB8::1 dns2.example.com. 3600 A 192.0.2.2 dns2.example.com. 3600 AAAA 2001:DB8::2 mail.example.com. 3600 A 192.0.2.3 mail.example.com. 3600 AAAA 2001:DB8::3
It's still human-readable, but updating with this style requires a bit too much work. If you have long TXT records such as DomainKeys, it will be more complicated.
This is one of the reasons to determine one server as the primary to be updated manually.
BUT, the records above will help you when you use DNS services such as Amazon Route53 or Cloudflare.
2021-08-27