Skip to content

sec(server): add per-sandbox authorization to gRPC RPCs #40

Closed as duplicate

Description

@pimlock

Problem

All sandbox pods and CLI tools share a single mTLS client certificate (CN=navigator-client). The server authenticates that the client holds a valid cert but has no mechanism to verify which sandbox is calling. Sandbox identity is determined entirely by the sandbox_id field in the protobuf request message, which is not verified against the TLS connection.

Impact

A compromised sandbox pod could:

  • Read another sandbox's policy via GetSandboxPolicy(sandbox_id="other-sandbox")
  • Read another sandbox's provider credentials via GetSandboxProviderEnvironment(sandbox_id="other-sandbox")
  • (After issue fix: use github backend for sccache in mise.toml #78) Spoof policy load status via ReportPolicyStatus(sandbox_id="other-sandbox")

Current Mitigations

  • mTLS prevents unauthenticated access (the channel is encrypted and authenticated)
  • Kubernetes pod isolation limits lateral movement
  • Sandbox IDs are UUIDs (not guessable, but enumerable via ListSandboxes)

Root Cause

  1. crates/navigator-bootstrap/src/pki.rs:35-98 — PKI generates a single shared client cert
  2. crates/navigator-server/src/sandbox/mod.rs:681-710 — Same navigator-client-tls secret mounted in all pods
  3. crates/navigator-server/src/multiplex.rs:44-56 — No tonic interceptor extracts client cert identity
  4. crates/navigator-server/src/grpc.rs:517-580 — Handlers call request.into_inner() discarding connection metadata

Proposed Fix Options

Option A: Per-sandbox client certificates

  • Extend crates/navigator-bootstrap/src/pki.rs to generate unique client certs per sandbox with sandbox_id in the SAN
  • Create per-sandbox Kubernetes secrets
  • Server extracts SAN from peer cert in a tonic interceptor and validates against sandbox_id in request

Option B: Tonic interceptor with Kubernetes identity

  • Add a tonic interceptor/middleware that extracts peer cert info from the TLS connection
  • Inject verified identity into Request::extensions() for handler consumption
  • Handlers compare extracted identity against sandbox_id in request

Option C: Service mesh or network policy

  • Use Kubernetes NetworkPolicy to restrict sandbox-to-server traffic to only its own sandbox_id
  • Less code change but relies on infrastructure-level controls

Affected RPCs

Related

Discovered during security review of issue #78 (live policy updates).


Originally by @johntmyers on 2026-02-25T08:58:12.857-08:00

Activity

  1. pimlock commented on Feb 27, 2026

    @pimlock
    CollaboratorAuthor

    I believe what we have is currently sufficient for single-player mode.

    This ticket is worth addressing in the context of enterprise multi-player mode. Consider something like spiffe/spire or integrating w/ existing enterprise customer solutions for service-to-service comms.


    Originally by @drew on 2026-02-25T09:44:01.878-08:00

  2. johntmyers commented on Mar 9, 2026

    @johntmyers
    Collaborator

    I've removed the security label as this is out of scope for initial release as we expect a single human controller of all sandboxes for a given deployment for initial release.

  3. drew commented on Mar 23, 2026

    @drew
    Collaborator

    Will have a RFC for this

  4. pimlock commented on Mar 23, 2026

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:gatewayGateway server and control-plane work

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions