Different blocking for different devices: steps and client groups

Client groups put devices (addresses, networks, or ACLs of them) at a
blocking step: off (only your rules), normal (the default), or steps
added above it, each including the lists of the steps below. Lists
start at a step. In each view, a gate policy zone in front of a step's
lists lets the devices below that step through (rpz-client-ip triggers
with rpz-passthru), so every list is still loaded once. The IPv6 part
of a gate leaves the IPv4-mapped range out: named matches IPv4 clients
as ::ffff:a.b.c.d.

Web UI: a Devices panel on the Blocking page and a step on each list.
CLI: jarl block step, jarl block group, jarl blocklist --step. API:
/blocking/steps, /blocking/groups. Database schema 8.

Also: copying the state kept empty lists as empty (not nil), so
reverting a changeset no longer adds restore operations for unchanged
blocklists, rules and views.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adamandClaude Opus 5.5 committed 2026-10-11 21:33:16 +01:00
1 parent 5b8f681e2a
commit 76aaaf3ef5
33 files changed
+2096 -55

No files matched your search

+37 -1
View File
@@ -486,7 +486,7 @@ expect_rpz "an Adblock entry blocks the names below it" x.tracker.adbraun.test t
expect_rpz "a hosts entry blocks the name" ads.adbraun.test test.blocklist.jarl.invalid.
expect_rpz "but not the names below it" sub.ads.adbraun.test none
run "blocklist list" 0 $J blocklist list
contains "^test *ok *2 "
contains "^test *ok *normal *2 "
run "block allow" 0 $J block allow ok.tracker.adbraun.test
expect_rpz "an allow rule beats the list" deep.ok.tracker.adbraun.test none
expect_rpz "the rest of the list still blocks" x.tracker.adbraun.test test.blocklist.jarl.invalid.
@@ -516,6 +516,42 @@ printf '<html>gone</html>\n' > $E/lists/list.txt
run "a broken download fails (exit 2)" 2 $J blocklist refresh test --force
contains "no domains found"
expect_rpz "and the previous version stays" x.new.adbraun.test test.blocklist.jarl.invalid.
# Client groups and steps: per-device blocking in one view, by client
# address. 127.0.0.2 and .3 are in homelab too, so they get view internal.
rpz_from() { # SOURCE NAME
$DIG +tries=1 +time=3 -b "$1" -p $DNS @127.0.0.1 "$2" A | awk '$4 == "SOA" && $1 ~ /jarl\.invalid\.$/ {print $1; f = 1} END {if (!f) print "none"}'
}
expect_rpz_from() { # DESC SOURCE NAME WANT
local got
for i in $(seq 30); do got=$(rpz_from "$2" "$3"); [ "$got" = "$4" ] && break; sleep 0.2; done
if [ "$got" = "$4" ]; then ok "$1: $3 from $2 -> $4"; else bad "$1: $3 from $2 -> $got, want $4"; fi
}
printf '||social.adbraun.test^\n' > $E/lists/strict.txt
run "block step add" 0 $J block step add strict
run "blocklist add at a step" 0 $J blocklist add social http://127.0.0.1:$LISTS/strict.txt --step strict
contains "applied"
run "a step no group reaches: warned" 0 $J status
contains "blocks nothing"
run "block group add (off)" 0 $J block group add work 127.0.0.2 --step off
run "block group add (strict, with an IPv6 network)" 0 $J block group add kids 127.0.0.3 2001:db8:77::/64 --step strict
run "block group list" 0 $J block group list
contains "kids *strict *127.0.0.3, 2001:db8:77::/64"
expect_rpz_from "normal: the normal list" 127.0.0.1 x.new.adbraun.test test.blocklist.jarl.invalid.
expect_rpz_from "normal: not the strict list" 127.0.0.1 x.social.adbraun.test none
expect_rpz_from "off: no lists" 127.0.0.2 x.new.adbraun.test none
expect_rpz_from "off: but your rules" 127.0.0.2 blocked.adbraun.test rules.jarl.invalid.
expect_rpz_from "strict: the strict list" 127.0.0.3 x.social.adbraun.test social.blocklist.jarl.invalid.
expect_rpz_from "strict: and the normal list" 127.0.0.3 x.new.adbraun.test test.blocklist.jarl.invalid.
run "block check names the step" 0 $J block check x.social.adbraun.test
contains "for clients at step strict and above"
run "a step in use can't be deleted (exit 5)" 5 $J block step delete strict
run "move the work laptop to normal" 0 $J block group update work --step normal
expect_rpz_from "now normal" 127.0.0.2 x.new.adbraun.test test.blocklist.jarl.invalid.
run "block group delete" 0 $J block group delete work
run "block group delete kids" 0 $J block group delete kids
run "blocklist delete social" 0 $J blocklist delete social --yes
run "block step delete" 0 $J block step delete strict
run "no gates left" 1 grep -q 'gate.jarl.invalid' $E/etc-jarl/named.conf
run "blocklist disable" 0 $J blocklist disable test
expect_rpz "a disabled list doesn't block" x.new.adbraun.test none
run "blocklist delete" 0 $J blocklist delete test --yes