feat(sandbox): enforce agent egress blocklist with in-container proxy and firewall - #1114
Open
questiondlmarks wants to merge 2 commits into
Open
feat(sandbox): enforce agent egress blocklist with in-container proxy and firewall#1114questiondlmarks wants to merge 2 commits into
questiondlmarks wants to merge 2 commits into
Conversation
Task configs can declare a list of host or host/path-prefix entries that must stay unreachable while every other destination remains open — the inverse of allowlist, for experiments that hide specific papers from a web-enabled agent. The mode is parsed and validated (agent, sandbox, verifier sections), reported as an unsupported runtime feature until the egress layer enforces it, and graded like public by the integration rubric.
questiondlmarks
marked this pull request as ready for review
September 9, 2026 00:22
Contributor
There was a problem hiding this comment.
Devin Review found 3 potential issues.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
… and firewall
Implement layered, protocol-level egress blocklist enforcement (network_mode="blocklist")
to hide target research papers and mirrors from web-enabled agents while preserving
native container internet access, binary-safe downloads, and 404 stealth behavior.
Key changes:
- Sandbox-local filtering proxy (`src/benchflow/sandbox/egress.py`):
* Root-run stdlib HTTP/1.1 forward proxy on loopback with MAX_CONNECTIONS=256 backpressure.
* Transparent CONNECT tunneling for unblocked hosts; TLS MITM inspection with a per-run
CA bundle for hosts carrying path-specific rules.
* Stealth 404 Not Found status for blocked endpoints (indistinguishable from missing pages).
* SSRF defense: vets all resolved addresses and blocks loopback, link-local (169.254.169.254),
and reserved ranges, while preserving RFC1918 private nets for compose side services.
* Dual-host checking (URL target + Host header) and path normalization (percent-decoding,
dot-segments, slash-collapsing) to prevent WAF evasion.
* IPv4-preferred candidate traversal in resolve_upstream for Docker bridge compatibility.
- Kernel firewall & container isolation:
* Injects docker-compose-net-admin.yaml overlay when an agent network policy is active.
* Post-bootstrap iptables rule confines the agent UID to loopback so non-proxy egress fails closed.
* Sandbox-user self-check probe runs before the first prompt and fails fast on policy breach.
- Model provider & harness synchronization:
* LiteLLM pre-call hook rewrites Anthropic server-side web tools with blocked_domains
and strips OpenAI hosted search tools.
* Harness knobs disable server-side web tools for Codex and Gemini.
* Strips rule secret list from agent process env; downloads root-only egress.jsonl audit log.
- Batch evaluation & CLI overlays:
* Adds --block-url and --block-url-file to bench eval run as C-axis config overlays.
* Adds NetworkPolicyPreflightError to validate task compatibility before rollouts start.
* Adds ResumeMismatchError guard against resuming jobs with differing network policies.
- Cross-platform & test suite:
* Enforces encoding="utf-8" across shim readers and config JSONs for Windows compatibility.
* Adds comprehensive test suite (tests/test_egress_blocklist.py, 63 tests) covering proxy,
TLS inspection, path normalization, ALPN downgrade, and batch preflight.
questiondlmarks
force-pushed
the
feat/egress-blocklist
branch
from
September 9, 2026 00:54
25967bb to
97f96a1
Compare
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
Implements layered, protocol-level egress blocklist enforcement (
network_mode = "blocklist") to hide specific research papers, target repositories, and mirrors from web-enabled agents during evaluation, while keeping the rest of the internet open.Unlike naive virtualization or coarse-grained container-level network blocks, this implementation:
curl, Pythonrequests/urllib, Node.jsfetch, etc.404 Not Foundinstead of403 Forbidden, making it indistinguishable from a missing page and preventing the agent from guessing blocked resources.iptablesagent-UID confinement, and LiteLLM pre-call model tool interception.Architecture & Enforcement Layers
Enforcement is layered across three boundaries:
In-Sandbox Filtering Proxy (
src/benchflow/sandbox/egress.py)rooton loopback (127.0.0.1:61380) withMAX_CONNECTIONS = 256semaphore backpressure.CONNECTtunneling for unblocked hosts (zero overhead / end-to-end TLS preserved).SSL_CERT_FILE,REQUESTS_CA_BUNDLE,CURL_CA_BUNDLE,NODE_EXTRA_CA_CERTS, andGIT_SSL_CAINFO.http/1.1to ensure reliable plaintext inspection without h2 binary frames.169.254.169.254), loopback, unspecified, and multicast, while keeping RFC1918 private networks reachable for Compose side-services.Agent-UID Kernel Firewall (
src/benchflow/sandbox/lockdown.py)docker-compose-net-admin.yamloverlay when an agent network policy is active.iptables(andip6tablesif dual-stack) rule restricts the non-rootsandbox_userUID strictly to loopback (-o lo). Any direct TCP/UDP packets bypassingHTTP(S)_PROXYfail closed.sandbox_userbefore the first prompt, failing fast if the blocklist or firewall is breached.Provider-Side Model Tool Rewriting (
src/benchflow/providers/litellm_logging.py)web_search_*/web_fetch_*blocked_domains.-c tools.web_search=false) and Gemini.BENCHFLOW_EGRESS_BLOCKED_URLSfrom the agent process env so the agent never learns which URLs are hidden.Batch Safety & CLI Overlays
--block-urland--block-url-filetobench eval runas C-axis config overlays.NetworkPolicyPreflightErrorto validate task runtime support across the entire batch up-front before rollouts start.ResumeMismatchErrorto prevent resuming jobs whose completed tasks ran under a different network policy.agent/egress.jsonlcontaining structured logs of every proxy decision (allow/block/refuse/probe) for auditor verification.Defensive Hardening (WAF & Cross-Platform)
urllib.parse.unquoteandposixpath.normpathwith case-folding, preventing bypasses via URL percent-encoding (%xx), duplicate slashes (//), or dot-segments (/../).Host:request header to thwart IP-direct spoofing attempts.resolve_upstreamtraverses vetted address candidates prioritizing IPv4 to avoidErrno 101 Network is unreachablein standard Docker bridge setups.encoding="utf-8"across all file reading utilities and shim loaders, eliminating Windowscp1252encoding crashes.Validation
Validation was conducted across 5 discrete layers — from pure functional logic up to live end-to-end forward proxying on Linux and full repository baseline regression — followed by explicit disclosure of testing boundaries.
1. Static Gates (L0 CI Parity)
Verified against BenchFlow's CI L0 test workflow requirements:
ruff check .(Clean, 0 errors)ruff format --check .(Clean, all files compliant)ty check(Type checks passed)2. Unit Tests: Pure Logic & Configuration Layer
Isolated unit tests in
tests/test_egress_blocklist.pyverifying protocol and policy logic without external network dependencies:%xx), and path normalization (//and/../).-c tools.web_search=falseunder blocklists; Gemini setup commands were executed in bash to parse emittedsettings.json; non-applicable harnesses remain untouched.blocked_domains,allowed_domainsare narrowed, OpenAI hosted search tools are stripped, and baseline no-web dropping behavior remains intact.NET_ADMINcompose overlay,config.jsonschema updates, resume mismatch guards, and batch preflight conflict detection.3. Orchestration Flow Verification (Mocked Sandbox)
Tested
Rolloutlifecycle usingAsyncMockforenv.execandenv.upload_fileto assert ordering, privilege separation, and error contracts:rootwithchmod 600.sandbox_user, logging outcomes asroot.agent/egress.jsonlwhenever the proxy was started for any scene.4. Real-Proxy End-to-End Testing (Linux / WSL)
The in-sandbox stdlib proxy script was executed as an active subprocess in a Linux environment (Ubuntu 24.04 on WSL) with a live plaintext origin, an HTTPS origin with dynamic OpenSSL certificates, and client traffic sent via
urllib:200; blocked return404CONNECTtunnel to blocked host404immediately without upstream DNS resolution404; untrusted client fails handshake (proves MITM termination)169.254.169.254& loopback)403 Forbiddenand logsrefuseeventleaf.keygenerated across distinct inspected hosts//,.., case)404; neighboring paths answer200http://<IP>/path+Host: target)404; legitimate Host header returns200ok: true5. Full Repository Regression & Baseline Differential
To ensure zero regressions across BenchFlow's test suite, a full test rollout was executed in WSL and diffed against a clean checkout of
main:main(all 15 are known Windows/WSL CRLF checkout line-ending and symlink quirks). Zero new regressions introduced.