From 0546bf6ea5e70769672d54416ea5fc641b0b5cbe Mon Sep 17 00:00:00 2001 From: Colin Date: Fri, 20 Sep 2024 12:41:39 +0000 Subject: [PATCH] bunpen: barebones pasta integration totally untested, except that it builds --- .../bunpen/restrict/ns/namespace.ha | 23 ++++++---- pkgs/additional/bunpen/restrict/ns/pasta.ha | 46 +++++++++++++++++++ 2 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 pkgs/additional/bunpen/restrict/ns/pasta.ha diff --git a/pkgs/additional/bunpen/restrict/ns/namespace.ha b/pkgs/additional/bunpen/restrict/ns/namespace.ha index 7a0ffef41..c4d2a4690 100644 --- a/pkgs/additional/bunpen/restrict/ns/namespace.ha +++ b/pkgs/additional/bunpen/restrict/ns/namespace.ha @@ -107,7 +107,7 @@ export fn namespace_restrict(what: *restrict::resources) void = { case restrict::net_none => errors::ext::check("namespace: unshare net", rt::ext::unshare(rt::ext::clone_flag::NEWNET)); case let subset: restrict::net_subset => - errors::ext::check("namespace: unshare net", rt::ext::unshare(rt::ext::clone_flag::NEWNET)); + setup_pasta(subset); case restrict::net_all => log::println("[namespace] keeping net namespace"); }; @@ -164,19 +164,22 @@ type ns_ctx = struct { // - in the parent: wait for the child, then propagate its exit status fn fork_and_propagate() (void | os::exec::error) = { match (os::exec::fork()?) { - case let child_pid: os::exec::process => - forward_signals(child_pid); - log::println("[namespace/fork] signals configured"); - let status = wait_child(child_pid)?; - let rc = rt::wexitstatus(status.status); - // TODO: if the child exited due to a signal (e.g. SIGTERM), we (confusingly?) exit 0. - // seems correct behavior may be to send the same termination signal to ourselves. - log::printfln("[namespace/fork] child exited with {}; forwarding as {}", status.status, rc); - os::exit(rc); // propagate exit code + case let child_pid: os::exec::process => return wait_and_propagate(child_pid); case => log::println("[namespace/fork] continuing as child"); }; }; +fn wait_and_propagate(child_pid: os::exec::process) (void | os::exec::error) = { + forward_signals(child_pid); + log::println("[namespace/fork] signals configured"); + let status = wait_child(child_pid)?; + let rc = rt::wexitstatus(status.status); + // TODO: if the child exited due to a signal (e.g. SIGTERM), we (confusingly?) exit 0. + // seems correct behavior may be to send the same termination signal to ourselves. + log::printfln("[namespace/fork] child exited with {}; forwarding as {}", status.status, rc); + os::exit(rc); // propagate exit code +}; + fn wait_child(child_pid: os::exec::process) (os::exec::status | os::exec::error) = { for (true) { match (os::exec::wait(&child_pid)) { diff --git a/pkgs/additional/bunpen/restrict/ns/pasta.ha b/pkgs/additional/bunpen/restrict/ns/pasta.ha new file mode 100644 index 000000000..ce9c14745 --- /dev/null +++ b/pkgs/additional/bunpen/restrict/ns/pasta.ha @@ -0,0 +1,46 @@ +// vim: set shiftwidth=2 : +use errors; +use errors::ext; +use fmt; +use os; +use os::exec; +use restrict; +use rt; +use rt::ext; + +fn setup_pasta(net: restrict::net_subset) void = { + // `pasta PID [options]`: creates a device in the netns of PID. + // ordering: + // 1. fork + // 2. child: enter netns, signal parent. + // 3. parent: fork and spawn `pasta --pid /proc/fd/$N` + // then dumbly wait on child until completion + // 4. child: wait for `pasta` to signal readiness on fd `$N` + // + // after that, we can continue on & exec into the user code + // + // TODO: this currently lacks the synchronization described above + + match (os::exec::fork()) { + case let child_pid: os::exec::process => + errors::ext::check("setup_pasta: attach", attach_pasta(child_pid)); + errors::ext::check("setup_pasta: wait", wait_and_propagate(child_pid)); + case void => + errors::ext::check("namespace: unshare net", rt::ext::unshare(rt::ext::clone_flag::NEWNET)); + case let e: os::exec::error => + errors::ext::check("setup_pasta: fork", e); + }; +}; + +// spawn pasta as a separate process, and have it attach to the netns of the given pid. +fn attach_pasta(child: os::exec::process) (void | os::exec::error | rt::errno) = { + return match (os::exec::fork()?) { + case let pasta_pid: os::exec::process => yield void; + case void => + let pidstr = fmt::asprint(child: int); + defer free(pidstr); + let pasta_args = [ "pasta", pidstr ]; + // TODO: append dns, gateway arguments to `pasta`. + yield rt::ext::execvpe("pasta", pasta_args, os::getenvs()); + }; +};