bunpen: fix --bunpen-net pasta integration to work even when pasta isnt on PATH
This commit is contained in:
@@ -1,6 +1,21 @@
|
||||
all: bunpen
|
||||
|
||||
bunpen:
|
||||
# these are *runtime* dependencies
|
||||
IP ?= $(shell which ip)
|
||||
IPTABLES ?= $(shell which iptables)
|
||||
PASTA ?= $(shell which pasta)
|
||||
|
||||
config/runtime_deps.ha: config/runtime_deps.ha.in
|
||||
test -x "$(IP)"
|
||||
test -x "$(IPTABLES)"
|
||||
test -x "$(PASTA)"
|
||||
sed -e "s:@IP@:$(IP):" \
|
||||
-e "s:@IPTABLES@:$(IPTABLES):" \
|
||||
-e "s:@PASTA@:$(PASTA):" \
|
||||
$^ > $@
|
||||
cat $@
|
||||
|
||||
bunpen: config/runtime_deps.ha
|
||||
hare build -o $@
|
||||
|
||||
install:
|
||||
|
6
pkgs/by-name/bunpen/config/runtime_deps.ha.in
Normal file
6
pkgs/by-name/bunpen/config/runtime_deps.ha.in
Normal file
@@ -0,0 +1,6 @@
|
||||
// the sandboxer sometimes shells out to these binary helpers;
|
||||
// build script is expected to patch in the right paths at build time.
|
||||
|
||||
export const IP: str = "@IP@";
|
||||
export const IPTABLES: str = "@IPTABLES@";
|
||||
export const PASTA: str = "@PASTA@";
|
@@ -1,6 +1,9 @@
|
||||
{
|
||||
hareHook,
|
||||
iproute2,
|
||||
iptables,
|
||||
lib,
|
||||
passt,
|
||||
procps,
|
||||
stdenv,
|
||||
util-linux,
|
||||
@@ -10,11 +13,20 @@
|
||||
version = "0.1.0";
|
||||
src = ./.;
|
||||
|
||||
nativeBuildInputs = [ hareHook ];
|
||||
makeFlags = [ "PREFIX=${builtins.placeholder "out"}" ];
|
||||
nativeBuildInputs = [
|
||||
hareHook
|
||||
which
|
||||
];
|
||||
|
||||
makeFlags = [
|
||||
"PREFIX=${builtins.placeholder "out"}"
|
||||
"IP=${lib.getExe' iproute2 "ip"}"
|
||||
"IPTABLES=${lib.getExe' iptables "iptables"}"
|
||||
"PASTA=${lib.getExe' passt "pasta"}"
|
||||
];
|
||||
|
||||
nativeCheckInputs = [
|
||||
iproute2
|
||||
iproute2 # for `ip`
|
||||
procps # for `ps`
|
||||
util-linux # for `setsid`
|
||||
which
|
||||
|
@@ -1,5 +1,6 @@
|
||||
// vim: set shiftwidth=2 :
|
||||
|
||||
use config;
|
||||
use errors;
|
||||
use errors::ext;
|
||||
use fmt;
|
||||
@@ -10,22 +11,25 @@ use os::exec;
|
||||
use restrict;
|
||||
use rt;
|
||||
use rt::ext;
|
||||
use strings;
|
||||
use unix;
|
||||
|
||||
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`
|
||||
// 1. fork into (P, C1)
|
||||
// 2. child C1: enter netns, signal parent P.
|
||||
// 3. parent P: fork and spawn `pasta --pid /proc/fd/$N` (C2)
|
||||
// then dumbly wait on child (C2) until completion
|
||||
// 4. child C1:: wait for `pasta` to signal readiness on fd `$N`
|
||||
//
|
||||
// after that, we can continue on & exec into the user code
|
||||
// after that, C1 can continue on & exec into the user code
|
||||
//
|
||||
// TODO: this currently lacks the synchronization described above
|
||||
// TODO: this currently lacks the synchronization described above.
|
||||
// it still generally _works_, but possible that some applications are flaky
|
||||
|
||||
let (pipe_parent_rd, pipe_child_wr) = unix::pipe()!;
|
||||
log::printfln("[namespace/pasta]: forking: parent will launch pasta while child will exec user code");
|
||||
match (os::exec::fork()) {
|
||||
case let child_pid: os::exec::process =>
|
||||
io::close(pipe_child_wr)!;
|
||||
@@ -78,29 +82,32 @@ fn attach_pasta(net: restrict::net_subset, child: os::exec::process) (void | os:
|
||||
// log::printfln("[namespace/pasta] notify {}", notify_fd_path);
|
||||
// defer free(notify_fd_path);
|
||||
|
||||
// TODO: append dns, gateway arguments to `pasta`.
|
||||
yield rt::ext::execvpe(
|
||||
// TODO: append dns argument to `pasta`.
|
||||
let pasta_args = [
|
||||
"pasta",
|
||||
[
|
||||
"pasta",
|
||||
"--ipv4-only",
|
||||
// pasta `up`s `lo` regardless of this flag; `--config-net` just tells
|
||||
// it to assign an IP and routes to the new device it creates
|
||||
"--config-net",
|
||||
// port forwards:
|
||||
"-u", "none",
|
||||
"-t", "none",
|
||||
"-U", "none",
|
||||
"-T", "none",
|
||||
// "-U", "53", #< if using the host's DNS
|
||||
// "-T", "53", #< if using the host's DNS
|
||||
"--outbound-if4", net.dev,
|
||||
"--gateway", net.gateway,
|
||||
"--netns-only",
|
||||
// pidstr,
|
||||
"--netns", netns_path,
|
||||
// "--pid", notify_fd_path,
|
||||
],
|
||||
"--ipv4-only",
|
||||
// pasta `up`s `lo` regardless of this flag; `--config-net` just tells
|
||||
// it to assign an IP and routes to the new device it creates
|
||||
"--config-net",
|
||||
// port forwards:
|
||||
"-u", "none",
|
||||
"-t", "none",
|
||||
"-U", "none",
|
||||
"-T", "none",
|
||||
// "-U", "53", #< if using the host's DNS
|
||||
// "-T", "53", #< if using the host's DNS
|
||||
"--outbound-if4", net.dev,
|
||||
"--gateway", net.gateway,
|
||||
"--netns-only",
|
||||
// pidstr,
|
||||
"--netns", netns_path,
|
||||
// "--pid", notify_fd_path,
|
||||
];
|
||||
log::printfln("[namespace/pasta]: invoking pasta: {}", strings::join(" ", pasta_args...));
|
||||
yield rt::ext::execvpe_or_default(
|
||||
config::PASTA,
|
||||
"pasta",
|
||||
pasta_args,
|
||||
os::getenvs(),
|
||||
);
|
||||
};
|
||||
@@ -116,7 +123,8 @@ fn config_routing_in_ns(net: restrict::net_subset) (void | os::exec::error | rt:
|
||||
// forward dns to the desired endpoint
|
||||
let dnsdest = fmt::asprintf("{}:53", net.dns);
|
||||
defer free(dnsdest);
|
||||
let rc = rt::ext::shellvpe(
|
||||
let rc = rt::ext::shellvpe_or_default(
|
||||
config::IPTABLES,
|
||||
"iptables",
|
||||
[
|
||||
"iptables",
|
||||
@@ -139,7 +147,8 @@ fn config_routing_in_ns(net: restrict::net_subset) (void | os::exec::error | rt:
|
||||
// therefore, set it up *now*, and delete the addrs, and then since it's
|
||||
// already up they won't re-appear when we exec pasta.
|
||||
|
||||
let rc = rt::ext::shellvpe(
|
||||
let rc = rt::ext::shellvpe_or_default(
|
||||
config::IP,
|
||||
"ip",
|
||||
[
|
||||
"ip",
|
||||
@@ -150,7 +159,8 @@ fn config_routing_in_ns(net: restrict::net_subset) (void | os::exec::error | rt:
|
||||
)?;
|
||||
log::printfln("[namespace/pasta] 'ip link set lo up' exited {}", rc.status);
|
||||
|
||||
let rc = rt::ext::shellvpe(
|
||||
let rc = rt::ext::shellvpe_or_default(
|
||||
config::IP,
|
||||
"ip",
|
||||
[
|
||||
"ip",
|
||||
|
@@ -37,6 +37,16 @@ export fn execvpe(name: str, argv: []str, envp: []str = []) rt::errno = {
|
||||
};
|
||||
};
|
||||
|
||||
// try `execvpe(name, argv, envp)`,
|
||||
// but if it fails to find `name` on PATH, then `execve(default, argv, envp)`
|
||||
export fn execvpe_or_default(default: str, name: str, argv: []str, envp: []str = []) rt::errno = {
|
||||
let rc = execvpe(name, argv, envp);
|
||||
return switch (rc) {
|
||||
case rt::ENOENT => yield execve(default, argv, envp);
|
||||
case => yield rc;
|
||||
};
|
||||
};
|
||||
|
||||
// run the provided command in a new process, and wait for its return.
|
||||
// under the hood:
|
||||
// 1. fork
|
||||
@@ -53,6 +63,19 @@ export fn shellvpe(name: str, argv: []str, envp: []str = []) (os::exec::status |
|
||||
};
|
||||
};
|
||||
|
||||
// same as `shellvpe(name, argv, envp)` but fallback to `name=default` if `name`
|
||||
// isn't found on PATH
|
||||
export fn shellvpe_or_default(default: str, name: str, argv: []str, envp: []str = []) (os::exec::status | os::exec::error) = {
|
||||
return match (os::exec::fork()) {
|
||||
case void =>
|
||||
errors::ext::check("shellvpe", execvpe_or_default(default, name, argv, envp));
|
||||
assert(false, "unreachable");
|
||||
return os::exec::status { ... };
|
||||
case let child_pid: os::exec::process =>
|
||||
yield wait_child(child_pid);
|
||||
};
|
||||
};
|
||||
|
||||
fn wait_child(child_pid: os::exec::process) (os::exec::status | os::exec::error) = {
|
||||
for (true) {
|
||||
match (os::exec::wait(&child_pid)) {
|
||||
@@ -82,6 +105,8 @@ fn to_cstr_array(strs: []str) []nullable *const c::char = {
|
||||
|
||||
// split `path` on `:`, crawl it, and try executing until we succeed.
|
||||
// note that 1:1 parity with libc's execvpe is probably not what we have here.
|
||||
//
|
||||
// returns ENOENT if unable to locate the command on PATH
|
||||
fn _execvpe(path: str, name: str, argv: []str, envp: []str = []) rt::errno = {
|
||||
let status = rt::ENOENT;
|
||||
let pieces = strings::split(path, ":");
|
||||
|
Reference in New Issue
Block a user