Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(workspace): bwrap mount ordering + AppArmor fallback - #651

Merged
joshuajbouw merged 4 commits into
mainfrom
fix/bwrap-sandbox-linux
Apr 16, 2026
Merged

joshuajbouw merged 4 commits into
mainfrom
fix/bwrap-sandbox-linux

Conversation

@joshuajbouw

@joshuajbouw joshuajbouw commented Apr 16, 2026

Copy link
Copy Markdown
Member

Linked Issue

Closes #648

Summary

Two compounding bugs prevented OpenClaw Tier 2 (Node.js) capsules from starting on Linux. The macOS Seatbelt path was already fixed in PR #534 — this ports the fix to the Linux bwrap path and adds AppArmor detection.

Changes

  • Mount ordering fix: Reordered build_bwrap_prefix() so hidden --tmpfs overlays come before writable --bind mounts. In bwrap, later mounts override earlier ones — capsule bind-mounts now punch through the ~/.astrid tmpfs overlay instead of being erased by it.
  • AppArmor/bwrap probe: Added bwrap_available() with a cached probe (bwrap --unshare-user --ro-bind / / -- /bin/true). When user namespaces are blocked (Ubuntu 24.04+ default), sandbox_prefix() returns None and the existing unsandboxed fallback in server.rs runs with a clear warning.
  • Distro-aware install hint: When bwrap is missing, bwrap_install_hint() reads /etc/os-release and logs the correct install command for the detected package manager (apt/dnf/pacman/apk/zypper/xbps/nix).
  • Module split: sandbox.rs (1241 lines) split into sandbox/mod.rs (629), sandbox/bwrap.rs (385), sandbox/seatbelt.rs (189) to stay within the CI line limit.
  • Server test resilience: 4 astrid-mcp server tests updated to accept both sandboxed and unsandboxed command shapes, since bwrap may be unavailable in CI containers.
  • CHANGELOG.md updated under [Unreleased] > Fixed.

Test Plan

Automated

  • cargo test --workspace passes (2 pre-existing socket test failures unrelated to this PR)
  • No new clippy warnings (cargo clippy --workspace -- -D warnings)
  • cargo +nightly fmt --all --check passes

New tests added:

Test File Covers
test_bwrap_prefix_writable_inside_hidden_path bwrap.rs Bug 2: writable root inside hidden path → bind after tmpfs
test_seatbelt_prefix_writable_inside_hidden_path seatbelt.rs macOS equivalent: ancestor check skips deny rule
test_bwrap_probe_success bwrap.rs Probe interprets exit 0 as available
test_bwrap_probe_namespace_denied bwrap.rs Probe interprets AppArmor/namespace failure as unavailable
test_bwrap_probe_not_found bwrap.rs Probe interprets missing binary as unavailable
test_bwrap_install_hint_returns_nonempty bwrap.rs Install hint always returns non-empty string mentioning bubblewrap

Manual

  • Built and ran cargo test -p astrid-workspace -- sandbox in Docker (ubuntu:24.04) — all 28 Linux tests pass.
  • Verified bwrap probe returns false in unprivileged Docker container (simulates AppArmor restriction).
  • Verified bwrap probe returns true in --privileged Docker container.

Checklist

  • Linked to an issue
  • CHANGELOG.md updated under [Unreleased]

Two compounding bugs prevented OpenClaw Tier 2 capsules from starting
on Linux:

1. Mount ordering: --tmpfs for hidden paths (e.g. ~/.astrid) was applied
   BEFORE writable --bind mounts, causing the tmpfs to erase the capsule
   directory. In bwrap, later mounts override earlier ones — reordered so
   hidden tmpfs comes first and writable bind-mounts punch through after.
   This mirrors the ancestor check already present in the macOS Seatbelt
   path (PR #534).

2. AppArmor: Ubuntu 24.04+ blocks unprivileged user namespaces by default
   (kernel.apparmor_restrict_unprivileged_userns=1), silently killing bwrap.
   Added a cached probe (bwrap --unshare-user -- /bin/true) that detects
   this at startup and falls back to unsandboxed execution with a clear
   warning, rather than producing a misleading 'connection closed' error.

Closes #648
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses critical issues preventing OpenClaw Tier 2 capsules from starting on Linux systems, specifically concerning bwrap sandboxing. By refining the mount ordering and introducing a robust detection mechanism for bwrap availability, the changes ensure better compatibility with modern Linux security configurations like AppArmor. Additionally, the PR improves the user experience by providing actionable installation instructions when dependencies are missing.

Highlights

  • Mount Ordering Fix: Reordered bwrap configuration to ensure writable bind-mounts occur after hidden tmpfs overlays, allowing capsule directories to correctly punch through hidden paths.
  • AppArmor/bwrap Probe: Implemented a cached startup probe to detect if bwrap is functional; if blocked by AppArmor or missing, the system now gracefully falls back to unsandboxed execution with a warning.
  • Distro-aware Install Hints: Added logic to detect the host Linux distribution via /etc/os-release and provide specific installation commands for bubblewrap when it is missing.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.


The sandbox was tight and quite deep, Where secrets and files were to keep. But bwrap would fail, On a Linux-based trail, Now logic has woken from sleep.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request fixes a Linux sandboxing issue where bubblewrap mount ordering could hide capsule directories and adds detection for AppArmor restrictions on unprivileged user namespaces. It introduces a cached probe to verify sandbox availability and provides distro-specific installation hints. Review feedback suggests increasing the robustness of /etc/os-release parsing by trimming whitespace and improving error diagnostics in the bwrap probe by checking specific io::ErrorKind variants.

Comment thread crates/astrid-workspace/src/sandbox.rs Outdated
Comment thread crates/astrid-workspace/src/sandbox.rs Outdated
sandbox.rs exceeded the CI line limit (1241 lines). Split into:
- sandbox/mod.rs (629) — shared types, validation, dispatch, cross-platform tests
- sandbox/bwrap.rs (385) — Linux bubblewrap: probe, install hint, prefix builder, tests
- sandbox/seatbelt.rs (189) — macOS Seatbelt: profile builder, tests

No functional changes — pure file reorganization.
When bwrap is unavailable at runtime (e.g. AppArmor blocks user
namespaces in CI), sandbox_prefix() returns None and the command
runs unsandboxed. Updated 4 server tests to accept both sandboxed
and unsandboxed command shapes.
- Trim whitespace in /etc/os-release parsing for robustness
- Differentiate NotFound vs other errors in bwrap probe diagnostic
@joshuajbouw
joshuajbouw merged commit 919e9ce into main Apr 16, 2026
13 checks passed
@joshuajbouw
joshuajbouw deleted the fix/bwrap-sandbox-linux branch April 16, 2026 12:51
joshuajbouw added a commit that referenced this pull request May 18, 2026
#655)

#651 introduced a silent fallback when bwrap can't create user namespaces
— which is the default state on Ubuntu 24.04+ because
`kernel.apparmor_restrict_unprivileged_userns=1` ships enabled. The
fallback returned `Ok(None)` from `sandbox_prefix()` and the MCP caller
launched the subprocess unsandboxed with a single `tracing::warn!` line
as the only signal. This contradicted the README's "subprocess capsules
are sandboxed" promise: a Node.js MCP server or OpenClaw Tier 2 plugin
could read `~/.ssh/id_rsa` or write to `~/.bashrc` and the 5-layer
security flow would never fire because none of those calls touched the
Astrid host ABI.

New `SandboxPolicy` enum with three values:

- `Required` (new default) — `sandbox_prefix()` errors if the OS sandbox
  is unavailable. The error message names the most likely cause
  (apparmor sysctl on Ubuntu 24.04+) and the remediation. MCP server
  startup propagates the error and refuses to launch the subprocess.

- `Preferred` — old behaviour: try the sandbox, fall back to unsandboxed
  with a `warn`. Retained for environments that explicitly accept the
  reduced isolation.

- `Off` — always launch without a sandbox, no warn. Trusted dev only.

Operators can override via `ASTRID_SANDBOX_POLICY=required|preferred|off`
or per-call via `ProcessSandboxConfig::with_policy(...)`. Malformed env
values warn loudly and fall back to `Required`.

This is a breaking change for Ubuntu 24.04+ deployments that were
tacitly relying on the silent fallback. Migration: flip the sysctl
(`sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0`) or
set `ASTRID_SANDBOX_POLICY=preferred` if the unsandboxed launch was
intentional.

Six new unit tests cover the policy enum, env-var parsing, the default
non-`Off` invariant, and the `Off` short-circuit return.
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.

OpenClaw Tier 2 capsules fail on Linux: bwrap mount ordering hides capsule dir + AppArmor silent failure

1 participant