Setting up a Caching-only DNS Server on RHEL 7

Different packages are available on RHEL 7 to configure a caching-only DNS server. We are going to configure unbound and bind today.

We use a RHEL 7.0 virtual machine in this article. Please be advised that only one package should be deployed on a DNS server, either unbound or bind.

Catching-only Name Server with Unbound

Install the unbound package, enable the service on boot and configure firewall:

# yum install -y unbound
# systemctl enable unbound
# firewall-cmd --permanent --add-service=dns 
# firewall-cmd --reload

Prevent getting errors about server-keys that do not exist:

# unbound-control-setup

Open the file /etc/unbound/unbound.conf for editing, and add the following lines, where access control is set to our lab LAN:

interface: 0.0.0.0
access-control: 10.8.8.0/24 allow

Now, if we have some other DNS server already configured which contains some local zones (f.e. rhce.local), then we want to forward all requests for that zone to such DNS server so that they can be resolved. Since we have a FreeIPA server in place with the “rhce.local” zone, we want to add it to out unbound configuration:

forward-zone:
    name: "rhce.local"
    forward-addr: 10.8.8.70

Forward address is the IP address of our FreeIPA server. To forward all requests to another DNS server, we need to configure a forward zone for the root (.) domain.

Now, if we tried to restore the local zone, it would give us a validation failure. By default, unbound requires DNSSEC validation on all DNS responses it receives. For internal DNS domains that have not been configured with DNSSEC, we may want to bypass this.

Include the domain-insecure parameter, followed by the name of the DNS domain we don’t want to do DNSSEC validation for:

domain-insecure: "rhce.local"

Verify configuration:

# unbound-checkconf

Start the unbound service:

# systemctl start unbound

Configure the nameservers, in our particular case:

# nmcli c mod mybond0 ipv4.dns 127.0.0.1

Verify:

# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 127.0.0.1

Unbound Version Disclosure

Unbound version is returned by default:

# dig localhost version.bind chaos txt +noall +answer|tail -n1
version.bind.    0   CH   TXT     "unbound 1.4.20"

The unbound version number can be hidden by adding “hide-version: no” to the file unbound.conf.

Catching-only Name Server with Bind

Install the bind package, enable the service on boot and configure firewall:

# yum install -y bind
# systemctl enable named
# firewall-cmd --permanent --add-service=dns
# firewall-cmd --reload

Open the file /etc/named.conf for editing. Create the following access control list (acl) statement:

acl "trusted" {
    127.0.0.0/8;
    10.8.8.0/24;
};

This allows us to define groups of hosts, so that they can be permitted access to the nameserver.

Tell Bind to listen on any IPv4 address, and allow queries and transfers from the “trusted” clients.

listen-on port 53 { any; };
allow-query       { "trusted"; };
allow-transfer    { "trusted"; };
dnssec-validation no;

Now the same thing as with the unbound configuration, if we have some other DNS server already configured which contains some local zones, then we want to forward all requests for that zone to such DNS server so that they can be resolved.

Add a per-domain forwarding:

zone "rhce.local" IN {
    type forward;
    forwarders { 10.8.8.70; };
};

Check for syntax errors:

# named-checkconf

Start the service:

# systemctl enable named

Configure the nameservers, in our particular case:

# nmcli c mod mybond0 ipv4.dns 127.0.0.1

Verify:

# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 127.0.0.1

Bind Version Disclosure

Bind version is returned by default:

# dig localhost version.bind chaos txt +noall +answer|tail -n1
version.bind.   0   CH  TXT   "9.9.4-RedHat-9.9.4-14.el7"

In case there is a need to hide the version number, this can be achieved by adding a “version none” to the file named.conf.

SELinux

It doesn’t hurt to mention that depending on Bind configuration, some SELinux changes may be required.

For example, if we wanted to determine whether Bind can bind tcp socket to http ports, we would need turn on the named_tcp_bind_http_port boolean. It’s disabled by default.

# setsebool -P named_tcp_bind_http_port=1

If we wanted to determine whether Bind can write to master zone files, what is generally used for dynamic DNS or zone transfers, we would need to turn on the named_write_master_zones boolean (disabled by default):

# setsebool -P named_write_master_zones=1

Troubleshooting DNS Issues

Journal

Check journal for any obvious errors:

# journalctl -xlf

Dig

Check if DNS resolution was successful:

# dig rhce.local | grep status
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14297

The status of NXDOMAIN means that the DNS information that was requested was not found:

# dig rhce2.local | grep status
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 23674

Check what name servers are defined for the zone of interest:

# dig NS rhce.local +noall +answer

; < <>> DiG 9.9.4-RedHat-9.9.4-14.el7 < <>> NS rhce.local +noall +answer
;; global options: +cmd
rhce.local.             86392   IN      NS      ipa.rhce.local.

Find out the IPs of the name servers for the zone of interest, and check if they are available:

# dig A ipa.rhce.local +noall +answer

; < <>> DiG 9.9.4-RedHat-9.9.4-14.el7 < <>> A ipa.rhce.local +noall +answer
;; global options: +cmd
ipa.rhce.local.         443     IN      A       10.8.8.70

Check if DNS server is catching queries:

# dig A google.com +noall +stats

; < <>> DiG 9.9.4-RedHat-9.9.4-14.el7 < <>> A google.com +noall +stats
;; global options: +cmd
;; Query time: 67 msec
;; SERVER: 10.8.8.71#53(10.8.8.71)
;; WHEN: Tue May 31 11:44:15 BST 2016
;; MSG SIZE  rcvd: 55
# dig A google.com +noall +stats

; < <>> DiG 9.9.4-RedHat-9.9.4-14.el7 < <>> A google.com +noall +stats
;; global options: +cmd
;; Query time: 0 msec
;; SERVER: 10.8.8.71#53(10.8.8.71)
;; WHEN: Tue May 31 11:44:21 BST 2016
;; MSG SIZE  rcvd: 55

It sometimes may be the case that some invalid records are cached.

Unbound

Print cache to stdout:

# unbound-control dump_cache

In case some invalid records are cached, it may be required to flush everything:

# unbound-control flush_zone .
ok removed 19 rrsets, 8 messages and 3 key entries

13 thoughts on “Setting up a Caching-only DNS Server on RHEL 7

  1. Hello.
    When I configure unbound to use 8.8.8.8 as forward -addr it is OK and I can ping or dig to Internet addresses from any another server. But when I set up forward-addr as my router’s IP I have an issue. I can ping and dig without any errors from server where unbound is installed but I can’t ping or dig to the Internet’s addresses from another servers and get SERVFAIL status.
    Could you assist?
    Thanks.

  2. Great article, thanks

    just one minor thing, I think:

    The unbound version number can be hidden by adding “hide-version: no” to the file unbound.conf.

    should say:

    The unbound version number can be hidden by adding “hide-version: yes” to the file unbound.conf.

    and

    dig localhost version.bind chaos txt +noall +answer|tail -n1

    didn’t work for me so I used:

    dig localhost version.bind chaos txt +noall +answer|grep version

  3. Does linux documentation provides ready made examples for both the packages to set up cache dns ?
    like we see in smtp.

    • Bind features a range of installed documentation covering many different topics, check under /usr/share/doc/bind-VERSION_NUMBER/ to see if there is anything that you’re looking for.

  4. So I have to share issue that I faced with unbound package on RHEL 7.0
    In my environment every package was installed from ftp repository configured in yum repositories, packages are copied from original ISO file provided by RedHat(their website).
    I have installed unbound and out of the box if I execute systemctl start unbound, it fails without any message in journal – seriously completely without any sign – just says process failed, exited.
    How I managed to overcome this, install unbound, run unbound-control-setup, configured /etc/unbound/unbound.conf, after that started service with unbound-control start, after that enabled it with systemctl enable unbound and rebooted the server, after that it works. I am glad that this is fixed in later RHEL versions, but anyhow, it would be the shame to fail on the exam because of this stupid issue.

  5. In real life, what’s the interest of a caching-only DNS server ? It’s maybe too obvious for me to see any use case. Please help.

    • CentOS (and probably other Linux) does not cache DNS queries unless you have something like dnsmasq installed.

      Now, if you use a 3-Tier infrastructure, then your load balancer has to talk to the application, and the application has to talk to the backend. If you use DNS, then for each request there is a DNS lookup performed. It may not sound like a big issue, but imagine a scenario where you have say 500 requests coming in every second. That’s 500 DNS lookups per second. It can quickly add up to a noticeable delay. I worked on a project where this was a real thing.

      Using a cacthing-only DNS server can help to solve the problem (but it’s not the only solution, obviously). You should get the idea.

    • Thanks Tomas. Now I remember that 10 years ago we were using NSCD, but it had its own problems. I don’t know what became this software, and I don’t remember if it was installed by default (on CentOS 5 for example).

Leave a Reply

Your email address will not be published. Required fields are marked *