Thanks Vladimir,
I read your responses with interest :-)
I will take a look at kdig and attempt to reproduce the behaviour
experienced with dig.
On the zone transfers, if you ever add this functionality, the
resolver needs to answer SOA queries per RPZ zone, as ISPs need to
check zone freshness / consistency, comparing the serial numbers
across multiple platforms / servers in different regions / etc.
Thanks for explaining the current RPZ logic and compromise for use
with the version on the jezek-test branch.
I'll drop off for now, as I don't want to take up too much of your
time, but I've enjoyed our conversation.
For completeness, I'm pasting a commented version of my current
config, in case anyone's interested.
Cheers,
GC
user@knot:/etc/knot-resolver$ cat config.yaml
workers: 4
network:
tcp-pipeline: 100
address-renumbering:
# If DNS answer is %source%, change it to %dest%
# Works great but logically better under local-data:?
- source: x.x.x.x/32
destination: y.y.y.y
# Recurse on this interface
out-interface-v4: a.s.d.f
do-ipv4: true
do-ipv6: false
listen:
- interface: z.x.c.v@53
kind: dns
# DoH works really well
- interface: 192.168.123.122
port: 443
kind: doh2
proxy-protocol:
enable: true
allow:
- 192.168.0.0/16
- 10.0.0.0/8
- 172.16.0.0/12
logging:
level: info
# crit|err|warning|notice|info|debug
target: syslog
# Can target directory be specified?
dnstap:
enable: false
unix-socket: /tmp/dnstap.sock
# by default log is disabled for all
log-queries: true
log-responses: true
# Nice - Also possible to enable/disable this
# for general logging, without adjusting the
# logging level?
views:
- subnets: [192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8]
answer: allow
- subnets: [0.0.0.0/0]
answer: refused
# Tagging untested
cache:
storage: /var/cache/knot-resolver
size-max: 2G
# prefetch expiring/frequent records
prefetch:
expiring: true
prediction:
period: 48
window: 60m
prefill:
- origin: "."
url:
https://www.internic.net/domain/root.zone
refresh-interval: 12h
ca-file: /etc/ssl/certs/ca-certificates.crt
options:
priming: true
rebinding-protection: true
time-jump-detection: false
# Prevent snooping on cache content
# refuse-no-rd: false
local-data:
# addresses:
#
a1.example.com: 2001:db8::1
#
a2.example.org: [ 192.0.2.2, 192.0.2.3, 2001:db8::4 ]
# addresses-files:
# - /etc/hosts
records: |
trump.com. CNAME
berniesanders.com.
# This works really well :-)
# Have not tested other query types
# Warning. 6.X currently flattens all layers as single zone
# First rpz-passthru for a question passes traffic
# with no further RPZ processing
rpz:
- file: /var/cache/bind/whitelist.rpz
watchdog: auto
- file: /var/cache/bind/malicious.ioc2rpz
watchdog: auto
- file: /var/cache/bind/doh.ioc2rpz
watchdog: auto
- file: /var/cache/bind/blocklist-malicious.ioc2rpz
watchdog: auto
- file: /var/cache/bind/oisd-full.ioc2rpz
watchdog: auto
- file: /var/cache/bind/blox-malicious.ioc2rpz
watchdog: auto
- file: /var/cache/bind/urlhaus.ioc2rpz
watchdog: auto
- file: /var/cache/bind/bforeai.ioc2rpz
watchdog: auto
- file: /var/cache/bind/phishtank.ioc2rpz
watchdog: auto
- file: /var/cache/bind/hblock.ioc2rpz
watchdog: auto
- file: /var/cache/bind/dga.ioc2rpz
watchdog: auto
# Return nxdomain for an entire zone and set the TTL
# Again, this is great
rules:
- name:
- x.com.
subtree: nxdomain
ttl: 30d
forward:
# Local zones to ns1.local.lan
- subtree: local.lan
servers: [ x.x.x.x ]
options:
authoritative: true
dnssec: false
dnssec:
enable: false
negative-trust-anchors:
# disable validation for specific zone
- 123ex4mple321.zxc
#RPZ zones for ref - No 1 GB+ tiers here ;-)
user@knot:/var/cache/bind$ ls -lth $PWD/*rpz*
-rw-r--r-- 1 root root 21M Feb 4 11:40 /var/cache/bind/oisd-full.ioc2rpz
-rw-r--r-- 1 root root 43K Feb 4 11:01 /var/cache/bind/urlhaus.ioc2rpz
-rw-r--r-- 1 root root 3.4M Feb 4 10:07 /var/cache/bind/blox-malicious.ioc2rpz
-rw-r--r-- 1 root root 1.1M Feb 4 09:17 /var/cache/bind/phishtank.ioc2rpz
-rw-r--r-- 1 root root 836 Feb 4 09:17 /var/cache/bind/phishtank.ioc2rpz.jnl
-rw-r--r-- 1 root root 188K Feb 3 17:24 /var/cache/bind/bforeai.ioc2rpz
-rw-r--r-- 1 root root 158K Feb 3 17:18 /var/cache/bind/doh.ioc2rpz
-rw-r--r-- 1 root root 56M Feb 3 17:14 /var/cache/bind/dga.ioc2rpz
-rw-r--r-- 1 root root 40M Feb 3 17:04 /var/cache/bind/hblock.ioc2rpz
-rw-r--r-- 1 root root 148M Feb 3 16:53 /var/cache/bind/malicious.ioc2rpz
-rw-r--r-- 1 root root 94M Feb 3 16:50
/var/cache/bind/blocklist-malicious.ioc2rpz
On Wed, 4 Feb 2026 at 10:49, Vladimír Čunát <vladimir.cunat(a)nic.cz> wrote:
On 04/02/2026 11.10, Giles Crawford wrote:
Believe it or not, I tried the 'zone transfer via dig' route,
scheduling via cron :-)
6 5 * * * dig @X.X.X.X -y hmac-sha256:ioc2rpz.net-foo doh.ioc2rpz
AXFR > /etc/knot-resolver/doh.ioc2rpz
The files produced by dig don't follow the masterfile format, however,
so neither kresd nor named would load them without modification.
YMMV with kdig; haven't tried it as yet.
That's surprising me. I certainly did succeed in the past with kdig and
knot-resolver with some zones (relatively simple cases, no authentication).
Given current constraints, I found that it's more elegant to take a
zone transfer using an authoritative server (speaking of which, I must
try the Knot one), which can then also better keep the zone fresh.
Kresd's excellent watch functionality for RPZs makes this possible.
Sure, I do consider using auth servers among the best approaches here (even though
it's not trivial to set up all).
For the RPZ stack logic to make sense in a top-down approach, guessing
that we should make the decision / exist the stack on the first match,
so if passthru matches in tier 1 and there's an NXDOMAIN match in tier
2, the traffic gets passed.
I wasn't too keen on adding (many) tiers for efficiency reasons. So in the currently
released 6.x versions all local-data rules (+defaults) get merged into a single tier, so a
single search can search it all.
The issue is that kresd does not currently recognise "rpz-passthru" in
the RPZ zone files, according to logged errors.
Yes. We do have this feature inside one public branch (jezek-test, which gets used in a
particular project now). The current semantics in there is that if the QNAME in a
client's request falls into a subtree of any names in the allow-list (expressed by
rpz-passthru), this whole request's processing skips any blocking rules. It's a
compromise, nothing perfect, and it's unfortunately also far from the rpz-passthru
definition in the RPZ draft.
--Vladimir