Skip to content

Preserve interception for offline Docker agents#1841

Closed
xeophon wants to merge 9 commits into
mainfrom
docker-interception-only
Closed

Preserve interception for offline Docker agents#1841
xeophon wants to merge 9 commits into
mainfrom
docker-interception-only

Conversation

@xeophon

@xeophon xeophon commented Jun 23, 2026

Copy link
Copy Markdown
Member

Overview

Preserves the framework routes required by an offline Docker harness while denying arbitrary agent egress. Trusted setup retains ordinary network access; after setup, the runtime moves to an isolated execution network for the remainder of its lifetime.

Details

  • Adds a runtime network-policy boundary that receives named framework routes and returns the URLs reachable from the sandbox. This keeps model and MCP routing independent from the Docker implementation and gives future sandbox backends one integration point for allowlists and blocklists.
  • Separates runtime placement from both runtime-to-host and host-to-runtime reachability, so tunnel and service exposure decisions describe the actual network topology.
  • Uses a per-runtime setup network, an isolated internal execution network, and one hardened nginx egress container. The agent loses the setup network when execution begins.
  • Gives every required route its own relay port and limits it to the declared URL path, including descendants. Direct host access, DNS, and arbitrary Internet egress remain unavailable, while container loopback still works.
  • Keeps the restriction active until the runtime is destroyed, so agent child processes cannot regain setup networking after the main harness process exits.
  • Supports model routes plus task-scoped, shared, and external MCP routes. Colocated services are rejected when a backend policy cannot preserve their required network direction.
  • Keeps the allow/block policy model internal until target syntax and backend enforcement are ready to become a supported public configuration surface.

Note

Preserve interception for offline Docker agents by routing egress through a restricted network

  • Adds network_access: bool to DockerConfig (docker.py); when False, the agent container is isolated from arbitrary egress after setup and all framework routes are proxied through a local nginx egress container on an isolated execution network.
  • Introduces docker_interception_host() to resolve the Linux bridge gateway IP so the interception server can bind an extra host address reachable from within the restricted container.
  • Extends InterceptionServer, StaticInterceptionPool, and ElasticInterceptionPool (server.py, pool.py) to accept an extra_host parameter and bind on multiple local addresses simultaneously when not tunneling.
  • Replaces runtime_is_local with runtime_reaches_host_locally throughout MCP launch and interception logic to correctly distinguish runtimes that need tunnels from those that reach host services directly.
  • Adds apply_network_policy to the Runtime base class (base.py); Rollout.run calls it after resolving endpoints so the harness runtime can rewrite model and MCP URLs before execution.
  • Risk: containers running with network_access=False use different networks during setup vs. execution; cleanup now removes the egress container and both networks, which may leave resources behind on hard failures.

Macroscope summarized 4d23b5a.


Note

High Risk
Changes security-critical Docker network topology, URL rewriting for all harness traffic, and interception binding; misconfiguration could break rollouts or weaken isolation.

Overview
Introduces restricted agent networking for Docker harnesses via DockerConfig.network_access (default true). When false, setup keeps a normal bridge network; after rollout setup, apply_network_policy moves the agent onto an internal execution network and rewrites model/MCP URLs to per-route nginx egress proxies (egress:8080+N) that only allow the declared paths—arbitrary internet/DNS egress is blocked until teardown.

Adds a shared NetworkPolicy / Runtime.apply_network_policy hook on the base runtime (other backends still error if restricted). Rollout.run passes named routes (model, mcp:*) through this boundary and rejects colocated MCP/user servers when the harness policy cannot support them.

Refactors locality into explicit reachability: runtime_reaches_host_locally, reaches_host_locally, reachable_from_host_locally, and SharedToolServer.reaches_host (replacing local / harness_is_local in tunnel and MCP URL logic). Interception can listen on an extra_host (Docker bridge gateway from docker_interception_host) so containers without host loopback still hit /state and model routes when no public tunnel is used.

Tool/user runtimes with a restricted policy cannot be launched standalone (serve raises); expose failures now surface as SandboxError.

Reviewed by Cursor Bugbot for commit 4d23b5a. Bugbot is set up for automated code reviews on this repo. Configure here.

@xeophon
xeophon marked this pull request as draft June 23, 2026 14:07

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 07e5e51513

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread verifiers/v1/runtimes/docker.py Outdated
@macroscopeapp

macroscopeapp Bot commented Jun 23, 2026

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

This PR introduces significant new Docker networking infrastructure with network policies and egress control. Three unresolved review comments identify high-severity bugs: incorrect bridge gateway resolution, timing issues with interception host computation, and an attribute rename breaking shared tools functionality.

You can customize Macroscope's approvability policy. Learn more.

@xeophon xeophon changed the title Add interception-only Docker networking Preserve interception for offline Docker agents Jun 25, 2026
Comment thread verifiers/v1/runtimes/docker.py Outdated
@xeophon
xeophon marked this pull request as ready for review June 25, 2026 17:35
# Conflicts:
#	verifiers/v1/ARCHITECTURE.md
#	verifiers/v1/GUIDE.md
#	verifiers/v1/interception/pool.py
#	verifiers/v1/interception/server.py
#	verifiers/v1/rollout.py
#	verifiers/v1/runtimes/__init__.py
#	verifiers/v1/runtimes/base.py
#	verifiers/v1/runtimes/docker.py
Comment thread verifiers/v1/rollout.py Outdated
Comment thread verifiers/v1/runtimes/docker.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1ac94fc1e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread verifiers/v1/runtimes/docker.py Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 81fed07. Configure here.

"could not resolve the Docker host gateway: "
f"{(result.stderr or result.stdout).strip()}"
)
return gateway

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong Docker bridge gateway

High Severity

With network_access=False, the harness joins a per-runtime setup bridge, but docker_interception_host() reads the default bridge network gateway. That IP usually differs from the setup network gateway, so host interception and egress host.docker.internal mapping can point at the wrong address and framework routes fail from the container.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 81fed07. Configure here.

Comment thread verifiers/v1/env.py
and not runtime.network_access
and not tunneled
else None
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shared interception host too early

High Severity

Eval serving() fixes extra_host from docker_interception_host() before any rollout creates its setup network, and every rollout shares that single bind address. Per-rollout bridge gateways differ, so shared interception often never listens on the address restricted Docker harnesses use to reach the host.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 81fed07. Configure here.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 81fed07b1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


url: str
local: bool
reaches_host: bool

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the shared-tool locality attribute

Renaming this field leaves serve_tools() still reading server.local when attaching non-external shared tool servers to a rollout, so any Taskset.tools shared server launched by the framework now fails before the harness starts with AttributeError: 'SharedToolServer' object has no attribute 'local'. Update the caller to use reaches_host (or keep a compatible property) so shared tools continue to work.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d23b5acf4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if url is None:
raise SandboxError(f"{service.type} runtime cannot expose port {port}")
yield url
elif consumer_reaches_host: # host network, direct consumer → localhost, no tunnel

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid Prime tunnels for local MCP routes

When the harness is DockerConfig(network_access=False) and a task-scoped or shared MCP tool uses the default non-colocated subprocess runtime, serve() sets consumer_reaches_host to false, so this branch exposes the otherwise local host tool via PrimeTunnel and the Docker sidecar whitelists that public URL. Even after the interception route is kept local, Docker-only offline evals with default host tools still fail on hosts without prime_tunnel credentials or external egress; local framework tool servers need a Docker-bridge/local URL path instead of falling through to the public tunnel branch.

Useful? React with 👍 / 👎.

@xeophon xeophon closed this Jul 18, 2026
@xeophon

xeophon commented Jul 18, 2026

Copy link
Copy Markdown
Member Author

superseded by #2024

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