box-firewall: converge the UFW carve-out off the live bridge; fail closed at boot (#86 follow-up) #89

Merged
dan-claude-bot merged 4 commits from fix/ufw-subnet-converge into main 2026-07-18 21:14:18 +00:00

4 commits

Author SHA1 Message Date
dan-claude-bot
56638d9654 docs: changelog — the UFW converge and the fail-closed boot window
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 20:45:21 +00:00
dan-claude-bot
ce9ec684d1 test(cli): drive box-firewall under a shim ufw, and the doctor's UFW findings
The real script, driven end to end (the setup-host seam): a fake ufw
serves canned status tables and logs every mutation, fake
nft/sysctl/iptables swallow the rest, the shim ip answers the
live-bridge read. Proven: a remapped bridge converges (stale tcp+udp
allows deleted, live gateway's inserted, live rules never deleted), an
agreeing host deletes nothing, a fresh host gets exactly the five
historical mutations, the unaddressed-bridge boot window mutates
NOTHING and says so (the fallback's absence is pinned to non-comment
lines), and a no-UFW host keeps its interface-scoped nft path — boot
window included. ufw_dns_findings is extracted and driven against the
same tables: agreement silent, stale carve-out flagged and named,
deny-with-no-allow flagged, untouched UFW host clean, stale-beside-live
named, other interfaces ignored; wiring grep-guarded. 297 checks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 20:45:21 +00:00
dan-claude-bot
fbad141a82 doctor: judge UFW's own DNS carve-out against the live gateway
The new ACL carve-out check compared only the incus ACL to the live
gateway — which setup-host now converges — so a remapped UFW host got a
clean bill while the stale UFW allow silently dropped box DNS: the
safety net had a blind spot precisely for UFW hosts using the #80
escape hatch (#86 review, item 1). ufw_dns_findings is pure text in
('ufw status' output, the network, the live gateway), findings out
(silence is agreement) — the gw_squat_signature seam, so the suite
drives it against canned tables. It flags a DNS allow that does not
match the gateway, our deny with no DNS allow at all, and stale allows
left beside a live one; a UFW host box-firewall never touched has
nothing to judge and stays clean. Wired into the admin Firewall section
wherever UFW is active and a bridge exists to compare against, with the
fix pointing at the now-converging box-firewall.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 20:45:21 +00:00
dan-claude-bot
ca1a759f3a box-firewall: converge the UFW carve-out off the live bridge; fail closed at boot
The UFW block was create-once: gated on 'a DENY on boxnet exists', it
pinned every UFW host to the gateway of the FIRST run. A bridge remapped
off a colliding subnet (#80's escape hatch) kept its stale
'allow ... to <old-gw> port 53' and never gained the live gateway's, so
box->gateway DNS died at box's own deny — the exact stale-carve-out
failure #86 rewrote the ACL path to prevent, left standing in the UFW
path (#86 review, item 1). The allows now converge on every run: DNS
allows aimed anywhere but the live gateway are deleted, then the live
set is ensured (ufw skips existing rules, so a fresh host gets the
identical five rules and a re-run is a no-op).

And the boot fallback GW=10.88.0.1 is gone (#86 review, item 2): with
the bridge not yet addressed when the service ran, a BOX_SUBNET host got
its carve-out built — and, once converging, converged — for the WRONG
gateway, a latent DNS drop. Fail closed instead: an unaddressed bridge
leaves the persisted UFW rules exactly as they are (they survive boots
on their own, and nothing else in this script needs the gateway — the
nft carve-out is interface-scoped) and says so on stderr; the next
setup-host run or service restart converges them once the bridge is up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 20:45:21 +00:00