fix(smolmachines): use bridge gateway as TSI proxy host on Linux
lint / lint (push) Failing after 1m49s
test / unit (pull_request) Failing after 41s
test / integration (pull_request) Successful in 19s

On Linux the guest kernel's LOCAL routing table routes all
127.0.0.0/8 to the guest's own loopback interface (priority 0,
checked before any main-table route), so TSI never sees
connections to the per-bottle loopback alias — the fix_guest_
loopback_routing approach confirmed this at the kernel level.

Use the per-bottle docker bridge gateway (192.168.N.1) instead.
It is not a loopback address, so the guest routes it via eth0
and TSI intercepts it normally.  The TSI allowlist remains a
/32 that is distinct from the container IP (192.168.N.2), so
direct bypass to egress:9099 is still blocked by TSI.

Changes:
- Add _proxy_host() helper: returns bundle_gateway on Linux,
  loopback alias on macOS
- Thread proxy_host through _start_bundle, _discover_urls,
  _launch_vm, and _bundle_launch_spec (publish_host_ip)
- Remove _fix_guest_loopback_routing (no longer needed)
- Relax proxy-URL assertion in the integration test to accept
  any http://IP:port (with a comment explaining the difference)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 13:15:34 -04:00
parent 688e4cd546
commit 8f9005582b
5 changed files with 93 additions and 57 deletions
+13 -4
View File
@@ -21,13 +21,14 @@ security properties the design pivot was about:
bind-address mitigation is what closes TSI's port-granularity
gap.
Gated on macOS + smolvm + docker + not GITEA_ACTIONS — the
Gated on macOS/Linux + smolvm + docker + not GITEA_ACTIONS — the
runner can't host libkrun-backed VMs."""
from __future__ import annotations
import os
import platform
import re
import shutil
import tempfile
import unittest
@@ -65,8 +66,8 @@ def _minimal_manifest() -> ManifestIndex:
@skip_unless_docker()
@unittest.skipUnless(
platform.system() == "Darwin",
"smolvm is macOS-only for v1; Linux+KVM path is a future PRD",
platform.system() in ("Darwin", "Linux"),
"smolvm requires macOS or Linux",
)
@unittest.skipUnless(
_smolvm_available(),
@@ -141,7 +142,15 @@ class TestSmolmachinesLaunch(unittest.TestCase):
proxies = [line.strip() for line in r.stdout.splitlines()]
self.assertEqual(2, len(proxies), proxies)
self.assertEqual(proxies[0], proxies[1], proxies)
self.assertTrue(proxies[0].startswith("http://127."), proxies[0])
# macOS: proxy binds to the per-bottle loopback alias (127.x.x.x) so
# TSI can intercept guest connections to it. Linux: the guest kernel
# routes 127.0.0.0/8 to its own loopback (TSI never sees those), so
# the proxy instead binds to the per-bottle bridge gateway (192.168.x.1)
# which routes via eth0 and is intercepted by TSI normally.
self.assertRegex(
proxies[0], r"^http://\d+\.\d+\.\d+\.\d+:\d+$",
"expected proxy URL to be an http://IP:port address",
)
r = self.bottle.exec(
"curl -fsS --max-time 20 https://example.com >/dev/null && echo OK"