fix(security): Restrict sandbox loopback access to proxy and DNS only#95
Open
usnavy13 wants to merge 1 commit into
Open
fix(security): Restrict sandbox loopback access to proxy and DNS only#95usnavy13 wants to merge 1 commit into
usnavy13 wants to merge 1 commit into
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 on127.0.0.1:8000.This PR replaces the blanket loopback rule with targeted rules that only allow DNS resolution (
127.0.0.53:53UDP/TCP).Vulnerability Details
Root cause:
src/services/sandbox/egress_firewall.pyline 120–135 contained: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):
Submit a Python execution request with
ENABLE_SANDBOX_NETWORK=true:Result:
200 {"status":"healthy","version":"1.2.0","service":"code-interpreter-api"}— sandbox code reached the API.A port scan from inside the sandbox confirmed:
With
AUTH_ENABLED=true(current config), calling/execfrom the sandbox returns 401 — auth blocks escalation. However, ifAUTH_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_NETWORKdefaults tofalse— only affects deployments that enable it for skill installsAUTH_ENABLEDdefaults totrue— full escalation requires auth to be disabled/procor environment variablesWhat Changed
src/services/sandbox/egress_firewall.py— replaced one rule with two:-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-resolvedAll other loopback ports (including 8000/API) are now rejected.
Test Plan
pytest tests/unit/test_egress_proxy.py— 21 tests passflake8andblack --checkcleanurllib.request.urlopen("https://127.0.0.1:8000/health")should fail with connection refused/rejectedpip install,npm install) through the proxy🤖 Generated with Claude Code