RFC 0005: Sandbox proxy egress adapter model#2155
Conversation
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>
|
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 The RFC's local service adapter category is the natural home for this, but Phase 8 currently models only the three existing services ( A few questions:
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 |
|
@maruiz93 have you tried using network_policies:
host_service:
name: host_service
endpoints:
- host: host.openshell.internal
port: 8080
binaries:
- path: /usr/bin/curlThe 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 |
|
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:
- "{{HOST_IP}}/32" # rendered at runtime by the orchestrator2. Servers must bind to 3. The bridge IP varies by driver. Rootless Podman + pasta uses a link-local address ( These are the problems #1633 targets. The supervisor already solves the structurally identical problem for The experiment findings document all of this in detail: findings — "API servers must bind to all interfaces". |
russellb
left a comment
There was a problem hiding this comment.
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.
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
|
@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. |
| - Middleware can allow, deny, replace the bounded request body, add approved | ||
| headers, and emit audit-safe findings/metadata. External middleware must not | ||
| receive OpenShell-managed credentials. |
There was a problem hiding this comment.
Worth mentioning that the protocol enforcement is re-evaluated after the middleware, so the middleware cannot modify the request in a way that could bypass request policy (applicable to at least things like graphql, json-rpc, mcp).
| 1. Should overlapping endpoint metadata be rejected at policy load time, or | ||
| should one documented policy/endpoint precedence key select the complete | ||
| decision? The initial compatibility refactor does not choose between them. |
There was a problem hiding this comment.
One datapoint on this - because of the overlapping endpoint metadata, I initially removed the ability to attach the middleware on the endpoint config.
Here's the context: #1738 (comment)
Excerpt
Why explicit policy/endpoint attachment was removed
OpenShell network policies are an additive bag of grants, not a hierarchy with one canonical winning rule. A request is allowed when at least one policy matches, and several policies may match the same request. Those policies may come from different sources: the user-authored base policy, provider-derived policy, or merged policy chunks/proposals.
That makes attachment semantics ambiguous. For example:
- Policy A allows a request and attaches middleware, while policy B also allows it without middleware. Should the middleware run?
- Policy A attaches chain
[x]and policy B attaches[y]. Should OpenShell choose one, union them, reject the overlap, or invent cross-policy ordering? - A broad provider-derived rule and a specific user rule may both admit the request. Neither is inherently the parent or winner.
Choosing the matched policy as the attachment source could also make enforcement depend on policy provenance or allow an overlapping grant without middleware to bypass a middleware-bearing grant.
There was a problem hiding this comment.
Actually I wonder if this could be an issue right now, if there are 2 endpoints, one with json-rpc protocol and another one raw, and the raw one may be selected ahead of the json-rpc one?
| Transparent TCP is for native clients that do not know they are using a proxy. | ||
| It depends on policy DNS and nftables capture: DNS answers create correlated | ||
| active endpoint mappings, userland later calls `connect(ip:port)`, nftables | ||
| redirects matching traffic to a userland listener, and the TCP adapter recovers | ||
| the original destination before building an intent. |
There was a problem hiding this comment.
[agent review]
Question about this mechanism:
and the TCP adapter recovers the original destination before building an intent
In order to map between the original DNS call and the connect(ip:port) there needs to be something to correlated between these 2.
E.g. the DNS lookup returns a synthetic IP, which then is used for connect and based on which we can find the mapping that was created during the DNS lookup. Potentially, the process identity could be used as well (DNS + later connect is from the same process).
Summary
Drafts RFC 0005 for reshaping sandbox proxy egress around transport adapters, shared
EgressIntent/EgressDecisionauthorization, 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
Testing
mise run pre-commitpassesRFC-only documentation change. Ran
git diff --checkand ASCII scans on the RFC files. Pre-commit was intentionally not run for this RFC draft.Checklist