Domain Name Filtering using PowerDNS Resolvers

The PowerDNS Recursor is a high-performance DNS recursor with built-in scripting capabilities. The following guide outlines the deployment methodology and installation instructions for performing Domain Name filtering via DNS servers. The solution includes PowerDNS Recursor application deployed on Ubuntu 20.04 servers.The domain block list will be stored in SQLITE3 database which is small and fast SQL engine.

PowerDNS Recursor Installation

Configure the PowerDNS repositories. Create the file ‘/etc/apt/sources.list.d/pdns.list’ and add following content to the file:

deb [arch=amd64] http://repo.powerdns.com/ubuntu focal-rec-46 main

Change the package installation preference to the PowerDNS repository for the pdns packages. Create and add following content in the file /etc/apt/preferences.d/pdns:

Package: pdns-*
Pin: origin repo.powerdns.com
Pin-Priority: 600

Install the PowerDNS Recursor package. Also install sqlite3 and lua driver for sqlite3 which will be used to perform domain blocking.

$ curl https://repo.powerdns.com/FD380FBB-pub.asc | sudo apt-key add -
$ sudo apt-get update
$ sudo apt-get install pdns-recursor
$ sudo apt install sqlite3 lua-sql-sqlite3

Make the following configuration changes to the main configuration file /etc/powerdns/recursor.conf.

allow-from-file=/etc/powerdns/allowedclients

api-key=SomeApiKey

config-dir=/etc/powerdns

distributor-threads=2

hint-file=/usr/share/dns/root.hints

include-dir=/etc/powerdns/recursor.d

local-address=0.0.0.0

loglevel=6

lua-config-file=/etc/powerdns/recursor.lua

lua-dns-script=/etc/powerdns/blacklistcheck.lua

max-cache-entries=3000000

max-packetcache-entries=1000000

network-timeout=2000

pdns-distributes-queries=yes

quiet=yes

reuseport=yes

setgid=pdns

setuid=pdns

threads=4

webserver=yes

webserver-address=0.0.0.0

webserver-allow-from=127.0.0.1,192.168.10.0/27

webserver-loglevel=none

webserver-password=SomePassWord

Add the clients allowed to use the recursor DNS in the file /etc/powerdns/allowedclients:

127.0.0.0/8
10.0.0.0/8
100.64.0.0/10
192.168.0.0/16
172.16.0.0/12

Create LUA script /etc/powerdns/blocklistcheck.lua which will search the SQLITE3 database for blocked domains. If a record is found it will return TRUE with a local IP against A record. If no match is found it will return FALSE which means that standard DNS resolution process will follow.

driver = require "luasql.sqlite3"
local env = driver.sqlite3()
function preresolve ( dq )
	if dq.qtype == pdns.A then
        	local con = env:connect('/var/lib/powerdns/blocklistdomains.sqlite3',READONLY)
        	local sth = con:execute( string.format("SELECT 1 FROM domains WHERE name = '%s'", dq.qname ) )
        	if sth:fetch() then
			dq:addAnswer(pdns.A, "127.0.0.1", 300)
			sth:close()
			con:close()
                        return true;
        	end
		con:close()
	end
        return false;
end

Create the SQLITE3 database to store the blocked domains list. The following will create the database and domains table with single column, import the blocked domain list into the table and enable INDEXING on the table for fast searching. Before adding to the domain list the domain names need to be appended with a “.” at the end because the domain name passed to the LUA filter is of the format “domain.name.com.”.

$ sed 's/$/./' inputlist > domainlist
$
$ sudo mkdir /var/lib/powerdns/
$ sudo chown -R pdns:pdns /var/lib/powerdns
$ sudo sqlite3 /var/lib/powerdns/blocklistdomains.sqlite3

sqlite> create table domains (name VARCHAR(255) NOT NULL COLLATE NOCASE UNIQUE);
sqlite> .import domainslist domains
sqlite> CREATE INDEX name_index ON domains(name);

To improve the recursor performance the ROOT DNS zone can be cached on the DNS server. Edit the file /etc/powerdns/recursor.lua and add the following file:

zoneToCache(".", "url", "https://www.internic.net/domain/root.zone", { refreshPeriod = 0 })

Start the DNS server and verify that everything is working via log files.

$ sudo systemctl restart pdns-recursor
$
$ journalctl -u pdns-recursor.service -f
Dec 17 09:14:37 pdnsfilter.domain.com systemd[1]: Starting PowerDNS Recursor...
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: PowerDNS Recursor 4.6.0-rc1 (C) 2001-2021 PowerDNS.COM BV
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Using 64-bits mode. Built using gcc 9.3.0 on Dec  1 2021 20:06:03 by root@2cc4b71556fc.
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: PowerDNS comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it according to the terms of the GPL version 2.
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: If using IPv6, please raise sysctl net.ipv6.route.max_size, currently set to 4096 which is < 16384
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Enabling IPv4 transport for outgoing queries
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Done parsing 10 allow-from ranges from file '/etc/powerdns/allowedclients' - overriding 'allow-from' setting
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Will not send queries to: 127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10, 0.0.0.>
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: PowerDNS Recursor itself will distribute queries over threads
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Inserting rfc 1918 private space zones
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Not decreasing socket buffer size from 16777216 to 250000
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Listening for UDP queries on 0.0.0.0:53
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Enabled TCP data-ready filter for (slight) DoS protection
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Listening for TCP queries on 0.0.0.0:53
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Not decreasing socket buffer size from 16777216 to 250000
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Listening for UDP queries on 0.0.0.0:53
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Enabled TCP data-ready filter for (slight) DoS protection
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Listening for TCP queries on 0.0.0.0:53
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: msg="Getting zone by URL" subsystem="ztc" level=0 ts="1639714477.263" zone="."
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Launching 2 distributor threads
Dec 17 09:14:37 pdnsfilter.domain.com pdns-recursor[38907]: Launching 4 worker threads
Dec 17 09:14:37 pdnsfilter.domain.com systemd[1]: Started PowerDNS Recursor.

Updates to the DNS BlockList DB

To add domain names into the blocklist or to remove names, SQLITE3 IMPORT and DELETE functions can be used. And this does not require the DNS recursor process to restart.

To add domains the block list, create a text will with the domain names. Add a “.” at the end of each domain name.

$ sed 's/$/./' inputlist > domainlist

Create a import script file which will be passed to the SQLITE3 database.

.import /home/ubuntu/domainlist domains

Run the import command script.

$ sudo sqlite3 /var/lib/powerdns/blocklistdomains.sqlite3 < importcmd

Create a delete script which will read the domain names from the file and will delete from the database.

#!/bin/bash


DBFILE="/var/lib/powerdns/blacklistdomains.sqlite3"
DELETEFILE=”/home/ubuntu/deletelist”

sed 's/$/./' inputlist > $DELETEFILE

while read DOMAINNAME
do
	echo "Deleting $DOMAINNAME"
	echo "delete from domains where name='$DOMAINNAME'; " | sqlite3 $DBFILE
done < $DELETEFILE

References

https://doc.powerdns.com/recursor/
https://makoserver.net/articles/Lua-SQLite-Database-Tutorial

Leave a Reply