Skip to content

fix(sandbox): fall back to --map-auto when root-user mapping is restricted - #3280

Open
Einspanner123 wants to merge 3 commits into
ultraworkers:mainfrom
Einspanner123:fix/sandbox-map-auto-fallback
Open

fix(sandbox): fall back to --map-auto when root-user mapping is restricted#3280
Einspanner123 wants to merge 3 commits into
ultraworkers:mainfrom
Einspanner123:fix/sandbox-map-auto-fallback

Conversation

@Einspanner123

@Einspanner123 Einspanner123 commented Jul 31, 2026

Copy link
Copy Markdown

Replaces the closed #3013 (stale, conflicts with main) with a fresh rebase onto ultraworkers:main.

Problem

unshare --user --map-root-user fails on kernels and containers that block unprivileged writes to /proc/self/uid_map (e.g. GitHub Actions, restricted AppArmor profiles, some container runtimes) with EPERM. As a result the sandbox silently disables itself even though a working mapping exists.

Fix

util-linux delegates to the setuid newuidmap/newgidmap helpers when --map-auto is also present. Probe both candidate mappings at startup and prefer the plain form:

  1. --user --map-root-user (works on most systems, no extra deps)
  2. --user --map-root-user --map-auto (fallback for restricted kernels/containers)

The chosen mapping is cached and reused by both the capability probe and the launcher, so the sandbox now enables on systems where only the fallback works. The plain form stays first, so systems without newuidmap/newgidmap or a /etc/subuid range are unaffected.

Note: the --map-auto fallback depends on the setuid newuidmap/newgidmap helpers (the uidmap package on Debian/Ubuntu) and on the current user having a subuid/subgid range. The startup probe covers this dependency: when the helpers or range are missing, unshare --map-auto fails and the plain form is used (verified: unshare --user --map-root-user --map-auto true exits 127 with failed to execute newuidmap without the helper).

Verification

  • On a restricted host: unshare --user --map-root-user true fails, combined form succeeds
  • cargo test -p runtime sandbox passes (incl. new test guarding candidate order)
  • cargo clippy clean for the changed file

No behavior change. Move the inline probe out of
unshare_user_namespace_works into a reusable unshare_probe helper and a
cached working_unshare_mapping() that picks the first working candidate
from UNSHARE_MAPPING_CANDIDATES, so the launcher and the capability probe
share one code path.
…icted

Plain `unshare --user --map-root-user` fails on kernels and containers
that block unprivileged writes to /proc/self/uid_map (e.g. GitHub Actions,
restricted AppArmor profiles). On those systems util-linux delegates to the
setuid newuidmap/newgidmap helpers when --map-auto is also present.

Add the combined form as a fallback candidate and build the launcher args
from the probed mapping, so systems without newuidmap/newgidmap or a
/etc/subuid range keep using the plain form.
@Einspanner123
Einspanner123 force-pushed the fix/sandbox-map-auto-fallback branch from d17590b to 277fdda Compare July 31, 2026 09:11
@1716775457damn

Copy link
Copy Markdown

Solid fix. The fallback from --map-root-user to --map-auto is the correct approach for environments where /proc/self/uid_map is restricted (GitHub Actions, AppArmor, some Docker setups). Probing both at startup and caching the result avoids runtime surprises. One thing to verify: does the --map-auto path require
ewuidmap/
ewgidmap setuid binaries to be present? If so, worth documenting as a dependency for that fallback path.

… fallback

The fallback candidate relies on the setuid newuidmap/newgidmap helpers
(uidmap package) plus a subuid/subgid range for the current user. Note in
the candidate docs that the startup probe rejects the candidate when those
are missing, so the plain --map-root-user form is used instead.
@Einspanner123

Copy link
Copy Markdown
Author

Good question — yes, the --map-auto path does require the setuid helpers, and that's exactly why it's only a probed fallback.

Dependency: when --map-auto is passed, util-linux stops writing /proc/self/uid_map directly and instead delegates to newuidmap/newgidmap (the uidmap package on Debian/Ubuntu, normally setuid-root), and it also requires the current user to have a range in /etc/subuid//etc/subgidunshare exits with an error if no matching line exists.

Why this is safe: the startup probe ("Probing both at startup") runs the exact candidate command, so it covers this automatically. Verified empirically:

# PATH without newuidmap
$ unshare --user --map-root-user --map-auto true
unshare: failed to execute newuidmap: No such file or directory
# rc=127 → probe rejects the candidate

# normal PATH (newuidmap present)
$ unshare --user --map-root-user --map-auto true
# rc=0 → fallback selected on restricted kernels

So on systems without uidmap or without a subuid range, the probe simply falls through to the plain --map-root-user form (current behavior preserved).

Documented this dependency in the candidate docs — see the new commit (9cbe6d9) in this PR.

@1716775457damn

Copy link
Copy Markdown

Thanks for the detailed clarification. The startup probe covering the dependency automatically is exactly what I was hoping for — no need for extra config or docs then. LGTM on the approach.

@Einspanner123

Copy link
Copy Markdown
Author

Hi @code-yeongyu @Yeachan-Heo — quick heads-up on this PR (replacement for the closed #3013).

Following @code-yeongyu's earlier request, the branch has been rebased onto the latest main and the merge conflicts are resolved (currently mergeable: MERGEABLE). Reviewer @1716775457damn has already given LGTM on the approach.

One thing needs a maintainer's help: since this is a fork PR, the Rust and Rust CI workflows on the latest commit (9cbe6d9) are stuck at action_required awaiting approval to run. Could you approve them when you get a chance? Thanks in advance!

@1716775457damn

Copy link
Copy Markdown

The rebase looks clean and the --map-auto fallback approach is the right direction. Since this supersedes #3013, the commit history is well-structured. I think this is good to go once CI passes.

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.

3 participants