forked from heavy-duty/box
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>
This commit is contained in:
parent
f90d98a25d
commit
bd849181bd
3 changed files with 32 additions and 7 deletions
|
|
@ -78,6 +78,17 @@ else
|
||||||
inf "claudenet does not exist (a fresh host — setup-host.sh will create it)"
|
inf "claudenet does not exist (a fresh host — setup-host.sh will create it)"
|
||||||
fi
|
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)"
|
head_ "Profile — claude-dev (the NIC is the isolation contract)"
|
||||||
if incus profile show claude-dev >/dev/null 2>&1; then
|
if incus profile show claude-dev >/dev/null 2>&1; then
|
||||||
for k in security.mac_filtering security.ipv4_filtering; do
|
for k in security.mac_filtering security.ipv4_filtering; do
|
||||||
|
|
|
||||||
|
|
@ -204,18 +204,16 @@ KEEP="${KEEP:-0}"
|
||||||
# clean-ish slate and setup-host is the no-op it should be.
|
# 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
|
# 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.
|
# 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=""
|
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.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"
|
[ -n "$(incus profile device get claude-dev eth0 security.mac_filtering 2>/dev/null)" ] && dirty="$dirty mac_filtering"
|
||||||
if [ -n "$dirty" ]; then
|
if [ -n "$dirty" ]; then
|
||||||
note "this host still carries a previous run's phase-D mutations:$dirty — reverting them now"
|
note "this host carries the VETOED NIC filtering from an old rehearsal:$dirty — reverting"
|
||||||
incus network unset claudenet dns.mode >/dev/null 2>&1
|
|
||||||
incus profile device unset claude-dev eth0 security.mac_filtering >/dev/null 2>&1
|
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 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
|
fi
|
||||||
|
|
||||||
inf "clearing anything a previous run left behind…"
|
inf "clearing anything a previous run left behind…"
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,14 @@ fi
|
||||||
sudo install -m 755 "$here/host/claudebox-firewall.sh" /usr/local/sbin/claudebox-firewall
|
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 install -m 644 "$here/host/claudebox-firewall.service" /etc/systemd/system/
|
||||||
sudo systemctl daemon-reload
|
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
|
# Profile
|
||||||
if ! incus profile show claude-dev >/dev/null 2>&1; then
|
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
|
fi
|
||||||
incus profile edit claude-dev < "$here/profiles/claude-dev.yaml"
|
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 <box>"
|
echo "Host ready. Launch with: claudebox new --name <box>"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue