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

Leave a Reply

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