diff --git a/bot_bottle/backend/smolmachines/launch.py b/bot_bottle/backend/smolmachines/launch.py index 7227d23..d722b4c 100644 --- a/bot_bottle/backend/smolmachines/launch.py +++ b/bot_bottle/backend/smolmachines/launch.py @@ -406,15 +406,13 @@ def _stage_sidecar_data(plan: SmolmachinesBottlePlan) -> Path: for name in ("entrypoint.sh", "pre-receive", "access-hook"): (scripts_dir / name).chmod(0o755) - # Patch credential paths: the rendered entrypoint hardcodes - # /git-gate/creds/; rewrite to the in-VM git-gate subdir. + # Patch paths: the rendered entrypoint hardcodes /git-gate/creds/ + # and /etc/git-gate/ for hooks; rewrite both to the in-VM subdir. ep_path = scripts_dir / "entrypoint.sh" - ep_path.write_text( - ep_path.read_text().replace( - "/git-gate/creds/", - f"{_GIT_GATE_SCRIPTS_DIR_IN_VM}/creds/", - ) - ) + text = ep_path.read_text() + text = text.replace("/git-gate/creds/", f"{_GIT_GATE_SCRIPTS_DIR_IN_VM}/creds/") + text = text.replace("/etc/git-gate/", f"{_GIT_GATE_SCRIPTS_DIR_IN_VM}/") + ep_path.write_text(text) creds_dir = scripts_dir / "creds" creds_dir.mkdir(exist_ok=True) diff --git a/bot_bottle/backend/smolmachines/loopback_alias.py b/bot_bottle/backend/smolmachines/loopback_alias.py index 50b4ba6..06ee6e2 100644 --- a/bot_bottle/backend/smolmachines/loopback_alias.py +++ b/bot_bottle/backend/smolmachines/loopback_alias.py @@ -152,23 +152,31 @@ def force_allowlist(machine_name: str, allowed_cidrs: list[str]) -> None: """Ensure the machine's persisted TSI allowlist equals `allowed_cidrs`, failing **closed** if that can't be confirmed. - Runs on both macOS and Linux. It exists because smolvm 0.8.0 - silently drops `--allow-cidr` when combined with `--from`, so - the allowlist has to be written into smolvm's persistent state - DB before `machine start`. Rather than assume the flag was - dropped, we read the persisted row and only patch when it - doesn't already match — so a newer smolvm that honors the flag - is left untouched. + macOS only. On Linux, smolvm's TSI defaults to full loopback + access and patching `allowed_cidrs` into the state DB crashes + TSI boot (the VM fails with "boot process exited (code 1)"). + Per-bottle CIDR isolation is therefore not enforced on Linux + until smolvm fixes the `--allow-cidr` + TSI interaction. This + is a known limitation — the agent VM can reach all of host + loopback, not just its bottle's forwarder ports. + + On macOS, smolvm 0.8.0 silently drops `--allow-cidr` when + combined with `--from`, so the allowlist has to be written + into smolvm's persistent state DB before `machine start`. + Rather than assume the flag was dropped, we read the persisted + row and only patch when it doesn't already match — so a newer + smolvm that honors the flag is left untouched. Must run AFTER `smolvm machine create` (the row has to exist) and BEFORE `smolvm machine start` (smolvm reads the row on start; in-flight VMs don't pick up changes). - Fail-closed: if the state DB is missing, the row is missing, or - the allowlist still doesn't match after patching, we `die()` - rather than boot a VM whose egress confinement we can't verify - — an unconfirmed allowlist is a sandbox-escape risk (the agent - VM could reach all of host loopback).""" + Fail-closed (macOS): if the state DB is missing, the row is + missing, or the allowlist still doesn't match after patching, + we `die()` rather than boot a VM whose egress confinement we + can't verify.""" + if not _is_macos(): + return want = list(allowed_cidrs) if not _SMOLVM_DB_PATH.is_file(): die( diff --git a/tests/unit/test_smolmachines_loopback_alias.py b/tests/unit/test_smolmachines_loopback_alias.py index 7847dc3..35540ef 100644 --- a/tests/unit/test_smolmachines_loopback_alias.py +++ b/tests/unit/test_smolmachines_loopback_alias.py @@ -313,9 +313,9 @@ class TestForceAllowlist(unittest.TestCase): self.assertEqual(4, cfg["cpus"]) self.assertTrue(cfg["network"]) - def test_patches_on_linux_too(self): - # force_allowlist no longer no-ops on Linux — the TSI - # allowlist must be enforced there as well. + def test_skips_patch_on_linux(self): + # On Linux, patching allowed_cidrs into the smolvm state DB + # crashes TSI boot. force_allowlist is a no-op on Linux. with patch.object(loopback_alias, "_is_macos", return_value=False), \ patch.object(loopback_alias, "_SMOLVM_DB_PATH", self.db): loopback_alias.force_allowlist("demo-vm", ["127.0.0.16/32"]) @@ -324,7 +324,7 @@ class TestForceAllowlist(unittest.TestCase): "SELECT data FROM vms WHERE name='demo-vm'", ).fetchone()[0]) con.close() - self.assertEqual(["127.0.0.16/32"], cfg["allowed_cidrs"]) + self.assertIsNone(cfg["allowed_cidrs"]) def test_skips_write_when_already_matching(self): # A newer smolvm that honors --allow-cidr at create leaves the