This tutorial will be showing you how to override public DNS records in your BIND DNS resolver with response policy zone (RPZ) on CentOS/RHEL.

What is Response Policy Zone?

Response policy zone (RPZ) allows a DNS resolver to modify DNS records. It was originally developed as a way to block access to dangerous websites. For example, if a computer queries the IP address of a known dangerous site that spreads malware, the DNS resolver can return 127.0.0.1 as the DNS response, so the computer can’t connect to the dangerous site. This is the original use case. As such, response policy zone is also known as DNS firewall.

You can use RPZ in other ways. For example,

  • If you have self-hosted services like Nextcloud in the local network, you can use RPZ to point your Nextcloud domain (nextcloud.your-domain.com) to the local IP address, so you don’t have to go out to the Internet and then go back to your local network to access the Nextcloud server.
  • Parents can use RPZ to block their children from accessing porn sites.
  • You can block unwanted ads.

Yes, you can create a DNS entry in the /etc/hosts file on the local computer to override public DNS records, but it doesn’t scale well. Furthermore, iOS and Android don’t allow you to create local DNS entries. Wouldn’t it be nice if the BIND DNS resolver overrides the public DNS record, so all devices in the network using the BIND resolver can use the custom DNS record?

Prerequisites

To follow this tutorial, it’s assumed that you have a BIND DNS resolver running on your CentOS/RHEL server. If not, please read the following tutorial to set up BIND resolver.

Once your BIND Resolver is up and running, follow the instructions below.

How to Set Up BIND Response Policy Zone on CentOS/RHEL Server

First, edit the named.conf file with a command-line text editor like Nano.

sudo nano /etc/named.conf

Add the following lines in the options {...} clause to enable response policy zone. (The first line is a comment.)

//enable response policy zone. 
response-policy { 
    zone "rpz.local"; 
};

Set Up Response Policy Zone (RPZ) in BIND Resolver on CentOS/RHEL centos DNS DNS Firewall Redhat Response Policy Zone

Then scroll down to the bottom of this file and add the RPZ zone in this file.

zone "rpz.local" {
    type master;
    file "db.rpz.local";
    allow-query { localhost; };
    allow-transfer { 12.34.56.78; };
};

Notes:

  • It is important that you use an absolute path instead of a simple file name in the file directive, or BIND would assume the file is in /var/cache/bind/.
  • RPZ zones should allow queries from localhost only.
  • Replace 12.34.56.78 with the IP address of the slave BIND DNS resolver, which is allowed to do zone transfer. If there’s only one DNS resolver, you can use localhost like this: allow-transfer { localhost; };

Set Up Response Policy Zone (RPZ) in BIND Resolver on CentOS/RHEL centos DNS DNS Firewall Redhat Response Policy Zone

It’s recommended to use a separate log file for RPZ to better analyze the log, so add the following lines in the logging {...}; clause.

    channel rpzlog {
  	file "https://www.linuxbabe.com/var/log/named/rpz.log" versions unlimited size 100m;
    	print-time yes;
    	print-category yes;
    	print-severity yes;
    	severity info;
    };
    category rpz { rpzlog; };

Set Up Response Policy Zone (RPZ) in BIND Resolver on CentOS/RHEL centos DNS DNS Firewall Redhat Response Policy Zone

Save and close the file. Then create the /var/log/named/ directory and make named as the owner.

sudo mkdir /var/log/named/
sudo chown named:named /var/log/named/ -R

Then we need to create the zone file. Instead of creating a zone file from scratch, we can use a zone template file. Copy the content of named.empty to a new file.

sudo cp /var/named/named.empty /var/named/rpz.local

Edit the zone file.

sudo nano /var/named/rpz.local

There is no need to change the existing content. We just add our custom DNS records. For instance, if you have a Nextcloud server on the local network with an IP address 192.168.0.103, then you add the following DNS record, so Nextcloud clients don’t have to go out to the Internet in order to connect to the Nextcloud server.

nextcloud.your-domain.com      A   192.168.0.103

If you don’t want your children to visit porn sites like pornhub.com, add the following line in this file to block the whole pornhub.com domain.

*.pornhub.com          CNAME  .

If you don’t like to see Google Adsense ads on web pages, you can add the following line to block the doubleclick.net domain, which is used to deliver Adsense Ads.

*.doubleclick.net      CNAME   .

To override the MX record for a domain name, add a line like below.

example.com         MX     0    mail.example.com.

Note that all left-hand names must NOT end with a dot and all right-hand names must end with a dot.

Set Up Response Policy Zone (RPZ) in BIND Resolver on CentOS/RHEL centos DNS DNS Firewall Redhat Response Policy Zone


Save and close the file. Next, we should set named as the group owner of the /var/named/rpz.local file, or named won’t be able to load this zone.

sudo chown root:named /var/named/rpz.local

Next, run the following command to check if there are syntax errors in the main configuration file. A silent output indicates no errors are found.

sudo named-checkconf

Then check the syntax of the RPZ zone files.

sudo named-checkzone rpz /var/named/rpz.local

If no errors are found, then restart BIND.

sudo systemctl restart named

Now you can run the dig command on the BIND server to see if RPZ is working. For example, query a DNS record of a domain name which is included in the response policy zone.

dig A nextcloud.your-domain.com @127.0.0.1

You should see something like below in the command output, which indicates the DNS response was served from local RPZ.

;; AUTHORITY SECTION:
rpz.local			86400	IN	NS	localhost.

You can also check the BIND9 query log.

sudo tail /var/log/named/rpz.log

You would see something like below, meaning the response was served from local RPZ.

(example.com): rpz QNAME Local-Data rewrite example.com via example.com.rpz.local

Using RPZ with Forwarders

If you add a fowarders directive like below in the options clause in the /etc/named.conf file, then your BIND resolver becomes a forwarder, which will forward DNS requests to an upstream DNS resolver like 8.8.8.8.

options {
//      listen-on port 53 { 127.0.0.1; };
//      listen-on-v6 port 53 { ::1; };
        directory       "https://www.linuxbabe.com/var/named";
        max-cache-size 100m;
        dump-file       "https://www.linuxbabe.com/var/named/data/cache_dump.db";
        statistics-file "https://www.linuxbabe.com/var/named/data/named_stats.txt";
        memstatistics-file "https://www.linuxbabe.com/var/named/data/named_mem_stats.txt";
        secroots-file   "https://www.linuxbabe.com/var/named/data/named.secroots";
        recursing-file  "https://www.linuxbabe.com/var/named/data/named.recursing";
        allow-query     { localhost; 10.10.60.0/24;};

        //enable response policy zone. 
        response-policy { 
           zone "rpz.local"; 
        };

        forwarders {
                8.8.8.8;
                8.8.4.4;
        };
        ...
};

Response policy zone works with this forwarder setup. Bind will query the local response policy zone first. If DNS record is not found in the RPZ, then the request will be forwarded to an upstream DNS resolver.

Configure Zone Transfer

If you have another BIND DNS resolver, you can configure it as a slave resolver to automatically receive updates from the master DNS resolver.

First, you need to edit the /etc/named.conf file on the master DNS resolver.

sudo nano /etc/named.conf

Add the IP address of the slave DNS resolver to the allow-transfer directive.

zone "rpz.local" {
    type master;
    file "https://www.linuxbabe.com/etc/bind/db.rpz.local";
    allow-query { localhost; };
    allow-transfer { 12.34.56.78; };
    also-notify { 12.34.56.78; };
};

If you have multiple slave DNS resolver, then add multiple IP addresses like below.

 allow-transfer { 12.34.56.78; 12.34.56.79; };

The also-notify directive will make the master DNS resolver send a notification message to the slave resolver when the RPZ zone is changed. Save and close the file. Restart BIND for the changes to take effect.

sudo systemctl restart named

If there’s a firewall running on the master DNS resolver, you need to allow the slave DNS resolver to connect to port 53.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="12.34.56.78" accept'
sudo systemctl reload firewalld

Next, edit the /etc/named.conf file on the slave DNS resolver.

sudo nano /etc/named.conf

Add the following lines in the options {...} clause to enable response policy zone. (The first line is a comment.)

//enable response policy zone. 
response-policy { 
    zone "rpz.local"; 
};

Then add a slave RPZ zone at the end of this file. Replace 11.22.33.44 with the IP address of the master DNS resolver.

zone "rpz.local" {
    type slave;
    file "rpz.local";
    masters { 11.22.33.44;};
    allow-notify { 11.22.33.44; };
    allow-transfer { none; };
    allow-query { localhost; };
};

Save and close the file.

You also need to configure the slave resolver’s firewall to allow the master DNS resolver to send notify messages.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="11.22.33.44" accept'
sudo systemctl reload firewalld

Next, run the following command to check if there are syntax errors in the main configuration file. A silent output indicates no errors are found.

sudo named-checkconf

If no errors are found, then restart BIND.

sudo systemctl restart named

After BIND restarts, zone tranfer will start immediately. Check the BIND9 log with the following command.

sudo journalctl -eu named

You can see messages like below, which indicates the zone transfer is successful.

transfer of 'rpz.local/IN' from xx.xx.xx.xx#53: Transfer status: success
transfer of 'rpz.local/IN' from xx.xx.xx.xx#53: Transfer completed: 1 messages, 34 records, 899 bytes, 0.248 secs (3625 bytes/sec)

The zone file will be saved as /var/named/rpz.local on the slave resolver.

Note: Whenever you modify the RPZ zone on the master resolver, you need to update the serial number. Make it bigger, so that slave resolvers know the RPZ zone is changed.

Creating Multiple RPZ Zones

Sometimes you might not want certain DNS records to be transferred to slave resolvers. You can create a separate RPZ zone. Edit the /etc/named.conf file.

sudo nano /etcnamed.conf

Add a new RPZ zone.

//enable response policy zone. 
response-policy { 
    zone "rpz.local";
    zone "rpz.local.notransfer"; 
};

Add a definition for the new zone at the bottom of this file.

zone "rpz.local.notransfer" {
    type master;
    file "https://www.linuxbabe.com/var/named/rpz.local.notransfer";
    allow-query { localhost; };
    allow-transfer { localhost; };
};

Save and close the file. Then we need to create the zone file. Instead of creating a zone file from scratch, we can use a zone template file. Copy the content of named.empty to a new file.

sudo cp /var/named/named.empty /var/named/rpz.local.notransfer

Edit the zone file.

sudo nano /var/named/rpz.local.notransfer

Wrapping Up

I hope this tutorial helped you set up response policy zone/DNS firewall on CentOS/RHEL. As always, if you found this post useful, then subscribe to our free newsletter to get more tips and tricks. Take care 🙂

Rate this tutorial

[Total: 0 Average: 0]