diff --git a/drill/doctor.sh b/drill/doctor.sh index 34769cc..9f8ba33 100755 --- a/drill/doctor.sh +++ b/drill/doctor.sh @@ -78,6 +78,17 @@ else inf "claudenet does not exist (a fresh host — setup-host.sh will create it)" fi +head_ "Firewall — the box-to-box drop" +if nft list table bridge claudebox >/dev/null 2>&1 || sudo -n nft list table bridge claudebox >/dev/null 2>&1; then + ok "nft bridge table 'claudebox' is present — boxes cannot reach each other" +else + no "the box-to-box drop is MISSING — boxes can reach each other" + inf "an L3 ACL never sees frames switched between two ports of one bridge;" + inf "the drop is an nft BRIDGE-family rule, and without it siblings are wide open." + inf "fix: sudo /usr/local/sbin/claudebox-firewall" + inf " (or: sudo systemctl restart claudebox-firewall.service)" +fi + head_ "Profile — claude-dev (the NIC is the isolation contract)" if incus profile show claude-dev >/dev/null 2>&1; then for k in security.mac_filtering security.ipv4_filtering; do diff --git a/drill/drill.sh b/drill/drill.sh index 7a0e423..49f14e9 100755 --- a/drill/drill.sh +++ b/drill/drill.sh @@ -204,18 +204,16 @@ KEEP="${KEEP:-0}" # clean-ish slate and setup-host is the no-op it should be. # A host still carrying a previous run's phase-D mutations mints boxes with no # DNS, and then reports the resulting breakage as a finding. Refuse to run. +# NOTE: dns.mode=none is now part of the SHIPPED stack (it closes the sibling +# DNS-enumeration leak), so it is no longer "dirt" from a rehearsal — do not +# revert it. Only the vetoed NIC filtering counts as leftover. dirty="" -[ -n "$(incus network get claudenet dns.mode 2>/dev/null)" ] && dirty="dns.mode" [ -n "$(incus profile device get claude-dev eth0 security.ipv4_filtering 2>/dev/null)" ] && dirty="$dirty ipv4_filtering" [ -n "$(incus profile device get claude-dev eth0 security.mac_filtering 2>/dev/null)" ] && dirty="$dirty mac_filtering" if [ -n "$dirty" ]; then - note "this host still carries a previous run's phase-D mutations:$dirty — reverting them now" - incus network unset claudenet dns.mode >/dev/null 2>&1 + note "this host carries the VETOED NIC filtering from an old rehearsal:$dirty — reverting" incus profile device unset claude-dev eth0 security.mac_filtering >/dev/null 2>&1 incus profile device unset claude-dev eth0 security.ipv4_filtering >/dev/null 2>&1 - incus network acl rule remove claude-isolate egress action=drop destination=@internal >/dev/null 2>&1 - still="$(incus network get claudenet dns.mode 2>/dev/null)" - [ -n "$still" ] && { echo "drill: could not revert dns.mode ('$still'). run: bash drill/doctor.sh --fix" >&2; exit 1; } fi inf "clearing anything a previous run left behind…" diff --git a/host/setup-host.sh b/host/setup-host.sh index 4b26ccf..12a1721 100755 --- a/host/setup-host.sh +++ b/host/setup-host.sh @@ -70,7 +70,14 @@ fi sudo install -m 755 "$here/host/claudebox-firewall.sh" /usr/local/sbin/claudebox-firewall sudo install -m 644 "$here/host/claudebox-firewall.service" /etc/systemd/system/ sudo systemctl daemon-reload -sudo systemctl enable --now claudebox-firewall.service +sudo systemctl enable claudebox-firewall.service +# RESTART, not 'enable --now'. The unit is RemainAfterExit, so once it has run +# it stays "active" forever — and 'enable --now' does nothing to an active unit. +# Re-running setup-host after upgrading claudebox therefore installed the new +# rules to /usr/local/sbin and never applied them: the host kept the old +# firewall, silently, and the box→box hole stayed open through a release that +# claimed to close it. Restart re-runs the script, which is idempotent by design. +sudo systemctl restart claudebox-firewall.service # Profile if ! incus profile show claude-dev >/dev/null 2>&1; then @@ -78,4 +85,13 @@ if ! incus profile show claude-dev >/dev/null 2>&1; then fi incus profile edit claude-dev < "$here/profiles/claude-dev.yaml" +# The sibling drop is the one rule whose absence is invisible: everything keeps +# working, and boxes can simply reach each other. Assert it landed. +if nft list table bridge claudebox >/dev/null 2>&1; then + echo "Isolation: box-to-box drop is live (nft bridge table 'claudebox')." +else + echo "WARNING: the box-to-box drop is NOT active — boxes can reach each other." >&2 + echo " check: sudo /usr/local/sbin/claudebox-firewall ; sudo nft list table bridge claudebox" >&2 +fi + echo "Host ready. Launch with: claudebox new --name "