bunpen: implement --bunpen-try-keep-users to try to keep the user namespace, but create a new one if keeping the existing one would require less sandboxing elsewhere

This commit is contained in:
2024-09-06 06:25:27 +00:00
parent baf5aab4b9
commit 9bd5a7e4e4
4 changed files with 41 additions and 18 deletions

View File

@@ -21,6 +21,7 @@ export type cli_opts = struct {
keep_pid: bool,
paths: []str,
run_paths: []str,
try_keep_users: bool,
};
export fn usage() void = {
@@ -46,6 +47,8 @@ export fn usage() void = {
fmt::println(" allow unrestricted access to the network")!;
fmt::println(" --bunpen-keep-pid")!;
fmt::println(" allow this process to see other processes running on the machine")!;
fmt::println(" --bunpen-try-keep-users")!;
fmt::println(" if we have permissions to perform all other operations without creating a new user namespace, then don't create a user namespace")!;
fmt::println(" --bunpen-path <path>")!;
fmt::println(" allow access to the host <path> within the sandbox")!;
fmt::println(" path is interpreted relative to the working directory if not absolute")!;
@@ -99,22 +102,23 @@ export fn parse_args(args: []str) (cli_opts | errors::invalid) = {
next = &args[idx+1];
};
switch (arg) {
case "--bunpen-autodetect" => idx += 1; parsed.autodetect = autodetect_fromstr(expect_arg("--bunpen-autodetect", next)?)?;
case "--bunpen-cap" => idx += 1; parse_caparg(&parsed, expect_arg("--bunpen-cap", next)?)?;
case "--bunpen-debug" => parsed.debug = 2;
case "--bunpen-debug=0" => parsed.debug = 0;
case "--bunpen-debug=1" => parsed.debug = 1;
case "--bunpen-debug=2" => parsed.debug = 2;
case "--bunpen-debug=3" => parsed.debug = 3;
case "--bunpen-debug=4" => parsed.debug = 4;
case "--bunpen-drop-shell" => parsed.drop_shell = true;
case "--bunpen-help" => parsed.help = true;
case "--bunpen-home-path" => idx += 1; append(parsed.home_paths, expect_arg("--bunpen-home-path", next)?);
case "--bunpen-keep-net" => parsed.keep_net = true;
case "--bunpen-keep-pid" => parsed.keep_pid = true;
case "--bunpen-path" => idx += 1; append(parsed.paths, expect_arg("--bunpen-path", next)?);
case "--bunpen-run-path" => idx += 1; append(parsed.run_paths, expect_arg("--bunpen-run-path", next)?);
case => append(parsed.cmd, arg);
case "--bunpen-autodetect" => idx += 1; parsed.autodetect = autodetect_fromstr(expect_arg("--bunpen-autodetect", next)?)?;
case "--bunpen-cap" => idx += 1; parse_caparg(&parsed, expect_arg("--bunpen-cap", next)?)?;
case "--bunpen-debug" => parsed.debug = 2;
case "--bunpen-debug=0" => parsed.debug = 0;
case "--bunpen-debug=1" => parsed.debug = 1;
case "--bunpen-debug=2" => parsed.debug = 2;
case "--bunpen-debug=3" => parsed.debug = 3;
case "--bunpen-debug=4" => parsed.debug = 4;
case "--bunpen-drop-shell" => parsed.drop_shell = true;
case "--bunpen-help" => parsed.help = true;
case "--bunpen-home-path" => idx += 1; append(parsed.home_paths, expect_arg("--bunpen-home-path", next)?);
case "--bunpen-keep-net" => parsed.keep_net = true;
case "--bunpen-keep-pid" => parsed.keep_pid = true;
case "--bunpen-path" => idx += 1; append(parsed.paths, expect_arg("--bunpen-path", next)?);
case "--bunpen-run-path" => idx += 1; append(parsed.run_paths, expect_arg("--bunpen-run-path", next)?);
case "--bunpen-try-keep-users" => parsed.try_keep_users = true;
case => append(parsed.cmd, arg);
};
};

View File

@@ -109,6 +109,9 @@ export fn ingest_cli_opts(opts: cli_opts) (cli_request | exec_params | help) = {
//---- ingest `run_paths` ----//
ingest_paths(&req.resources.paths, opts.run_paths, os::getenv("XDG_RUNTIME_DIR"));
//---- ingest `try_keep_users` ----//
req.resources.try_users = opts.try_keep_users;
//---- ingest `autodetect` (must be done after exec_params) ----//
match (opts.autodetect) {
case let method: autodetect =>

View File

@@ -36,9 +36,22 @@ export fn namespace_restrict(what: *resources) void = {
log::println("[namespace] keeping pid namespace");
what_to_unshare &= ~rt::ext::clone_flag::NEWPID;
};
if (what.try_users) {
log::println("[namespace] keeping user namespace *if possible*");
let unshare_keep_users = what_to_unshare & ~rt::ext::clone_flag::NEWUSER;
match (rt::ext::unshare(unshare_keep_users)) {
case void =>
log::println("[namespace] unshared user namespace successfully");
what_to_unshare = 0;
case let e: rt::errno =>
errors::ext::swallow("[namespace] unshare user namespace", e);
};
};
log::printfln("[namespace] unshare {}", what_to_unshare: u64);
errors::ext::check("namespace: unshare", rt::ext::unshare(what_to_unshare));
if (what_to_unshare != 0) {
log::printfln("[namespace] unshare {}", what_to_unshare: u64);
errors::ext::check("namespace: unshare", rt::ext::unshare(what_to_unshare));
};
// before mounting anything, set up the uids and gids in this namespace.
// without this, everything shows up as 65534 a.k.a. 'nobody' a.k.a. 'overflow',

View File

@@ -13,4 +13,7 @@ export type resources = struct {
// true to allow operations on other processes (e.g. viewing their cmdline,
// killing them, etc).
pid: bool,
// try to keep access to the current user namespace, but let it go if holding
// onto it would prevent us from sandboxing further
try_users: bool,
};