From 6bd0529e6df543a3433e8bc9f2e9afd6c7b5cf4b Mon Sep 17 00:00:00 2001 From: Joe Licata Date: Thu, 7 May 2026 02:57:05 +0000 Subject: [PATCH] fix(security): Restrict sandbox loopback access to proxy and DNS only 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) --- src/services/sandbox/egress_firewall.py | 37 ++++++++++++++++++++----- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/services/sandbox/egress_firewall.py b/src/services/sandbox/egress_firewall.py index 6afd3e5..d728181 100644 --- a/src/services/sandbox/egress_firewall.py +++ b/src/services/sandbox/egress_firewall.py @@ -10,7 +10,8 @@ This module installs iptables OUTPUT rules that match on the sandbox uid: - ALLOW the sandbox uid → 127.0.0.1: (so pip etc. work) - - DROP everything else from the sandbox uid + - ALLOW the sandbox uid → 127.0.0.53:53 (DNS via systemd-resolved) + - REJECT everything else from the sandbox uid The API process itself runs as root (uid 0), so the proxy's own outbound traffic to PyPI/npm/etc. is unaffected by these rules. @@ -113,10 +114,8 @@ def install_sandbox_egress_rules(sandbox_uid: int, proxy_port: int) -> bool: "-j", "ACCEPT", ], - # Allow loopback traffic generally (DNS to systemd-resolved on 127.0.0.53, - # localhost-only services, etc.). The proxy enforces hostname allowlist - # for actual outbound; this just keeps the sandbox uid able to talk - # to itself if it ever needs to. + # Allow DNS to systemd-resolved on loopback (some tools resolve + # before handing the CONNECT to the proxy). [ "-A", "OUTPUT", @@ -124,8 +123,32 @@ def install_sandbox_egress_rules(sandbox_uid: int, proxy_port: int) -> bool: "owner", "--uid-owner", str(sandbox_uid), - "-o", - "lo", + "-d", + "127.0.0.53", + "-p", + "udp", + "--dport", + "53", + "-m", + "comment", + "--comment", + _RULE_COMMENT, + "-j", + "ACCEPT", + ], + [ + "-A", + "OUTPUT", + "-m", + "owner", + "--uid-owner", + str(sandbox_uid), + "-d", + "127.0.0.53", + "-p", + "tcp", + "--dport", + "53", "-m", "comment", "--comment",