Atom Feed
Comments Atom Feed


Recent Articles

23/04/2017 14:21
Raspberry Pi SD Card Test
07/04/2017 10:54
DNS Firewall (blackhole malicious, like Pi-hole) with bind9
28/03/2017 13:07
Kubernetes to learn Part 4
23/03/2017 16:09
Kubernetes to learn Part 3
21/03/2017 15:18
Kubernetes to learn Part 2

Glen Pitt-Pladdy :: Blog

DNS Firewall (blackhole malicious, like Pi-hole) with bind9

After listening to Security Weekly #507 where Malvertising and Pi-hole was discussed. While blacklisting approaches have limited benefit, and should not be applied without the endpoint owner's consent (they can badly break stuff), this does none the less reduce exposure to malicious actors as well as allowing mitigation accidental access to undesirable sites.

I was disappointed this relied on dnsmasq and wondered if it wouldn't be possible to the same with bind9. Fortunately it turned out to be rather easy thanks to RPZ available since bind 9.8, so this is how I did it....

Maintaining RPZ zone file

This is essentially the blacklist, or Response Policy Zone (RPZ) file which tells bind what to do when it gets a query for a name or pattern we want to blackhole.

It took an hour or so to fish out a bunch of blacklist URLs from the Pi-hole source, figure out the formats and put together a Python script to maintain an RPZ zone file. The aim is that this will be able to understand multiple formats so as other sources become available we can update accordingly. It also caches files so that we don't have to keep re-fetching which is a pain while developing and testing.

From there I've built in a bunch of refinements including a config file, support for extra local blacklist entries and other tricks.


A useful feature of bind is that it has an XML via http monitoring/statistics interface that can be enabled This needs the following adding to the configuration (/etc/bind/named.conf.options on Debian/Ubuntu and derived distros) to enable the service:

statistics-channels {
    inet port 5380 allow {; };

Where 5380 is the port it will listen on. Many examples use 8080 as the port, but since that's a popular one for proxys and application servers, I've opted for a different one to avoid conflicts.

If you make a request from you will get an XML file of all the internal stats within bind.

Within that XML file there are a bunch of stats (follow the tags) that represent RPZ related counters:

  • server.nsstat.RPZRewrites
  • for each zone
  •* for the RPZ zone

The problem I have is that apart from the first one, all of these seem to return 0 so I'm guessing only the first one is useful for monitoring.

I am working on Cacti templates for monitoring bind, but since the volume of stats is so large and there are significant differences between formats of minor versions, don't expect to see anything soon.


To get this working you need the config file /etc/bind/py-hole-bind9RPZ_config.yaml which you can use the template to start off with. The options should be straight forward in the comments of the file. You need to ensure that the rpzfile, rpztemplate (provides the header to the zone file) and reloadzonecommand (list of argements) match your configuration. The remainder are things you can tweak.

There are two source formats supported:

  • raw - this is simply a list of hosts, one per line, and we assume lines starting with "#" are comments.
  • hosts - this is the standard /etc/hosts format, and here we need a hostkey specifier to match the IP from the hosts file that blacklisted hosts use.

Once you have a config file then you can run the Python script. This will download the source lists to a cache location (specified in the config or defaulting to /var/local/bindRPZ) and process them into your output file specified by rpzfile in the config.

# ./py-hole-bind9RPZ

Check the output file is as you expect it to be, tweak the config and re-run as you need to get this working.

In particular, I would recommend that you review the blacklist sources since these could have impact on your network if they block useful hosts, or worse, if a malicious actor got control and was able to inject their own content.

When you're ready to start blocking, then edit the zones list (/etc/bind/named.conf.local on Debian/Ubuntu and derived distros) and add the zone matching your configuration:

zone "" {
        type master;
        file "/etc/bind/";

Then in the options section (/etc/bind/named.conf.options on Debian/Ubuntu and derived distros) add the RPZ configuration:

    response-policy {zone "";};

Then reload bind, check the logs and you should have the domains in the list being blocked.

To disable this again, simply remove or comment what you added to the bind configuration and you can also delete the RPZ zone file, config, cache files etc.

For regular updates, put the Python script py-hole-bind9RPZ in an appropriate cron directory (eg. /etc/cron.weekly/) to keep the records up to date. When it runs it should automatically run your configured command to reload the zone after the update.

Bonus - DNS firewall for roadrunners

While having a DNS firewall on a network (via Pi-hole or bind RPZ) is a good idea, portable devices like laptops often roam networks and hence are out of band a lot of the time. To deal with this I've made a parallel variant of the script that works with dnsmasq on Laptops / portables running Mint, Ubuntu and others which use dnsmasq with NetworkManager. This allows you to take your DNS firewall setup with a very similar config file and use it when you're on the road.

The one unsolved problem in this particular case is that I've not been able to find any reliable way to get dnsmasq to reload it's configuration, nor restart it when it's managed by NetworkManager. Updating the hosts file(s) is possible, but that's all.

This follows a very similar setup to above with the config file /etc/py-hole-dnsmasq_config.yaml and the output files will be configured by hostsfile and dnsmasqblackholeconfig which will be created by the script. Follow the same configuration process as for the RPZ version above otherwise, paying particular attention to the blacklist sources which could impact availability of services.

Similar caching setup exists, and once configured you should be able to run the Python script py-hole-dnsmasq which will do all the setup:

# ./py-hole-dnsmasq

Again, do all the checks on the output files. Due to the problems reloading dnsmasq, the sure way to start this working you can either reboot, or manually restarting NetworkManager might do the trick. After the configuration has been read by dnsmasq, sending it a SIGHUP should cause it to re-read the hosts file(s) after you run updates.

Similarly, to above, if you want to remove this, delete the output files, config, Python script and the cache files.


You can get this from GitHub in my Py-hole repository.

Blacklists can be found around the web in different locations, but you probably want to spend some time understanding the quality/reliability and security implications of different lists. I've started off with some from the Pi-hole repository (see above), but these should be taken more as an example then being suited to the needs of your network.



Are you human? (reduces spam)
Note: Identity details will be stored in a cookie. Posts may not appear immediately