diff --git a/README.md b/README.md index ff03851..25e32b6 100644 --- a/README.md +++ b/README.md @@ -359,6 +359,11 @@ installed. resolver. This keeps DNS working on cloud images such as Kali that accept a DHCP route but omit its DNS option, and it preserves host VPN/split-DNS resolution rather than substituting public resolvers. +- New goldens include Stado's Linux sandbox helpers: `bwrap` for process and + filesystem isolation, plus `pasta` for proxy-only host-allowlist networking. + On Ubuntu 24.04, Devbox enables AppArmor's dedicated, restricted bwrap + profile; it does not disable Ubuntu's global user-namespace restriction, so + standalone `unshare` remains intentionally unavailable. - A box created before a `devbox build --force` keeps the *old* toolchain until you `destroy` and recreate it. - `--ssh-agent` enables Lima's agent socket for a new or existing box. An diff --git a/bin/devbox b/bin/devbox index f71d737..4999fa3 100755 --- a/bin/devbox +++ b/bin/devbox @@ -433,13 +433,27 @@ provision: RESOLVER systemctl restart systemd-resolved fi - apt="build-essential git curl wget jq ripgrep unzip ca-certificates procps file locales sudo python3 python3-venv python3-pip waypipe nftables" - dnf="@development-tools git curl wget jq ripgrep unzip ca-certificates procps-ng file glibc-langpack-en sudo python3 python3-pip waypipe nftables" - pac="base-devel git curl wget jq ripgrep unzip ca-certificates procps-ng file sudo python python-pip waypipe nftables" + apt="build-essential git curl wget jq ripgrep unzip ca-certificates procps file locales sudo python3 python3-venv python3-pip waypipe nftables bubblewrap passt apparmor apparmor-profiles" + dnf="@development-tools git curl wget jq ripgrep unzip ca-certificates procps-ng file glibc-langpack-en sudo python3 python3-pip waypipe nftables bubblewrap passt" + pac="base-devel git curl wget jq ripgrep unzip ca-certificates procps-ng file sudo python python-pip waypipe nftables bubblewrap passt" if command -v apt-get >/dev/null 2>&1; then apt-get update && apt-get install -y --no-install-recommends $apt elif command -v dnf >/dev/null 2>&1; then dnf install -y $dnf elif command -v pacman >/dev/null 2>&1; then pacman -Sy --noconfirm $pac fi + # Ubuntu 24.04 restricts unprivileged user namespaces through a generic + # AppArmor policy. Stado's Linux sandbox needs bwrap to construct private + # mount/pid/network namespaces. Enable AppArmor's dedicated bwrap policy: + # it permits bwrap's short-lived setup capabilities, then stacks a more + # restrictive profile on the sandboxed child. Do not weaken the global + # restriction: standalone unshare remains unavailable. + if [[ -r /proc/sys/kernel/apparmor_restrict_unprivileged_userns ]] \ + && [[ "$(/dev/null 2>&1; then printf " ok %-9s %s\n" "$t" "$(command -v "$t")" else printf " MISS %-9s (not on PATH)\n" "$t"; fi done' /dev/null || true)" @@ -547,6 +561,17 @@ verify_golden() { if printf '%s' "$out" | grep -q MISS; then warn "some tools missing in golden — inspect with: limactl shell $name" fi + # Stado's Linux sandbox uses bwrap for process/filesystem isolation and + # `pasta --splice-only` for proxy-only host allowlist networking. Exercise + # both in the golden: Ubuntu's AppArmor exception is deliberately scoped to + # bwrap, so a standalone `unshare` probe would correctly remain blocked. + if ! limactl shell "$name" -- bash -lc ' + bwrap --unshare-user --unshare-net --uid 0 --gid 0 --ro-bind / / true + pasta --help 2>&1 | grep -q -- "--splice-only" + ' /dev/null; then + warn "golden sandbox prerequisites are unusable (bwrap/pasta)" + return 1 + fi # shellcheck disable=SC2016 # $HOME expands in the guest shell. host_keys="$(limactl shell "$name" -- bash -lc ' test -s "$HOME/.ssh/known_hosts" diff --git a/test/devbox.bats b/test/devbox.bats index e019426..ad94554 100644 --- a/test/devbox.bats +++ b/test/devbox.bats @@ -156,6 +156,17 @@ setup() { grep -q 'brew install herdr' "$tmp" } +@test "golden yaml includes the Stado Linux sandbox helpers and scoped Ubuntu bwrap policy" { + tmp="$BATS_TEST_TMPDIR/g.yaml" + emit_golden_yaml ubuntu-24.04 "$tmp" + grep -q 'bubblewrap passt apparmor apparmor-profiles' "$tmp" + grep -q 'apparmor_restrict_unprivileged_userns' "$tmp" + grep -q 'bwrap-userns-restrict' "$tmp" + grep -q '/usr/share/apparmor/extra-profiles/bwrap-userns-restrict' "$tmp" + grep -q '/usr/sbin/apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict' "$tmp" + ! grep -q 'apparmor_restrict_unprivileged_userns=0' "$tmp" +} + @test "golden yaml installs Waypipe through every supported guest package manager" { tmp="$BATS_TEST_TMPDIR/g.yaml" emit_golden_yaml ubuntu-24.04 "$tmp" @@ -185,6 +196,13 @@ setup() { [[ "$source_text" == *'Golden verification failed; removing unusable'* ]] } +@test "golden verification exercises Stado's Linux sandbox helpers" { + source_text="$(<"$DEVBOX")" + [[ "$source_text" == *'for t in brew gh claude codex opencode pi herdr stado bwrap pasta'* ]] + [[ "$source_text" == *'bwrap --unshare-user --unshare-net --uid 0 --gid 0'* ]] + [[ "$source_text" == *'pasta --help 2>&1 | grep -q -- "--splice-only"'* ]] +} + @test "generated golden yaml validates with limactl" { command -v limactl >/dev/null || skip "limactl not installed" tmp="$BATS_TEST_TMPDIR/g.yaml"