Don't request --unshare-user for bwrap when running as root
Root already has full privilege without a new user namespace. Creating one anyway (bwrap's default single-mapping uid/gid setup, no --uid/--gid/subuid ranges) forces the kernel's unprivileged-userns setgroups() restriction: every supplementary group outside that one mapping collapses to the overflow gid (65534/"nobody"), and setgroups() calls inside the sandbox then fail. Reported by the user running -r images/gitea.tar as root: `id` showed groups=0(root),65534(nobody) repeated once per real supplementary group, and `su git` failed with "can't set groups: Operation not permitted". detect_bwrap_unshare_args() now skips --unshare-user (and stops combining the other probes with CLONE_NEWUSER) whenever geteuid() == 0 -- confirmed fixed by the user on a root-capable machine. The non-root path is unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,12 @@ Source layout (all under `src/`):
|
|||||||
- `bwrap.{h,cpp}` — `detect_bwrap_unshare_args()` probes the kernel (via a forked
|
- `bwrap.{h,cpp}` — `detect_bwrap_unshare_args()` probes the kernel (via a forked
|
||||||
`unshare(2)` per namespace type) for which `--unshare-xxx` flags `bwrap` can actually
|
`unshare(2)` per namespace type) for which `--unshare-xxx` flags `bwrap` can actually
|
||||||
use; `build_bwrap_args()`/`run_bwrap()` assemble and run the sandboxed command.
|
use; `build_bwrap_args()`/`run_bwrap()` assemble and run the sandboxed command.
|
||||||
|
Never requests `--unshare-user` when running as root: root doesn't need a fresh
|
||||||
|
user namespace for privilege, and bwrap's own single-mapping uid/gid setup for one
|
||||||
|
triggers the kernel's unprivileged-userns setgroups() restriction, which showed up
|
||||||
|
as every other supplementary group collapsing to the overflow gid ("nobody") in
|
||||||
|
`id`, and `su` inside the sandbox failing with "can't set groups: Operation not
|
||||||
|
permitted".
|
||||||
- `process.{h,cpp}` — argv-based subprocess helpers (fork/execvp, no shell):
|
- `process.{h,cpp}` — argv-based subprocess helpers (fork/execvp, no shell):
|
||||||
`run_process()` captures stdout (used for `containers-storage` calls),
|
`run_process()` captures stdout (used for `containers-storage` calls),
|
||||||
`run_process_foreground()` inherits all of stdio (used for the interactive `bwrap`
|
`run_process_foreground()` inherits all of stdio (used for the interactive `bwrap`
|
||||||
|
|||||||
+14
-4
@@ -116,7 +116,18 @@ std::vector<std::string> detect_bwrap_unshare_args() {
|
|||||||
|
|
||||||
bool user_ns_supported = kernel_supports_namespace(CLONE_NEWUSER);
|
bool user_ns_supported = kernel_supports_namespace(CLONE_NEWUSER);
|
||||||
spdlog::debug("namespace user: {}", user_ns_supported ? "supported" : "not supported");
|
spdlog::debug("namespace user: {}", user_ns_supported ? "supported" : "not supported");
|
||||||
if (user_ns_supported) {
|
|
||||||
|
// Root already has full privilege without a new user namespace, and bwrap's own
|
||||||
|
// uid/gid mapping into one (a single trivial mapping, since we don't pass
|
||||||
|
// --uid/--gid/subuid ranges) forces the kernel's unprivileged-userns
|
||||||
|
// setgroups() restriction: every other supplementary group collapses to the
|
||||||
|
// overflow gid (65534/"nobody"), and setgroups() calls inside the sandbox (e.g.
|
||||||
|
// `su`) fail with "Operation not permitted". Observed directly: `id` inside a
|
||||||
|
// root-launched sandbox showed "nobody" repeated once per real supplementary
|
||||||
|
// group, and `su git` failed exactly this way. So only request --unshare-user
|
||||||
|
// (and only combine the other probes with it) when not root.
|
||||||
|
bool combine_with_user_ns = user_ns_supported && geteuid() != 0;
|
||||||
|
if (combine_with_user_ns) {
|
||||||
args.push_back("--unshare-user");
|
args.push_back("--unshare-user");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,9 +137,8 @@ std::vector<std::string> detect_bwrap_unshare_args() {
|
|||||||
}
|
}
|
||||||
// As a regular (non-root) user, most namespace types can only be unshared
|
// As a regular (non-root) user, most namespace types can only be unshared
|
||||||
// together with a fresh user namespace (which supplies the capabilities
|
// together with a fresh user namespace (which supplies the capabilities
|
||||||
// needed), not in isolation -- so combine them here whenever the user
|
// needed), not in isolation -- so combine them here whenever it applies.
|
||||||
// namespace probe above succeeded.
|
int flags = probe.clone_flag | (combine_with_user_ns ? CLONE_NEWUSER : 0);
|
||||||
int flags = probe.clone_flag | (user_ns_supported ? CLONE_NEWUSER : 0);
|
|
||||||
bool supported = kernel_supports_namespace(flags);
|
bool supported = kernel_supports_namespace(flags);
|
||||||
spdlog::debug("namespace {}: {}", probe.name, supported ? "supported" : "not supported");
|
spdlog::debug("namespace {}: {}", probe.name, supported ? "supported" : "not supported");
|
||||||
if (supported) {
|
if (supported) {
|
||||||
|
|||||||
Reference in New Issue
Block a user