fix: isolate boxes with the bridge's port-isolation flag #38

Merged
dan-claude-bot merged 2 commits from fix/firewall-restart into main 2026-07-14 01:42:35 +00:00

2 commits

Author SHA1 Message Date
3bed7c2f2a fix: isolate boxes with the bridge's port-isolation flag, not an nft rule
The nft bridge-family rule from the previous commit is LIVE on the host
and boxes still reach each other:

    table bridge claudebox {
      chain forward { ... meta ibrname "claudenet" meta obrname "claudenet" drop }
    }

    FAIL  BOX A REACHES BOX B — sibling isolation does NOT hold [tcp: refused]

So the rule is not wrong about intent, it is wrong about mechanism —
whatever path these frames take, that hook does not stop them. Rather
than reason harder about netfilter (reasoning is what put the hole there
in the first place), use the mechanism Incus provides for exactly this:
security.port_isolation on the bridged NIC, which sets the kernel bridge
port's isolated flag so two isolated ports cannot exchange frames at all.

The nft rule stays as a second layer — it costs nothing — but the
profile flag is what carries the guarantee. doctor.sh checks it, because
the absence of this one is invisible: everything works and boxes can
simply reach each other.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 01:41:18 +00:00
bd849181bd fix: the firewall unit never re-ran, so new rules were never applied
The box-to-box drop shipped, the drill still found boxes reaching each
other, and the rule was simply not on the host. setup-host.sh ended with
'systemctl enable --now claudebox-firewall.service' — but the unit is
RemainAfterExit, so once it has run it stays "active" forever, and
'--now' does nothing to an active unit. Re-running setup-host after
upgrading claudebox therefore installed the new script to
/usr/local/sbin and never executed it. The host silently kept its old
firewall, and the box-to-box hole stayed open through the release that
claimed to close it.

This is worse than the original bug: every future firewall change would
have landed only on hosts that had never run setup-host before.

Restart the unit instead — the script is idempotent by design. Then
ASSERT the rule is live rather than assume it, because the absence of
this particular rule is invisible: everything keeps working and boxes
can simply reach each other. doctor.sh checks it too.

Also: dns.mode=none is now part of the shipped stack, so the drill must
stop treating it as leftover rehearsal dirt and reverting it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 01:40:02 +00:00