You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When sandbox-to-sandbox communication ships (#1049), the receiving sandbox needs to verify the identity of the calling sandbox and authorize the request. Today, sandbox supervisors only accept traffic from two sources: the gateway (via the relay stream, authenticated by the gateway's own identity) and the agent process inside the sandbox (via loopback). There is no mechanism for a sandbox to authenticate or authorize an inbound request from another sandbox or external caller.
This gap becomes critical in three scenarios:
1. Multi-agent workflows (#1049): Sandbox A delegates a task to Sandbox B. Sandbox B needs to verify that A is who it claims to be (identity) and that A is allowed to call B (authorization). Without this, any sandbox that can reach B's network endpoint can invoke it.
2. Exposed services: When a sandbox exposes a service via ExposeService RPC, external callers (webhooks, CI systems, other cluster workloads) can reach it. The supervisor accepts these connections without validating the caller's identity.
3. Delegated authority (#2025): When a parent agent spawns a child agent with narrower authority, the child's sandbox needs to verify that inbound instructions come from the authorized parent, not from an unrelated sandbox that discovered the child's endpoint.
Without inbound authentication, cross-sandbox communication is a lateral movement vector: a compromised sandbox can call any other sandbox that exposes a service, with no identity proof required.
Proposed Design
The supervisor's inbound path should validate caller identity using SPIFFE, consistent with how outbound identity already works (PR #1784).
Inbound authentication (identity)
When a request arrives at a sandbox from another sandbox or external caller, the supervisor validates the caller's SPIFFE JWT-SVID (or gateway-minted JWT) via the authenticator chain. The result is a Principal identifying the caller. Requests without a valid token are rejected.
For cross-sandbox traffic, both sandboxes present SPIFFE JWT-SVIDs. The receiving sandbox validates the caller's SVID via its own SPIRE Workload API socket (both share the same trust domain). The SPIFFE ID identifies which sandbox is calling.
Inbound authorization (policy)
The sandbox policy defines which callers are allowed. Options include:
SPIFFE ID allowlist: allow_callers: [spiffe://openshell.local/openshell/sandbox/<uuid>]
Gateway-brokered: all cross-sandbox traffic routes through the gateway, which performs the authorization check using its knowledge of both sandboxes' policies
Audit
Every inbound request should produce an OCSF event recording: caller SPIFFE ID, target sandbox, method called, and disposition (allowed/denied). This is essential for the delegation audit trail required by #2025.
Alternatives Considered
1. Gateway-only mediation: All cross-sandbox traffic routes through the gateway. The gateway authenticates both sides and authorizes the call. Pro: no changes to the supervisor. Con: adds latency (extra hop through gateway), gateway becomes a bottleneck for sandbox-to-sandbox traffic, doesn't work for direct peer connections.
2. Network policy only: Use Kubernetes NetworkPolicy to control which sandboxes can reach each other. Pro: no code changes. Con: network-level only (no identity verification, no per-method authorization, no audit trail). A sandbox that can reach another sandbox's IP can send any request.
3. mTLS between sandboxes: Each sandbox uses its SPIFFE X.509 SVID for mutual TLS to peer sandboxes. Pro: strong transport-level identity. Con: requires TLS termination at the supervisor for inbound traffic, more complex than JWT validation on gRPC metadata. Could complement JWT-SVID validation as defense in depth.
Agent Investigation
From analysis of the current codebase:
crates/openshell-server/src/auth/principal.rs defines Principal::Sandbox which could be extended with caller_sandbox_id for cross-sandbox context
The authenticator chain (crates/openshell-server/src/multiplex.rs) already supports SPIFFE validation; the same logic could be reused in the supervisor
crates/openshell-sandbox/src/grpc_client.rs handles outbound auth; an equivalent inbound path would validate tokens on incoming relay or service requests
OCSF builders in crates/openshell-ocsf/ already cover network activity and SSH authentication events; an inbound auth event type would follow the same pattern
Problem Statement
When sandbox-to-sandbox communication ships (#1049), the receiving sandbox needs to verify the identity of the calling sandbox and authorize the request. Today, sandbox supervisors only accept traffic from two sources: the gateway (via the relay stream, authenticated by the gateway's own identity) and the agent process inside the sandbox (via loopback). There is no mechanism for a sandbox to authenticate or authorize an inbound request from another sandbox or external caller.
This gap becomes critical in three scenarios:
1. Multi-agent workflows (#1049): Sandbox A delegates a task to Sandbox B. Sandbox B needs to verify that A is who it claims to be (identity) and that A is allowed to call B (authorization). Without this, any sandbox that can reach B's network endpoint can invoke it.
2. Exposed services: When a sandbox exposes a service via
ExposeServiceRPC, external callers (webhooks, CI systems, other cluster workloads) can reach it. The supervisor accepts these connections without validating the caller's identity.3. Delegated authority (#2025): When a parent agent spawns a child agent with narrower authority, the child's sandbox needs to verify that inbound instructions come from the authorized parent, not from an unrelated sandbox that discovered the child's endpoint.
Without inbound authentication, cross-sandbox communication is a lateral movement vector: a compromised sandbox can call any other sandbox that exposes a service, with no identity proof required.
Proposed Design
The supervisor's inbound path should validate caller identity using SPIFFE, consistent with how outbound identity already works (PR #1784).
Inbound authentication (identity)
When a request arrives at a sandbox from another sandbox or external caller, the supervisor validates the caller's SPIFFE JWT-SVID (or gateway-minted JWT) via the authenticator chain. The result is a
Principalidentifying the caller. Requests without a valid token are rejected.For cross-sandbox traffic, both sandboxes present SPIFFE JWT-SVIDs. The receiving sandbox validates the caller's SVID via its own SPIRE Workload API socket (both share the same trust domain). The SPIFFE ID identifies which sandbox is calling.
Inbound authorization (policy)
The sandbox policy defines which callers are allowed. Options include:
allow_callers: [spiffe://openshell.local/openshell/sandbox/<uuid>]Audit
Every inbound request should produce an OCSF event recording: caller SPIFFE ID, target sandbox, method called, and disposition (allowed/denied). This is essential for the delegation audit trail required by #2025.
Alternatives Considered
1. Gateway-only mediation: All cross-sandbox traffic routes through the gateway. The gateway authenticates both sides and authorizes the call. Pro: no changes to the supervisor. Con: adds latency (extra hop through gateway), gateway becomes a bottleneck for sandbox-to-sandbox traffic, doesn't work for direct peer connections.
2. Network policy only: Use Kubernetes NetworkPolicy to control which sandboxes can reach each other. Pro: no code changes. Con: network-level only (no identity verification, no per-method authorization, no audit trail). A sandbox that can reach another sandbox's IP can send any request.
3. mTLS between sandboxes: Each sandbox uses its SPIFFE X.509 SVID for mutual TLS to peer sandboxes. Pro: strong transport-level identity. Con: requires TLS termination at the supervisor for inbound traffic, more complex than JWT validation on gRPC metadata. Could complement JWT-SVID validation as defense in depth.
Agent Investigation
From analysis of the current codebase:
crates/openshell-server/src/auth/principal.rsdefinesPrincipal::Sandboxwhich could be extended withcaller_sandbox_idfor cross-sandbox contextcrates/openshell-server/src/multiplex.rs) already supports SPIFFE validation; the same logic could be reused in the supervisorcrates/openshell-sandbox/src/grpc_client.rshandles outbound auth; an equivalent inbound path would validate tokens on incoming relay or service requestsspiffe_endpoint.rsprovides the SPIFFE Workload API client that the supervisor would use for inbound SVID validationcrates/openshell-ocsf/already cover network activity and SSH authentication events; an inbound auth event type would follow the same patternDepends on: #1049 (sandbox-to-sandbox communication path)
Related: #2025 (delegated authority policy), #1056 (offline flow verification), #981 (split-pod model)