Skip to content

RFC-0002: Guest network egress and credential containment#100

Open
cspinetta wants to merge 5 commits into
mainfrom
claude/credential-egress-design
Open

RFC-0002: Guest network egress and credential containment#100
cspinetta wants to merge 5 commits into
mainfrom
claude/credential-egress-design

Conversation

@cspinetta

@cspinetta cspinetta commented Jun 19, 2026

Copy link
Copy Markdown
Member

Summary

This is the discussion PR for RFC-0002: Guest network egress and credential containment (docs/rfc/0002-guest-network-egress-and-credential-containment.md, status Draft).

RFC-0002 specifies a single host-side mechanism governing what the untrusted guest agent can reach on the network and how it authenticates to the endpoints it is allowed to reach. Both concerns rest on one shared, low-privilege host proxy and its per-connection handler pipeline (EgressPolicy, Tunnel, AuditSink, and the credential injector), so the RFC specifies them together.

What the RFC proposes

  • Credential containment. A host-side, TLS-terminating injection proxy, backed by a host credential store, holds durable secrets (API keys and OAuth refresh tokens) and injects them into requests only at egress, so the guest holds non-secret placeholders rather than the real credential. Covers the trust model, the containment invariant, provider terms-of-service constraints, the per-run name-constrained CA, snapshot/restore handling, the cold-boot performance budget, and risk register R1–R14.
  • Egress policy and profiles (open / monitored / allowlist / proxy-only / none), orthogonal to credential containment and holding under any credential configuration. Defines the destination-reachability model, audit, and the split between coarse network-layer enforcement and fine-grained name-based policy at the proxy, with risk register E1–E7.

Problem

The claude-personal and codex providers stage OAuth credentials — including the refresh token — into the guest, and the API-key providers forward the raw key into the guest exec environment. A uid-1000 agent (via prompt injection or a compromised dependency) can read and exfiltrate these for access that outlives the run. Separately, there is today no way to restrict, audit, or rate-limit which destinations the guest may reach.

Process

Per the RFC + ADR process (RFC-0001), this PR is the proposal and the venue for discussion and alternatives. Once accepted, the load-bearing decisions are distilled into ADRs, and implementation PRs link back to the RFC and ADR numbers.

@cspinetta cspinetta force-pushed the claude/credential-egress-design branch from 418d169 to be727e9 Compare June 21, 2026 05:02
@cspinetta cspinetta changed the title docs: credential-broker + egress-policy design RFC-0002: Guest network egress and credential containment Jun 21, 2026
@cspinetta cspinetta force-pushed the claude/credential-egress-design branch 12 times, most recently from 754b658 to f049dd3 Compare June 22, 2026 20:44
RFC-0002 (Draft) specifies one host-side mechanism governing what the untrusted guest agent can reach on the network and how it authenticates to the endpoints it may reach. Both concerns share a single low-privilege host proxy and its per-connection pipeline (`EgressPolicy`, `Tunnel`, `AuditSink`, and the credential injector), so the RFC specifies them together.

Credential containment keeps durable secrets — API keys and OAuth refresh tokens — off the guest, injecting them at egress through a TLS-terminating proxy backed by a host credential store; the guest holds only non-secret placeholders. Downstream services declare injection through a `CredentialSpec` binding an upstream host to a header and a host-side secret source.

Egress policy governs reachability, audit, and routing through a per-run profile (`open` default, `monitored`, `allowlist`, `proxy-only`, `none`), split between coarse network-layer pinning and fine-grained name-based policy at the proxy. Connection-to-run attribution is structural: a per-run rule denies a run every gateway hop but its own proxy listener, DNS, and enabled host-service ports, so one run cannot reach another's listener over the shared loopback. The per-run token authenticates on top — defense-in-depth on KVM, the primary cross-run control on VZ, where enforcement is in-guest. The RFC also covers cold-boot provisioning, snapshot-restore re-minting of the token and CA, per-box override semantics, the host-service exemption, and a `voidbox egress report` harvesting command.

Index the RFC as 0002 in the RFC README; load-bearing decisions become ADRs once it is accepted.
Add design-doc prose conventions in docs/agents/doc-style.md, referenced from AGENTS.md, so design docs read as practical descriptions of what is built rather than prose that taxes the reader. Introduce a named component before referring to it — define what it is, who creates it, where it lives, its lifetime, and the problem it solves on first mention, then use the definite article. State the mechanism before claiming the property it produces, so a guarantee is earned rather than asserted, and avoid hype adjectives that assert quality without showing it. Don't narrate the document — cut sentences whose subject is the doc's own structure. Prefer the field's standard vocabulary over coined shorthand, metaphor, or unexpanded abbreviations. Each rule names the habit that causes the defect so reviewers can catch it at the point of use.
@cspinetta cspinetta force-pushed the claude/credential-egress-design branch from f049dd3 to 20e146e Compare June 22, 2026 21:16
… diagrams

Replace the under-specified claim that the VZ reach floor is the existing blackhole-route deny-list "extended to express pin-to-proxy". That primitive matches on destination prefix only and cannot express the port-aware, default-deny allow-list pin-to-proxy requires (a run's own listener and a neighbor's differ by port on a shared gateway). VZ enforcement is instead a root-attached eBPF cgroup `connect4`/`connect6` (plus `sendmsg4`/`sendmsg6`) program that allow-lists egress by `(address, port)`, denies the rest with `EPERM`, and uses a `connect4` destination rewrite as the in-guest equivalent of the KVM DNAT. The required `CONFIG_CGROUP_BPF` and cgroup v2 are already present on both the production VZ kernel and the slim dev/test kernel, so no kernel rebuild is needed.

Scope the guarantee to the threat model: against the modeled uid-1000 adversary — which holds no `CAP_BPF`/`CAP_NET_ADMIN`/`CAP_NET_RAW` — the filter is a boundary, not merely defense-in-depth; only a uid-1000→root escalation (kernel LPE or a guest-agent exploit), outside the primary model, can subvert it. The trust model now states explicitly that the only root in the guest is the trusted guest-agent (PID 1) while the workload runs as uid 1000.

Add two mermaid diagrams to Connection attribution — per-run listeners (the arrival socket is the run's identity) and the KVM-vs-VZ enforcement point — and an Attribution-reliability paragraph that ties audit attribution to the per-run binding and bounds the beyond-model residual. Record the alternatives (a host userspace stack à la gvproxy as the bypass-resistant long-term answer, deferred to its own RFC; in-guest nftables) and why eBPF was chosen, plus the new risk (E8), the VZ uid-1000 bypass test, and the affected guest-agent and build-pipeline code.
Spell out Server-Side Request Forgery (SSRF) at its first occurrence and add a short gloss of the confused-deputy risk in R3, per the doc-style rule to expand abbreviations on first use. The terms stay (standard security terms of art); only the first-use expansion is added.
Clarify that selective routing (only credentialed clients pointed at the proxy) is the default-profile behavior; restrictive egress profiles route all traffic through the same proxy. Cross-reference §C so a reader of the credential-containment section does not over-generalize from the credential path to all egress.
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