blockinator.
GitHub ↗

Integrations

Install the PowerDNS Recursor integration

Install the local Python bridge and Lua DNS hook, then test your Recursor.

Before you start

Use an existing PowerDNS Recursor on Linux with systemd and Python 3.10+. CI exercises Ubuntu 24.04’s packaged Recursor. This integration uses a Lua DNS hook plus a local Python bridge; it is for Recursor, not PowerDNS Authoritative or dnsdist.

Start Blockinator first. Use an enabled policy API key from Access & Security, not the administrator password. Use a reachable final HTTPS URL when crossing an untrusted network; these integrations reject redirects. Inside separate containers, localhost refers to each container. Keep the policy hostname resolvable independently of the resolver being integrated to avoid a DNS/policy loop.

Install the bridge files

bash
git clone https://github.com/StealthCat/blockinator-powerdns.git
cd blockinator-powerdns
sudo apt-get install python3-venv
sudo install -d -m 755 /opt/blockinator-powerdns
sudo install -m 644 bridge.py /opt/blockinator-powerdns/
sudo install -m 644 requirements.txt /opt/blockinator-powerdns/
sudo python3 -m venv /opt/blockinator-powerdns/venv
sudo /opt/blockinator-powerdns/venv/bin/pip install -r /opt/blockinator-powerdns/requirements.txt
sudo install -m 600 bridge.env.example /etc/blockinator-powerdns.env
sudoedit /etc/blockinator-powerdns.env

Alternatively, obtain the versioned bundle and checksum from a successful Build PowerDNS integration Actions run. No releases were listed when this guide was reviewed. Extract the outer artifact ZIP, check the archive with sha256sum -c, and extract it. From that directory, start at the apt-get step above. The bundle contains scripts and configuration, not a resolver binary or installed Python dependencies.

Set the policy endpoint and key

Edit the protected environment file, replacing the key with your existing enabled policy key. The example HTTP endpoint assumes Blockinator runs on this host:

env
BLOCKINATOR_ENDPOINT=http://127.0.0.1:8080/api/v1/decision
POLICY_API_KEY=REPLACE_WITH_YOUR_ENABLED_KEY
POWERDNS_SERVER_ID=powerdns-1
POWERDNS_TIMEOUT_MS=250
POWERDNS_MAX_INFLIGHT=128
POWERDNS_BRIDGE_PORT=53001

For another host, use the final HTTPS endpoint and a certificate trusted by the bridge host. TLS verification stays enabled.

Enable the bridge and install the hook

bash
sudo install -m 644 blockinator-powerdns.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now blockinator-powerdns
sudo systemctl status blockinator-powerdns
sudo install -m 644 blockinator.lua /etc/powerdns/blockinator.lua

If necessary, edit bridge and failMode at the top of /etc/powerdns/blockinator.lua. Its port must match POWERDNS_BRIDGE_PORT. Keep the bridge on loopback; do not forward it or expose it to the LAN.

Enable the Lua DNS hook

For YAML configuration, add this setting to the existing recursor section without duplicating it:

yaml
recursor:
  lua_dns_script: /etc/powerdns/blockinator.lua

For legacy recursor.conf, use:

ini
lua-dns-script=/etc/powerdns/blockinator.lua

Restart Recursor to activate the integration and clear old packet-cache entries that could bypass policy:

bash
sudo systemctl restart pdns-recursor

Verify the integration

In Blockinator, add blocked.example to a block list and assign it to the testing client’s target, or apply it globally. Run the blocked and allowed queries below, then check Query Log for the correct client, server ID, and matching list. Repeat from a client outside the blocking scope if using scoped rules.

bash
dig @RECURSOR-IP blocked.example A
dig @RECURSOR-IP blocked.example AAAA
dig +tcp @RECURSOR-IP blocked.example A
dig @RECURSOR-IP example.com A
sudo journalctl -u blockinator-powerdns --since '5 minutes ago'

Check Query Log for the client identity and powerdns-1. Restrict Recursor’s allow-from / incoming.allow_from to intended clients. NAT and DNS proxies can make several devices share one visible client address.

Failure modes, updates, and removal

The Lua failMode defaults to open, continuing normal resolution on failure. Closed returns SERVFAIL, including malformed API replies and overload. POWERDNS_TIMEOUT_MS controls the bridge HTTP deadline; a stopped bridge instead uses Recursor’s UDP follow-up timeout.

The hook disables packet-cache insertion for allowed and blocked queries to preserve scope isolation and policy changes. The underlying DNS record cache still works. Earlier RPZ actions still apply; Blockinator cannot reverse an earlier Recursor denial.

To update, preserve local Lua settings, replace bridge/hook files, install the listed dependencies, and restart both services. To remove it, remove the hook setting or restore the previous script, restart Recursor, then disable the bridge:

bash
sudo systemctl disable --now blockinator-powerdns

Read the upstream documentation ↗

Find your next step