Skip to content

Container runs as root with silent sandbox passthrough on backend failure #405

@devrandom

Description

@devrandom

The Dockerfile has no USER instruction, so the daemon runs as root inside the container. Combined with a silent degradation path in the sandbox backend detection, this creates a privilege escalation risk under realistic deployment conditions.

The sandbox degradation path:

SandboxMode (the config value) and SandboxBackend (what's actually available at runtime) are independent. When mode is Enabled but no working backend is detected, wrap() falls through to wrap_passthrough() at sandbox.rs:466 — which runs the subprocess with no filesystem isolation. The only signal is a tracing::warn at sandbox.rs:219. Nothing in the health check, API status, or startup output surfaces this to an operator as a hard failure.

When this happens in Docker:

The Dockerfile installs bubblewrap, so under normal conditions the sandbox works. The risk has two distinct failure modes:

  1. bwrap absent from PATH (removed, different base image, stripped by policy): detect_bubblewrap() at sandbox.rs:930 returns SandboxBackend::None, wrap() silently falls through to wrap_passthrough(), and shell commands run as root with no OS-level containment. This is the silent-degradation path.
  2. bwrap present but non-functional (CAP_SYS_ADMIN dropped, user namespaces disabled by container runtime): detect_bubblewrap() runs a real probe (bwrap --ro-bind / / --proc /proc -- /usr/bin/true) at sandbox.rs:939-953, but on failure it returns SandboxBackend::Bubblewrap { proc_supported: false } rather than None. Subsequent wrap_bubblewrap() invocations fail at spawn time (shell.rs:251), surfacing as tool errors to the LLM. This fails closed — no silent passthrough — but every shell command is broken with no clear startup diagnostic.

In both cases the container is running as root, so any containment gap (mode 1) or future bwrap CVE gives full root access.

Potential Fixes:

  1. Add RUN useradd -ms /bin/bash spacebot and USER spacebot before ENTRYPOINT in the Dockerfile. This is the most impactful single change — it limits blast radius regardless of sandbox state.
  2. In sandbox.rs, treat SandboxBackend::None with SandboxMode::Enabled as a fatal startup error (or a non-OK health check response), not a warn-and-continue. Operators deploying in Docker expect containment to be active.
  3. detect_bubblewrap() already runs a real probe (bwrap --ro-bind / / --proc /proc -- /usr/bin/true), but on failure it still returns Bubblewrap { proc_supported: false }. It should return SandboxBackend::None when the probe fails entirely, not just demote the proc_supported flag. A backend that can't create namespaces should not be reported as available.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions