rfc-0012: Isolation Backend interface - #2048
Conversation
|
All contributors have signed the DCO ✍️ ✅ |
|
I have read the DCO document and I hereby sign the DCO. |
Tighten RFC 0012 for review without changing its structure or tone: - Remove the undefined `BoundaryIntent` from `attach`, and state that `claim` is the only transition that binds sandbox identity, policy, agent, and resources; an attached boundary holds no claimed or untrusted workload. - Add the runtime interface definitions (`BoundaryExec`/`ExecSession`, `BoundaryPortForward`, `EventSource`) and their semantics: owned distinct stdio, PTY resize, placement-neutral exit and signals, stable repeated `wait`, validated loopback targets, and single-consumer events. - Make boundary confirmation mandatory (`MUST`) and lifetime-long, and state retry, termination, and cleanup semantics (only attach is auto-retryable; a lost `start_agent` returns the existing process; descendants are owned; `wait_terminated` and an idempotent `shutdown`). - Require trusted admission to supply the expected backend id, contract version, policy digest, and capabilities that a backend cannot lower; add the no-silent-weakening policy invariant; mark `VerifiedBoundaryDescriptor` privately constructed and distinguish envelope from contract version; make `ResourceBinding` driver-issued, bound, and non-wideable. - Make the rollout executable and forbid a silent default backend. topology-matrix: rename "sidecar proxy" to sidecar-assisted (the proxy stays with the supervisor), frame sidecar and node as composite backends, move the single-pod outer sandbox to the shared-kernel cell, and note a node enforcer alone is not `restricted`-compatible. codebase-grounding: re-pin anchors to the RFC's parent ba21bb3 (driver.rs and proxy.rs line numbers refreshed) and add a permalink base; correct the event wording so a denial carries `Evidence` plus request-specific L7 data. Set state: review and link PR NVIDIA#2048. Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
Tighten RFC 0012 for review without changing its structure or tone: - Remove the undefined `BoundaryIntent` from `attach`, and state that `claim` is the only transition that binds sandbox identity, policy, agent, and resources; an attached boundary holds no claimed or untrusted workload. - Add the runtime interface definitions (`BoundaryExec`/`ExecSession`, `BoundaryPortForward`, `EventSource`) and their semantics: owned distinct stdio, PTY resize, placement-neutral exit and signals, stable repeated `wait`, validated loopback targets, and single-consumer events. - Make boundary confirmation mandatory (`MUST`) and lifetime-long, and state retry, termination, and cleanup semantics (only attach is auto-retryable; a lost `start_agent` returns the existing process; descendants are owned; `wait_terminated` and an idempotent `shutdown`). - Require trusted admission to supply the expected backend id, contract version, policy digest, and capabilities that a backend cannot lower; add the no-silent-weakening policy invariant; mark `VerifiedBoundaryDescriptor` privately constructed and distinguish envelope from contract version; make `ResourceBinding` driver-issued, bound, and non-wideable. - Make the rollout executable and forbid a silent default backend. topology-matrix: rename "sidecar proxy" to sidecar-assisted (the proxy stays with the supervisor), frame sidecar and node as composite backends, move the single-pod outer sandbox to the shared-kernel cell, and note a node enforcer alone is not `restricted`-compatible. codebase-grounding: re-pin anchors to the RFC's parent ba21bb3 (driver.rs and proxy.rs line numbers refreshed) and add a permalink base; correct the event wording so a denial carries `Evidence` plus request-specific L7 data. Set state: review and link PR NVIDIA#2048. Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
efaeff7 to
c31cbda
Compare
bb9cefb to
b0b6a44
Compare
|
I'd be interested to see if we can support MXC using this interface, #2071. |
|
@maxamillion @derekwaynecarr @mrunalp thoughts? |
e19656f to
0924382
Compare
|
@TaylorMutch thanks for the review and discussion last week. Based on our discussion, I've made the following changes:
I've also updated my POC implementation for the current in-pod supervisor topology to align with these updates. Please let me know what you all think! |
|
This pull request has had no activity for 14 days and is now marked stale. It may be closed in 7 days if there is no further activity. |
Tighten RFC 0012 for review without changing its structure or tone: - Remove the undefined `BoundaryIntent` from `attach`, and state that `claim` is the only transition that binds sandbox identity, policy, agent, and resources; an attached boundary holds no claimed or untrusted workload. - Add the runtime interface definitions (`BoundaryExec`/`ExecSession`, `BoundaryPortForward`, `EventSource`) and their semantics: owned distinct stdio, PTY resize, placement-neutral exit and signals, stable repeated `wait`, validated loopback targets, and single-consumer events. - Make boundary confirmation mandatory (`MUST`) and lifetime-long, and state retry, termination, and cleanup semantics (only attach is auto-retryable; a lost `start_agent` returns the existing process; descendants are owned; `wait_terminated` and an idempotent `shutdown`). - Require trusted admission to supply the expected backend id, contract version, policy digest, and capabilities that a backend cannot lower; add the no-silent-weakening policy invariant; mark `VerifiedBoundaryDescriptor` privately constructed and distinguish envelope from contract version; make `ResourceBinding` driver-issued, bound, and non-wideable. - Make the rollout executable and forbid a silent default backend. topology-matrix: rename "sidecar proxy" to sidecar-assisted (the proxy stays with the supervisor), frame sidecar and node as composite backends, move the single-pod outer sandbox to the shared-kernel cell, and note a node enforcer alone is not `restricted`-compatible. codebase-grounding: re-pin anchors to the RFC's parent ba21bb3 (driver.rs and proxy.rs line numbers refreshed) and add a permalink base; correct the event wording so a denial carries `Evidence` plus request-specific L7 data. Set state: review and link PR NVIDIA#2048. Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
Introduces RFC 0012, the runtime-selectable Isolation Backend contract: a pluggable component that establishes and enforces an agent's isolation boundary across network, filesystem, syscall, and identity, while the supervisor stays the policy authority (proxy, policy, audit) and the agent's only egress. Includes the supporting topology matrix and codebase-grounding notes. Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
Tighten RFC 0012 for review without changing its structure or tone: - Remove the undefined `BoundaryIntent` from `attach`, and state that `claim` is the only transition that binds sandbox identity, policy, agent, and resources; an attached boundary holds no claimed or untrusted workload. - Add the runtime interface definitions (`BoundaryExec`/`ExecSession`, `BoundaryPortForward`, `EventSource`) and their semantics: owned distinct stdio, PTY resize, placement-neutral exit and signals, stable repeated `wait`, validated loopback targets, and single-consumer events. - Make boundary confirmation mandatory (`MUST`) and lifetime-long, and state retry, termination, and cleanup semantics (only attach is auto-retryable; a lost `start_agent` returns the existing process; descendants are owned; `wait_terminated` and an idempotent `shutdown`). - Require trusted admission to supply the expected backend id, contract version, policy digest, and capabilities that a backend cannot lower; add the no-silent-weakening policy invariant; mark `VerifiedBoundaryDescriptor` privately constructed and distinguish envelope from contract version; make `ResourceBinding` driver-issued, bound, and non-wideable. - Make the rollout executable and forbid a silent default backend. topology-matrix: rename "sidecar proxy" to sidecar-assisted (the proxy stays with the supervisor), frame sidecar and node as composite backends, move the single-pod outer sandbox to the shared-kernel cell, and note a node enforcer alone is not `restricted`-compatible. codebase-grounding: re-pin anchors to the RFC's parent ba21bb3 (driver.rs and proxy.rs line numbers refreshed) and add a permalink base; correct the event wording so a denial carries `Evidence` plus request-specific L7 data. Set state: review and link PR NVIDIA#2048. Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
- Introduced topology nomenclature to align with how we've been talking about this in other conversations - Simplified the explanation in the proposal section - Clarified binary identity - Simplified the initial contract to require the minimal interface necessary to satisfy all known topologies - Confirmed this will work with the proposed mxc (RFC 0013) proposal
- Repin codebase-grounding.md to 8eacb47 (sidecar supervisor topology, NVIDIA#2076); update capabilities line numbers (1534→2538, 1540→2544), init container line numbers (191→423, 993→1506, 1185→2113), and remove the stale "no native sidecars today" claim. Add openshell-network-init and openshell-supervisor-network sidecar entries and expand the rg pattern. - Add Implementation column to topology-matrix.md; mark Co-located/in-pod and Same-pod composite as implemented (original topology and NVIDIA#2076 respectively); remaining patterns noted as proposed. Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
All line numbers and function names verified against the post-sidecar state of the codebase (commit 8eacb47, NVIDIA#2076). Changes: - process.rs: ProcessHandle::spawn 440→527, netns param 446→535; drop_privileges call sites 603/700→710/812, enforcement 613/705→721/818-819; enter_netns_and_sandbox now documented at ssh.rs:1245 - CLONE_NEWNET call sites: process.rs:589→695, ssh.rs:619/1186→653/1262, supervisor_session.rs:610→735, netns/mod.rs:363→342 (226 unchanged) - CLONE_NEWNS: was one unshare at :393; now unshare at :449 and a new setns at :480 added for sidecar mount-namespace entry - nft fail-open: line 264→265, return Ok(()) range 272-277→277; note that the sidecar path (netns/mod.rs:477) requires nft and returns an error if absent, fixing the invariant bug for the sidecar topology - nft_ruleset.rs: policy accept 41→53; accept rules 43-49→56-92; reject rules now at 106+ - VM driver MASQUERADE: runtime.rs:417/436→418/437 - Agent command: main.rs:331→601; sleep infinity driver.rs:1886→2937, clarify it is set via SANDBOX_COMMAND env var - OPA evaluation: proxy.rs:1611 / evaluate_opa_tcp renamed to authorize_egress_intent at proxy.rs:1955; NetworkInput built at :2032 - openshell.proto: clarify no lifecycle Attach; note AttachSandboxProvider (provider record attachment, not isolation lifecycle) - README.md appendix: update pinned commit reference a5161d0→8eacb477 Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
0924382 to
3f5bc31
Compare
russellb
left a comment
There was a problem hiding this comment.
Reviewed this against the in-progress cni-sidecar supervisor topology (#2606), which moves nftables installation from an in-pod NET_ADMIN init container to a node CNI DaemonSet. It lines up well with this RFC — it's a concrete instance of the "Delegated backend components" row, and the direction (privilege out of the agent container, one supervisor operating the boundary) is exactly what we built toward.
Most of my notes are inline. The substantive one is invariant 6: as written it assumes the backend can detect standing-enforcement loss at runtime, which a node-delegated backend structurally can't do from inside the pod. I think the contract needs a small amount of give for delegated backends there and in the Ready/confirmation wording; everything else is either a clean fit or a mechanical reshape on our side.
Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
|
@drew @russellb I pushed commit Please let me know how your testing goes with this and if there's any other changes you'd like to see. |
Keep RFC 0012 focused on placing binary-aware enforcement behind the Isolation Backend contract instead of requiring a new connection-initiation capture mechanism. Define identity as trusted resolution for an accepted connection before policy evaluation, retain fail-closed attribution requirements, and document the current authorization-time procfs behavior. This preserves RFC 0002's binary gating baseline while allowing future backends to provide stronger identity resolution without changing the contract. Signed-off-by: Jordan Ganoff <jordan.ganoff@docker.com>
|
I realized I had inadvertently changed the contract for how binary identity is resolved, and that shouldn't be part of RFC 0012. 86dc3f2 preserves RFC 0002's binary gating baseline while allowing future backends to provide stronger identity resolution without changing the contract. |
|
We're planning to implement RFC 12 for Agent Substrate, which lands as a delegated backend: a thin in-process implementation carrying its own transport, with delegation staying topology-private. My question is what conformance can cover for that shape. It's specified per backend and topology, so a delegated topology does get tested. What I can't tell is how much of its behavior the suite reaches. If the transport inside the topology is backend-private, the tests exercise the in-process trait while the load-bearing part happens over a link they never cross, and two delegated backends could pass the same suite while behaving differently in ways it stays silent on. So the concrete question: does conformance for a delegated topology stop at the trait boundary, or is there a point where the delegated transport becomes versioned contract surface? The alternatives section says
We may well be that concrete backend, so it would help to know what "requires" means in practice: a backend that can't be built without it, or one where conformance coverage argues for it. If it's the second, we can bring the specifics from our side rather than leave it abstract. Happy to write it up as a proposal if that's more useful than a question. |
@mayawang if it's not too much trouble a proposal would be great. We have an initial prototype of this RFC starting here, #3151. We could quickly prototype your proposal against that PR to make sure we have everything we need in the interface. You might also be interested in this doc, https://docs.google.com/document/d/11iR15xUgncckT50aBsDQgDVfCWrUU3nIh6Woc8IYaxI/edit?tab=t.0#heading=h.bwtw9rv4ec9q. We discussed this at our last community call and it outlines how we want to think about a concrete implementation (#3151) of the RFC for our existing drivers. We also have an initial set of conformance tests here, https://github.com/NVIDIA/OpenShell/tree/main/crates/openshell-conformance. Our conformance tests will let you standup a OpenShell deployment in any specific configuration. Once that deployment is available we can run tests against that configuration. I would expect this to be a good starting point to ensure that the substrate implementation is conformant. If you notice anything missing in the conformance tests we should be able to include additional tests as well. |
Addresses PR feedback: Removed the implementation plan and codebase grounding, renamed the forwarding primitive to BoundaryLoopbackConnector, and clarified that conformance tests exercise the deployed topology, including delegated components and their transport.
|
Thanks, @drew and @mayawang. I removed the implementation plan and codebase grounding, renamed the forwarding primitive to I'd like to leave the TCP/DNS interface details open for implementation work (see comment above). |
Yes, that covers it. "Exercises each backend in its deployed topology, including delegated components and their transport" is the part I couldn't tell from the earlier text, and it settles the question: the suite crosses the delegated link, so what's left is which behaviors it asserts rather than where it stops. Thanks for making it explicit. No view from me on the TCP/DNS lanes. Leaving them to implementation work seems reasonable. One thing I'll take to #2941 rather than here, flagging it so it isn't a surprise. Our boundary is a gVisor sandbox, and |
Summary
Adds RFC 0012 for the Isolation Backend, a proposed pluggable component that establishes and enforces an agent's isolation boundary.
OpenShell runs untrusted agent code inside an isolation boundary: the network, filesystem, syscall, and identity constraints that decide what the agent can reach and what can reach it. Building that boundary takes privilege, and today that privilege lives inside the agent's own container, beside the code it is meant to confine. That blocks restricted and multi-tenant clusters, whose Pod Security Standards reject the capability set the in-pod setup needs.
This RFC proposes making the boundary a pluggable component, the Isolation Backend, that separates the privileged work that builds the boundary from the supervisor that operates it as the policy authority (proxy, policy, audit) and the agent's only egress. The supervisor drives any backend through one runtime contract, so the privileged setup can move out of the agent's container (a sidecar, a separate pod, a microVM, a node component, and eventually outside the agent's kernel) without changing how the supervisor operates it. Each placement is a new backend, not a new supervisor.
Feedback especially welcome on: the lifecycle state boundaries and what each one guarantees, the provenance-based identity and attestation model, and the resource-binding split between the backend and the compute driver.
Related to #1737.
Checklist