nftblock 0.1.0

Atomically apply CIDR lists with nftables netlink batches
Documentation

nftblock

nftblock watches Blockmerge's newline-delimited mixed IPv4/IPv6 CIDR files and owns a single inet nftblock table. Every accepted reload is parsed completely first and then sent through libnftnl/libmnl as one atomic, paged netlink transaction. A parse, allocation, or kernel error leaves the active table unchanged. The daemon never invokes the nft executable.

The table contains separate interval sets for inbound/outbound IPv4/IPv6 traffic and base chains for input, forward, and output. Normal SIGINT/SIGTERM shutdown deliberately preserves the table.

Configuration

Start with config/nftblock.toml. Zones can be defined directly in it:

[zones]
WAN = ["eth0"]
LOCAL = { interfaces = [], local = true }
LAN = ["eth1"]

Alternatively, omit [zones] and set zones = "/data/zones.json" in [files] (or pass --zones/NFTBLOCK_ZONES). config/zones.example.json accepts either the shown map or { "zones": [{ "name": "WAN", "interfaces": ["eth0"] }] }. Inline and file definitions are mutually exclusive; an explicit CLI/environment zones path selects the file. Zone alternatives are expanded as separate nftables rules (OR); ingress and egress constraints in one forward mapping are paired as a cross product (AND).

Unknown zones, interface-less non-local zones, local zones in forward rules, and chain-inappropriate fields are rejected before netlink is opened. Flowtables are queried directly over netlink. If one contains any protected interface, startup/reconciliation fails unless allow_flowtable_bypass = true is explicitly set.

All file paths, table settings, timing settings, and the flowtable override have CLI flags and NFTBLOCK_* environment overrides. Use nftblock --help for their exact names. Validate and show the expanded mappings without changing the firewall:

nftblock --config ./config/nftblock.toml --check

Blocklist syntax remains producer-neutral:

# generated 2026-08-18T12:00:00Z
192.0.2.0/24
2001:db8::/32

Blank lines and comments are ignored. Any other line must be a CIDR. Networks are normalized and exact duplicates are removed. Blockmerge must remove overlaps before publishing a list; nftblock does not perform producer-side aggregation.

Container

The container needs the host network namespace and CAP_NET_ADMIN; it does not need privileged mode. Mount the Blockmerge output directory read-only so inotify can observe destination renames. Both Debian and Alpine images install only the libnftnl and libmnl runtime libraries—nft is not installed. Build both variants with Docker Bake, or build either Dockerfile directly:

docker buildx bake
docker build -f Dockerfile.debian -t nftblock:debian .
docker build -f Dockerfile.alpine -t nftblock:alpine .

Tests

cargo test
sudo ./tests/netns.sh

The Rust suite covers parsing, interval encoding, configuration rendering, atomic rename event classification, failed-batch retention, and multi-page construction. The namespace test exercises the native input, forward, and output rules without touching the host ruleset.