Skip to content

RFC 0005: Sandbox proxy egress adapter model#2155

Open
johntmyers wants to merge 9 commits into
mainfrom
0005-sandbox-proxy-egress-adapter/jm
Open

RFC 0005: Sandbox proxy egress adapter model#2155
johntmyers wants to merge 9 commits into
mainfrom
0005-sandbox-proxy-egress-adapter/jm

Conversation

@johntmyers

Copy link
Copy Markdown
Collaborator

Summary

Drafts RFC 0005 for reshaping sandbox proxy egress around transport adapters, shared EgressIntent / EgressDecision authorization, and relay-owned byte handling. The proposal covers CONNECT, forward HTTP, DNS, local services, native TCP, supervisor middleware, credential injection, WebSocket/body injection, JSON-RPC, MCP, and optional process identity for sidecar-style modes.

Related Issue

N/A - RFC proposal.

Changes

  • Adds RFC 0005 for the sandbox proxy egress adapter model.
  • Splits current-state notes, technical details, and implementation plan into RFC appendix documents.
  • Documents flow diagrams for unified adapters, relay handling, DNS/native TCP, local services, and deployment modes.
  • Accounts for recent networking changes including nftables, supervisor middleware, JSON-RPC/MCP L7 support, and identity-less runtime modes.

Testing

  • mise run pre-commit passes
  • Unit tests added/updated
  • E2E tests added/updated (if applicable)

RFC-only documentation change. Ran git diff --check and ASCII scans on the RFC files. Pre-commit was intentionally not run for this RFC draft.

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>
Signed-off-by: John Myers <johntmyers@users.noreply.github.com>
Signed-off-by: John Myers <johntmyers@users.noreply.github.com>
Signed-off-by: John Myers <johntmyers@users.noreply.github.com>
Signed-off-by: John Myers <johntmyers@users.noreply.github.com>
Signed-off-by: John Myers <johntmyers@users.noreply.github.com>
Signed-off-by: John Myers <johntmyers@users.noreply.github.com>
Signed-off-by: John Myers <johntmyers@users.noreply.github.com>
@maruiz93

maruiz93 commented Jul 8, 2026

Copy link
Copy Markdown

Thanks for this RFC — the adapter/authorization/relay separation is a clean model.

I wanted to flag #1633 (supervisor-proxied host-local endpoints) as a consumer of this design. The host.local / host_local: true pattern proposed there is structurally identical to inference.local — the supervisor proxies a CONNECT request to 127.0.0.1:<port> on the host instead of resolving DNS and connecting externally. It works across all deployment modes in the RFC's table because the supervisor always runs in the host/pod network namespace.

The RFC's local service adapter category is the natural home for this, but Phase 8 currently models only the three existing services (inference.local, policy.local, metadata loopback) without a generalization step for policy-declared host-local endpoints.

A few questions:

  1. Could Phase 8 include a step for policy-declared host-local endpoints, even if implementation is deferred? Designing the adapter boundary to accommodate this now would avoid bolting it on later.

  2. In the context of the EgressTransport enum, would a reserved hostname like host.local become a new local adapter (Option A from feat(proxy): supervisor-proxied host-local endpoints — generalize the inference.local pattern for arbitrary host services #1633), or would a host_local: true flag on a standard endpoint trigger routing through the existing LocalHttp transport (Option B)?

  3. The shared destination validation in Phase 2 centralizes SSRF checks — host-local endpoints bypass DNS entirely (the supervisor connects to 127.0.0.1 directly). Does the destination validator need an explicit carve-out for host-local routing, similar to how inference.local bypasses OPA network policy today?

Our use case: we're designing host-side API servers for fullsend (container builder, repo provisioner) and have an ADR and early experiments around it. Today the experiments bind to 0.0.0.0 with allowed_ips — the workaround described in #1633. Supervisor-proxied host-local endpoints would eliminate the network exposure and make the setup driver-agnostic, and we'd like to build on a stable foundation rather than the workaround.

@johntmyers

Copy link
Copy Markdown
Collaborator Author

@maruiz93 have you tried using host.openshell.internal? so something like:

network_policies:
  host_service:
    name: host_service
    endpoints:
      - host: host.openshell.internal
        port: 8080
    binaries:
      - path: /usr/bin/curl

The service you are running will need to be addressable by the driver infra you choose. For example with Docker the service you are running will need to be reachable by other containers in order for the sandbox workload to reach it through host.openshell.internal.

@maruiz93

Copy link
Copy Markdown

Yes — we tested exactly this pattern in fullsend-ai/experiments#28 (merged, 6 harness configs across 3 discovery methods × 2 network policies). It works, but requires three manual workarounds that motivate #1633:

1. allowed_ips with runtime IP templating. SSRF protection blocks private IPs by default, so the policy needs an explicit allowed_ips entry for the host bridge IP. That IP isn't known until runtime — the orchestrator resolves it with getent hosts host.openshell.internal and templates it into the policy via sed. Without this, the proxy blocks the connection. Your snippet above would hit that.

allowed_ips:
  - "{{HOST_IP}}/32"   # rendered at runtime by the orchestrator

2. Servers must bind to 0.0.0.0. On rootless Podman + pasta, the bridge gateway IP (e.g., 10.88.0.1) lives inside the container namespace — bind() from the host fails with EADDRNOTAVAIL. So host services can't bind to a specific internal interface; it's 0.0.0.0 or nothing, which exposes them on all network interfaces. Bearer token auth mitigates but doesn't eliminate the attack surface.

3. The bridge IP varies by driver. Rootless Podman + pasta uses a link-local address (169.254.1.2), Docker uses a bridge gateway (172.17.0.1), Kubernetes needs explicit host_gateway_ip config, and on macOS/WSL2 the bridge IP is often unreachable from the host entirely (#1358, #811).

These are the problems #1633 targets. The supervisor already solves the structurally identical problem for inference.local — it runs in the host/pod netns, intercepts the CONNECT request, and connects to 127.0.0.1 on the host side. Generalizing that mechanism to policy-declared endpoints would let host services bind to loopback (never network-exposed), eliminate the allowed_ips templating step, and work identically across all drivers.

The experiment findings document all of this in detail: findings — "API servers must bind to all interfaces".

@russellb russellb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Agent-assisted design review (principal-engineer pass, grounded in the current openshell-supervisor-network code). The direction is sound and the current-state findings check out — comments below focus only on actionable gaps. Inline notes cover: a leaky EgressDecision seam, scope (net-new capabilities bundled with the refactor), the identity-less fail-open change, generation-pinning, and migration/operational gaps.

Comment thread rfc/0005-sandbox-proxy-egress-adapter/technical-design.md
Comment thread rfc/0005-sandbox-proxy-egress-adapter/technical-design.md
Comment thread rfc/0005-sandbox-proxy-egress-adapter/technical-design.md Outdated
Comment thread rfc/0005-sandbox-proxy-egress-adapter/technical-design.md Outdated
Comment thread rfc/0005-sandbox-proxy-egress-adapter/technical-design.md Outdated
Comment thread rfc/0005-sandbox-proxy-egress-adapter/implementation-plan.md Outdated
Comment thread rfc/0005-sandbox-proxy-egress-adapter/README.md
Comment thread rfc/0005-sandbox-proxy-egress-adapter/current-shape.md
Comment thread rfc/0005-sandbox-proxy-egress-adapter/current-shape.md
Comment thread rfc/0005-sandbox-proxy-egress-adapter/README.md Outdated
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
@johntmyers

Copy link
Copy Markdown
Collaborator Author

@maruiz93 Thanks for the detailed #1633 context. c18e06a now names host-local endpoints as a prospective consumer of these boundaries and keeps the local-routing and destination contracts extensible for that work. The technical design says this should be an explicit local-routing adapter or destination mode, not a general loopback or SSRF exemption; authorization must happen before the supervisor opens the host-loopback connector, and supported runtimes must advertise the capability. RFC 0005 intentionally does not choose reserved hostname versus endpoint flag or add the feature to its compatibility milestone, so those policy and driver details remain in separate #1633 feature design while reusing EgressIntent, adapter-specific responses, and the unopened connector boundary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

3 participants