Configuration¶
The RPZ downloader reads a per-instance YAML config file. The
default path on a deployed box is
/etc/pdns-pf/oxfeed-rpz-downloader/<instance>.yml, where
<instance> matches the systemd template instance name. A commented
template with every option is shipped as
/etc/pdns-pf/oxfeed-rpz-downloader/example.yml.
Environment variables in YAML values are only expanded when the
service is started with --expand-env. Use this to inject the TSIG
secret through the unit's environment file:
The shared config struct also exposes feed.command_source and
feed.http_upstream sections, but the rpz-downloader binary only
ever instantiates the RPZ source. Setting feed.rpz_downloader is
mandatory; the other two source blocks should be left out. Use the
dedicated pdns-pf-oxfeed-commandsource and
pdns-pf-oxfeed-httpapi-downloader services for those sources.
Top-level structure¶
| Parameter | Type | Default | Description |
|---|---|---|---|
run_once |
boolean |
false |
Exit after a single successful update cycle. |
feed |
Feed |
— | Where and how the feed is written, plus the RPZ source itself. Required. |
http |
HTTP |
{} |
Status / metrics HTTP server. Disabled if address is empty. |
log |
Log |
see section | Log format and level. |
Deprecation policy¶
No YAML fields have been deprecated or removed.
A handful of command-line flags from the retired Python version are still accepted as drop-in replacements; see Operations → Deprecated flags for the mapping. New deployments should configure everything through the YAML file.
Full example config¶
Click to expand the shipped example config
# One may want the client to exit immediately after a run.
#run_once: true
# Configures where and how the feed directory with feed.json is written
feed:
# Path where the feed is saved to.
# This is equivalent to `simpleblob://fs?root_path=/tmp/feed`
# or `file:///tmp/feed`.
# As an alternative, the following format is accepted:
#root:
# # The kind of storage.
# # One of "fs", "memory", or "s3"
# type: fs
# # Options for storage, passed on to simpleblob.
# # Backend "memory" takes no option.
# # See the following links for "fs" and "s3" options:
# # https://pkg.go.dev/github.com/PowerDNS/simpleblob/backends/fs#Options
# # https://pkg.go.dev/github.com/PowerDNS/simpleblob/backends/s3#Options
# options:
# root_path: /tmp/feed
root: /tmp/feed
# Optional. Allows trying again storage operations.
retry:
# Duration for which consecutive network errors are tolerated.
# After this, an error will be returned.
#timeout: 5m
# Time to wait in case an error occurred while reading/writing.
#interval: 20s
# Optional. Maximum random time to add to interval to avoid multiple retries at once.
#max_jitter: 1s
# Optional. Static time to add to the interval for each failure.
#backoff: 2s
# Make every code have its own namespace.
# WARNING: Changing this later will trigger a full feed UUID reset!
#namespace_per_code: false
# Enable internal memdb stores pruning.
#prune_enabled: false
# Time between two memdb stores prunings.
#prune_interval: 24h
# RPZ-specific configuration values
rpz_downloader:
# Address of the DNS server to send *XFR queries to, including port.
address: rpz-server-addr:53
# DNS NS value, required for incremental transfers (IXFR).
zone: localhost.
# Time to wait after a fetch before another fetch is done.
#interval: 30m
# Minimum time between two AXFR queries.
# `0` (zero value) means "never".
#axfr_interval: 24h
# Duration for which consecutive network errors are tolerated.
#max_network_failure_duration: 24h
# Time to wait in addition of `interval`, in case a network
# error occurred.
#backoff: 10m
# Duration after which an attempt at opening a connection to `address`
# is considered a failure. `0` means it will not time out.
#dial_timeout: 2m
# Duration after which reading from `address` fails.
#read_timeout: 15m
# Duration after which writing to `address` fails.
#write_timeout: 2m
# Base64 secret for signed transaction to `zone`.
# If defined and not empty, TSIG will be used.
#tsig_secret: ""
# Supported message digest algorithms are: hmac-sha1, hmac-sha224,
# hmac-sha256, hmac-sha384 and hmac-sha512. hmac-md5 is not accepted.
#tsig_algo: hmac-sha256
# Time margin accepted when checking signature time.
#tsig_fudge: 300s
# The HTTP server exposes Prometheus /metrics and a status page
http:
# Address to listen on. Disabled by default.
#address: ":9605"
# Serve feed under /feed. Not for production use!
#serve_feed: false
log:
level: info # options: trace, debug, info, warning, error, fatal
format: human # options: human, logfmt, json
timestamp: short # options: short, full, disable
feed¶
The feed section configures both the destination of the produced
feed and the RPZ source that drives it.
| Parameter | Type | Default | Description |
|---|---|---|---|
root |
string / Root |
— | Storage destination for the feed tree. Required. |
retry |
retry.Config |
{} |
Retry policy for storage operations. |
namespace_per_code |
boolean |
false |
Place each classification code in its own namespace. Changing this resets the feed. |
rpz_downloader |
RPZDownloader |
— | DNS *XFR settings. Required. |
expiry |
ExpiryConfig |
see defaults | Snapshot and delta retention rules. |
snap_interval |
go: Duration |
1h |
Force a fresh snapshot at this cadence. |
prune_enabled |
boolean |
false |
Compact the in-memory feed state periodically. |
prune_interval |
go: Duration |
24h |
Time-based pruning trigger. Only consulted when prune_enabled is true. |
feed.root¶
The simpleblob storage where the feed tree is written. Accepts either a string URI / path or a struct.
String form (the recommended default):
A bare filesystem path is equivalent to file:///path and to the
simpleblob://fs?root_path=/path URI.
Struct form, for backends that need options:
feed:
root:
type: fs # one of "fs", "memory", "s3"
options:
root_path: /var/lib/pdns-pf-oxfeed-rpz-downloader/feeds/my-zone
The --oxfeed-path CLI flag overrides this field at startup. See
simpleblob/backends/fs
and
simpleblob/backends/s3
for backend-specific options.
feed.retry¶
Wraps every storage call (snapshot write, delta write, codes write, cleanup) in a retry loop. All durations are optional and an empty section disables retries entirely.
| Parameter | Type | Default | Description |
|---|---|---|---|
timeout |
go: Duration |
— | Total time window during which retries are attempted. Must be strictly greater than 2*interval + max_jitter + backoff. |
interval |
go: Duration |
— | Time to wait after a failed attempt before retrying. Minimum 10 ms. |
max_jitter |
go: Duration |
0 |
Random extra delay added on top of interval. Minimum 2 ms when non-zero. |
backoff |
go: Duration |
0 |
Static delay added to interval for every failure. Minimum 1 ms when non-zero. |
feed.namespace_per_code¶
When true, every classification code gets its own LMDB namespace on
downstream clients.
Warning
Changing namespace_per_code after a feed has been written
resets the feed UUID, which forces every downstream client to
re-snap from scratch. Decide this value once per feed and leave
it alone.
feed.rpz_downloader¶
The DNS source. address and zone are required; the rest have
sane defaults.
Warning
tsig_secret is a credential. Use --expand-env and reference
it as ${RPZ_TSIG_SECRET} from a systemd drop-in or environment
file instead of writing it into the YAML directly. The service
masks the value to (omitted) when logging the resolved config.
| Parameter | Type | Default | Description |
|---|---|---|---|
address |
string |
— | Upstream DNS server in host:port form. Required. |
zone |
string |
— | Zone NS (the value that appears in the SOA owner field). Required for IXFR continuation across restarts. Trailing dot recommended (example.com.). |
interval |
go: Duration |
30m |
Time between two XFR queries. Must be > 0. |
warn_no_xfr_interval |
go: Duration |
3h |
Trigger the rpz_recent_xfr_success healthz warning when the last successful XFR is older than this. Must be > 0. |
axfr_interval |
go: Duration |
24h |
See AXFR interval. 0 disables forced AXFR. |
max_network_failure_duration |
go: Duration |
24h |
After this long in a row of network errors, the service exits non-zero and is restarted by systemd. |
backoff |
go: Duration |
10m |
Extra delay applied on top of interval after a network error. |
dial_timeout |
go: Duration |
2m |
Timeout for opening the TCP connection. 0 means no timeout. |
read_timeout |
go: Duration |
15m |
Timeout for reading the XFR response. 0 means no timeout. A full AXFR over a slow link can take several minutes; leave headroom. |
write_timeout |
go: Duration |
2m |
Timeout for writing the XFR query. 0 means no timeout. |
tsig_secret |
string |
— | Base64-encoded TSIG key. Empty disables TSIG. Masked in log output. |
tsig_algo |
string |
hmac-sha256 |
TSIG algorithm. See TSIG algorithms. |
tsig_fudge |
go: Duration |
300s |
Signature-time error margin. Rounded to seconds; max 65535s. |
AXFR interval¶
The first XFR after startup is always AXFR (the service has no SOA
serial to ask for changes against). After that, IXFR is the default
and axfr_interval forces an AXFR whenever it has elapsed since the
last successful XFR.
The clock does not override interval. With interval = 13h and
axfr_interval = 24h, an AXFR fires every 26h (2 * interval),
not every 24 h. Setting axfr_interval = 0 disables forced AXFR
entirely — the only AXFR is the one at startup (and whenever the
local serial is reset, e.g. by wiping the feed root).
TSIG algorithms¶
| Value | Notes |
|---|---|
hmac-sha1 |
Accepted. |
hmac-sha224 |
Accepted. |
hmac-sha256 |
Accepted. Default. |
hmac-sha384 |
Accepted. |
hmac-sha512 |
Accepted. |
hmac-md5 is not accepted; the service refuses to start with an
explicit error if it is configured. A trailing dot is allowed
(hmac-sha256.), in line with DNS algorithm naming.
feed.expiry¶
Snapshot and delta retention. The default values are
feed.expiry.snapshots = {must_keep_count: 3, must_keep_interval: 30m, max_keep_count: 3}
and
feed.expiry.deltas = {must_keep_count: 5, must_keep_interval: 6h, max_keep_count: 200, max_keep_interval: 7d}.
Deltas on the upgrade path from the latest snapshot to the latest
delta are always preserved, even when expiry would otherwise drop
them.
Each of snapshots and deltas accepts the same four fields:
| Parameter | Type | Default | Description |
|---|---|---|---|
must_keep_count |
int |
0 |
Never drop the last N entries, regardless of max_keep_*. |
must_keep_interval |
go: Duration |
0 |
Never drop entries newer than this age. |
max_keep_count |
int |
0 |
Drop oldest entries beyond N newest, subject to must-keep rules. 0 means no count limit. |
max_keep_interval |
go: Duration |
0 |
Drop entries older than this age, subject to must-keep rules. 0 means no age limit. |
Example: keep the last three snapshots and a week of deltas, capped at 200 to bound disk usage.
feed:
expiry:
snapshots:
must_keep_count: 3
must_keep_interval: 30m
max_keep_count: 3
deltas:
must_keep_count: 5
must_keep_interval: 6h
max_keep_count: 200
max_keep_interval: 168h
feed.snap_interval¶
How often to force a fresh snapshot even when the content has not
changed enough to demand one. Shorter intervals shrink the delta
chain that newly connecting clients must replay but produce more
snapshot churn on disk. Set to 0s to write a snapshot on every
accepted change (rarely useful — clients usually only need one
snapshot per day).
feed.prune_enabled¶
When true, the service periodically compacts the in-memory feed
state to recover stringstore space taken by deleted domains. See
Architecture → In-memory pruning
for trigger conditions and the CPU cost.
feed.prune_interval¶
Time-based prune trigger. Only consulted when prune_enabled is
true. 0s disables the time-based trigger; mutation-based pruning
still applies.
http¶
| Parameter | Type | Default | Description |
|---|---|---|---|
address |
string |
— | Bind address in host:port form (for example :9605). Empty disables the HTTP server entirely. |
serve_feed |
boolean |
false |
Expose the feed tree under /feed. See warning below. |
--http-address on the command line overrides http.address.
Warning
serve_feed is intended for development and one-shot testing.
The shipped status server has no ETag support, no auth, and no
rate limiting — it is not safe to expose to clients. In
production, write the feed to a directory and front it with a
proper static HTTP server (Caddy or nginx).
log¶
| Parameter | Type | Default | Description |
|---|---|---|---|
level |
string |
info |
One of trace, debug, info, warning, error, fatal. |
format |
string |
human |
One of human, logfmt, json. |
timestamp |
string |
short |
One of short, full, disable. |