Skip to content

feat(sandbox): add the in-sandbox agent and the image bundle it ships in - #390

Open
ItamarZand88 wants to merge 1 commit into
itamar/alien-75-sandbox-1-corefrom
itamar/alien-75-sandbox-2-agent
Open

feat(sandbox): add the in-sandbox agent and the image bundle it ships in#390
ItamarZand88 wants to merge 1 commit into
itamar/alien-75-sandbox-1-corefrom
itamar/alien-75-sandbox-2-agent

Conversation

@ItamarZand88

@ItamarZand88 ItamarZand88 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the agent that runs inside a sandbox and executes a caller's code, plus the image bundle it ships in. Layer 1 added the Sandbox resource type; this adds the process that actually runs something inside one.

When a caller asks a sandbox to run a command:

  1. The agent authorises the request — a signed capability, or the transport itself where the cloud already scopes the caller to one sandbox.
  2. It resolves any path the request names through the kernel, so a path that would leave the session root is refused rather than checked and then opened. This is the heart of the change.
  3. It spawns the command with a fresh environment, as an unprivileged user, in its own process group, and streams stdout and stderr back as they are produced.
  4. The deadline kills that whole group, so what the command forked goes with it.

This turns the sandbox from a resource you can declare into one you can run code in.

What I did

The agent is a small HTTP server that ships inside the image. It runs as root so it can drop to an unprivileged user before every spawn; inside a MicroVM that drop sits behind hardware virtualisation, which is the tenant boundary.

Two decisions are worth pointing at:

  • Paths go through openat2, not a resolver. Resolving a path and then opening it by name leaves every check in a race window, and the code being confined runs in the same guest and can drive both sides of it. RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS makes resolution and open one operation. This removed the hand-written component walk, the canonicalisation, and the containment check rather than adding a fourth guard next to them.
  • spawn and spawn_sandboxed are two functions, not a flag. One inherits the environment for our own helper processes; the other clears it. The agent's environment names its own port and session, so handing it to a caller's code hands over a map to the API running it.

Files touched

  • crates/alien-sandbox-agent/ — the agent: HTTP surface, exec, file transfer, path confinement, privilege drop, PID-namespace support for runtimes that grant it.
  • crates/alien-core/src/sandbox_process.rs — process framing, shared with the GCP launcher path.
  • crates/alien-build/src/sandbox_bundle.rs — the image bundle and its Dockerfile.
  • crates/alien-bindings/src/error.rs — the error variants the agent's callers see.

How I tested

  • 27 unit + 14 protocol tests, run on Linux in a container as well as on macOS. The Linux run is the one that counts: it exercises the real openat2 path, and it caught a traversal on write returning 500 instead of 400 that macOS could not have surfaced.
  • A standalone probe against a real kernel: a legitimate file opens; a symlink to an outside file, a symlinked parent directory, a dangling symlink on write, .. traversal and an absolute path are all refused. The control arm reads the same symlink successfully without confinement, so the refusals are the code working rather than a broken fixture.
  • Each security-relevant test was mutation-tested — the fix reverted, the test observed to fail, the fix restored. That covers the dangling-symlink escape, environment inheritance, the process-group kill, the refusal of the agent's own supervised code, and Dockerfile directive injection.
  • A standalone probe confirming a connecting socket can be attributed to its owning user before that was relied on: a connection from the sandbox user is identified as such, and one from another user is not mistaken for it.
  • protected_hardlinks checked on the actual MicroVM base image, as the sandbox uid, with a control arm.
  • cargo check for alien-build and alien-bindings under their individual feature combinations, not just the workspace build.

Security review of this diff, since it runs untrusted code:

  • A caller planting a symlink — refused by the kernel at open, verified against a real kernel.
  • A caller racing a rename between check and open — no window; resolution and open are one call.
  • A caller reaching the agent's own API from inside the guest — refused. The agent does not serve a request whose connecting socket belongs to the identity it runs commands as, so the command it started cannot ask it for more work. Verified against a real kernel, with a control arm showing a caller on loopback under a different user is unaffected — a proxy terminating inside the guest still works.
  • An image reference carrying a newline — refused before it renders, since a reference reaching a generated Dockerfile would otherwise write its own directives. The check sits inside the renderer, so neither entry point can skip it.
  • A command outliving its deadline — the process group is killed; a setsid child still escapes, which needs a cgroup and is called out in the module docs.
  • A setuid binary in a caller-supplied base image — no_new_privs is set after the drop.
  • Supplementary groups surviving the drop — shed before the uid changes.
  • Hard links — deliberately not addressed here and documented; a link is the inode, so no resolver can tell it apart, and protected_hardlinks bounds it to files the caller could already write.

Nothing turned up.

@greptile-apps

greptile-apps Bot commented Aug 10, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds the in-sandbox command agent, its process and file-transfer protocol, and the image bundle used to deploy it.

  • Adds kernel-mediated path confinement, privilege dropping, process output framing, deadlines, and transport or capability authorization.
  • Adds sandbox bundle generation with centralized base-image validation and agent runtime configuration.
  • Adds structured caller-facing errors and protocol coverage.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains from the previously reported issues.

The Dockerfile validation is now enforced inside the shared renderer, protected transport-mode routes perform fail-closed socket-owner attribution, and the IPv4 socket-table conversion correctly recognizes guest-local peers.

Important Files Changed

Filename Overview
crates/alien-sandbox-agent/src/server.rs Defines the HTTP protocol and consistently applies capability or fail-closed transport authorization to protected operations.
crates/alien-sandbox-agent/src/peer.rs Attributes guest-local sockets to their owning uid and corrects the previously reported IPv4 address conversion and fail-open behavior.
crates/alien-build/src/sandbox_bundle.rs Generates the sandbox image bundle and now centralizes base-image validation inside the Dockerfile renderer.
crates/alien-sandbox-agent/src/confine.rs Uses Linux openat2 resolution flags to perform confined file access without a pathname check/open race.
crates/alien-core/src/sandbox_process.rs Adds shared process spawning, output framing, backpressure, deadline enforcement, and process-group cleanup.
crates/alien-sandbox-agent/src/exec.rs Runs caller commands under the configured unprivileged identity with controlled environment and process framing.

Sequence Diagram

sequenceDiagram
  participant Caller
  participant Agent
  participant Auth as Authorization
  participant Kernel as Kernel confinement
  participant Process as Sandboxed process

  Caller->>Agent: Exec or file request
  Agent->>Auth: Validate capability or transport peer
  Auth-->>Agent: Authorized
  Agent->>Kernel: Open path beneath session root
  Kernel-->>Agent: Confined descriptor
  Agent->>Process: Drop privileges and spawn
  Process-->>Agent: stdout/stderr frames
  Agent-->>Caller: Stream frames and terminal result
Loading

Reviews (33): Last reviewed commit: "feat(sandbox): add the in-sandbox agent ..." | Re-trigger Greptile

Comment thread crates/alien-build/src/sandbox_bundle.rs
Comment thread crates/alien-build/src/sandbox_bundle.rs Outdated
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch from 8f2beff to 7f2f487 Compare August 11, 2026 06:55
Comment thread crates/alien-sandbox-agent/src/server.rs Outdated
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch from 7f2f487 to 6510139 Compare August 11, 2026 07:37
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch from 6510139 to 39a3fd4 Compare August 11, 2026 07:50
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch from 39a3fd4 to 7d0e0a4 Compare August 11, 2026 08:59
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch from 7d0e0a4 to 33e13f4 Compare August 11, 2026 09:10
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch from 33e13f4 to 6c36ed2 Compare August 11, 2026 12:11
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch from 6c36ed2 to f8a4255 Compare August 11, 2026 13:19
Comment thread crates/alien-sandbox-agent/src/peer.rs Fixed
Comment thread crates/alien-sandbox-agent/src/peer.rs Outdated
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch 2 times, most recently from 853d4f1 to 182f60b Compare August 11, 2026 16:53
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch 2 times, most recently from 25bba81 to af8945f Compare August 11, 2026 17:28
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch from af8945f to 9d6a588 Compare August 11, 2026 18:40
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch 2 times, most recently from e89f04e to 76a1e5f Compare August 11, 2026 22:24
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch from 76a1e5f to aa667a7 Compare August 12, 2026 06:32
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch 2 times, most recently from 97e29ab to b9372c2 Compare August 12, 2026 07:05
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch 2 times, most recently from 4ac98fd to 59358f5 Compare August 12, 2026 07:49
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch from 59358f5 to 8842b67 Compare August 12, 2026 08:11
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch 2 times, most recently from 1c76bab to dda69b5 Compare August 12, 2026 09:15
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch from dda69b5 to 5f0b1bf Compare August 12, 2026 11:01
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch 2 times, most recently from ccead48 to b668d95 Compare August 12, 2026 12:14
@ItamarZand88
ItamarZand88 force-pushed the itamar/alien-75-sandbox-2-agent branch from b668d95 to a6bd5e2 Compare August 12, 2026 13:25
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.

2 participants