Skip to content

fix(security): Restrict sandbox loopback access to proxy and DNS only#95

Open
usnavy13 wants to merge 1 commit into
devfrom
fix/restrict-sandbox-loopback-access
Open

fix(security): Restrict sandbox loopback access to proxy and DNS only#95
usnavy13 wants to merge 1 commit into
devfrom
fix/restrict-sandbox-loopback-access

Conversation

@usnavy13
Copy link
Copy Markdown
Owner

@usnavy13 usnavy13 commented May 7, 2026

Summary

When ENABLE_SANDBOX_NETWORK=true, nsjail disables the network namespace clone (--disable_clone_newnet) so sandbox processes share the container's network namespace. The egress firewall is supposed to lock sandboxes down to only the allowlist proxy, but an overly broad iptables rule (-o lo -j ACCEPT) granted sandbox processes access to every port on loopback — including the API itself on 127.0.0.1:8000.

This PR replaces the blanket loopback rule with targeted rules that only allow DNS resolution (127.0.0.53:53 UDP/TCP).

Vulnerability Details

Root cause: src/services/sandbox/egress_firewall.py line 120–135 contained:

-A OUTPUT -m owner --uid-owner <sandbox_uid> -o lo -j ACCEPT

This was intended for DNS and "localhost-only services" but inadvertently opened all 65,535 loopback ports to the sandbox UID.

Attack path (demonstrated on the live dev instance):

  1. Submit a Python execution request with ENABLE_SANDBOX_NETWORK=true:

    import urllib.request, ssl
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    resp = urllib.request.urlopen("https://127.0.0.1:8000/health", timeout=5, context=ctx)
    print(resp.status, resp.read().decode())
  2. Result: 200 {"status":"healthy","version":"1.2.0","service":"code-interpreter-api"} — sandbox code reached the API.

  3. A port scan from inside the sandbox confirmed:

    Port Service Status
    8000 API (HTTPS) OPEN
    18443 Egress Proxy OPEN (intended)
    6379 Redis Closed (separate container)
    3900 Garage/S3 Closed (separate container)
  4. With AUTH_ENABLED=true (current config), calling /exec from the sandbox returns 401 — auth blocks escalation. However, if AUTH_ENABLED=false (a documented, supported configuration for trusted-network deployments), sandbox code would get full unauthenticated API access: executing code in other sessions, accessing other sessions' files, etc.

Mitigating factors:

  • ENABLE_SANDBOX_NETWORK defaults to false — only affects deployments that enable it for skill installs
  • AUTH_ENABLED defaults to true — full escalation requires auth to be disabled
  • nsjail PID/mount namespace isolation prevents the API key from being discoverable via /proc or environment variables

What Changed

src/services/sandbox/egress_firewall.py — replaced one rule with two:

Before After
-o lo -j ACCEPT (all loopback ports) -d 127.0.0.53 -p udp --dport 53 -j ACCEPT (DNS only)
-d 127.0.0.53 -p tcp --dport 53 -j ACCEPT (DNS over TCP)

The existing rule #1 (proxy port on 127.0.0.1) and rule #3 (REJECT everything else) are unchanged. The net effect is that the sandbox UID can now only reach:

  • 127.0.0.1:<proxy_port> — the egress allowlist proxy (pip, npm, go, cargo)
  • 127.0.0.53:53 — DNS resolution via systemd-resolved

All other loopback ports (including 8000/API) are now rejected.

Test Plan

  • pytest tests/unit/test_egress_proxy.py — 21 tests pass
  • flake8 and black --check clean
  • Rebuild container and re-run the sandbox loopback test — urllib.request.urlopen("https://127.0.0.1:8000/health") should fail with connection refused/rejected
  • Verify skill installs still work (pip install, npm install) through the proxy

🤖 Generated with Claude Code

The blanket `-o lo -j ACCEPT` iptables rule allowed sandbox processes to
reach the API on 127.0.0.1:8000 when ENABLE_SANDBOX_NETWORK=true (shared
network namespace). Replace with targeted rules permitting only the egress
proxy port and DNS (127.0.0.53:53), closing the SSRF/escalation path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant