Configuring Unbound as a simple forwarding DNS server
In part 1 of this article, I introduced you to Unbound, a great name resolution option for home labs and small network environments. We looked at what Unbound is, and we discussed how to install it. In this section, we'll work on the basic configuration of Unbound.
First find and uncomment these two entries in
interface: 0.0.0.0 interface: ::0
0 entry indicates that we'll be accepting DNS queries on all interfaces. If you have more than one interface in your server and need to manage where DNS is available, you would put the address of the interface here.
Next, we may want to control who is allowed to use our DNS server. We're going to limit access to the local subnets we're using. It's a good basic practice to be specific when we can:
Access-control: 127.0.0.0/8 allow # (allow queries from the local host) access-control: 192.168.0.0/24 allow access-control: 192.168.1.0/24 allow
We also want to add an exception for local, unsecured domains that aren't using DNSSEC validation:
Now I’m going to add my local authoritative BIND server as a stub-zone:
stub-zone: name: "forest" stub-addr: 192.168.0.220 stub-first: yes
If you want or need to use your Unbound server as an authoritative server, you can add a set of local-zone entries that look like this:
local-zone: “forest.local.” static local-data: “jupiter.forest” IN A 192.168.0.200 local-data: “callisto.forest” IN A 192.168.0.222
These can be any type of record you need locally but note again that since these are all in the main configuration file, you might want to configure them as stub zones if you need authoritative records for more than a few hosts (see above).
If you were going to use this Unbound server as an authoritative DNS server, you would also want to make sure you have a
root hints file, which is the zone file for the root DNS servers.
Get the file from InterNIC. It is easiest to download it directly where you want it. My preference is usually to go ahead and put it where the other unbound related files are in
wget https://www.internic.net/domain/named.root -O /etc/unbound/root.hints
Then add an entry to your
unbound.conf file to let Unbound know where the hints file goes:
# file to read root hints from. root-hints: "/etc/unbound/root.hints"
Finally, we want to add at least one entry that tells Unbound where to forward requests to for recursion. Note that we could forward specific domains to specific DNS servers. In this example, I'm just going to forward everything out to a couple of DNS servers on the Internet:
forward-zone: name: "." forward-addr: 188.8.131.52 forward-addr: 184.108.40.206
Now, as a sanity check, we want to run the
unbound-checkconf command, which checks the syntax of our configuration file. We then resolve any errors we find.
[root@callisto ~]# unbound-checkconf /etc/unbound/unbound_server.key: No such file or directory  unbound-checkconf[7553:0] fatal error: server-key-file: "/etc/unbound/unbound_server.key" does not exist
This error indicates that a key file which is generated at startup does not exist yet, so let's start Unbound and see what happens:
[root@callisto ~]# systemctl start unbound
With no fatal errors found, we can go ahead and make it start by default at server startup:
[root@callisto ~]# systemctl enable unbound Created symlink from /etc/systemd/system/multi-user.target.wants/unbound.service to /usr/lib/systemd/system/unbound.service.
And you should be all set. Next, let's apply some of our DNS troubleshooting skills to see if it's working correctly.
First, we need to set our DNS resolver to use the new server:
[root@showme1 ~]# nmcli con mod ext ipv4.dns 192.168.0.222 [root@showme1 ~]# systemctl restart NetworkManager [root@showme1 ~]# cat /etc/resolv.conf # Generated by NetworkManager nameserver 192.168.0.222 [root@showme1 ~]#
dig and see who we can see:
root@showme1 ~]# dig; <<>> DiG 9.11.4-P2-RedHat-9.11.4-9.P2.el7 <<>> ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36486 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;. IN NS ;; ANSWER SECTION: . 508693 IN NS i.root-servers.net. <snip>
Excellent! We are getting a response from the new server, and it's recursing us to the root domains. We don't see any errors so far. Now to check on a local host:
;; ANSWER SECTION: jupiter.forest. 5190 IN A 192.168.0.200
Great! We are getting the A record from the authoritative server back, and the IP address is correct. What about external domains?
;; ANSWER SECTION: redhat.com. 3600 IN A 220.127.116.11
Perfect! If we rerun it, will we get it from the cache?
;; ANSWER SECTION: redhat.com. 3531 IN A 18.104.22.168 ;; Query time: 0 msec ;; SERVER: 192.168.0.222#53(192.168.0.222)
Note the Query time of
0 seconds- this indicates that the answer lives on the caching server, so it wasn't necessary to go ask elsewhere. This is the main benefit of a local caching server, as we discussed earlier.
While we did not discuss some of the more advanced features that are available in Unbound, one thing that deserves mention is DNSSEC. DNSSEC is becoming a standard for DNS servers, as it provides an additional layer of protection for DNS transactions. DNSSEC establishes a trust relationship that helps prevent things like spoofing and injection attacks. It's worth looking into a bit if you are using a DNS server that faces the public even though It's beyond the scope of this article.
[ Getting started with networking? Check out the Linux networking cheat sheet. ]