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
|
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 $@
|
hare build -o $@
|
||||||
|
|
||||||
install:
|
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,
|
hareHook,
|
||||||
iproute2,
|
iproute2,
|
||||||
|
iptables,
|
||||||
|
lib,
|
||||||
|
passt,
|
||||||
procps,
|
procps,
|
||||||
stdenv,
|
stdenv,
|
||||||
util-linux,
|
util-linux,
|
||||||
@@ -10,11 +13,20 @@
|
|||||||
version = "0.1.0";
|
version = "0.1.0";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
|
|
||||||
nativeBuildInputs = [ hareHook ];
|
nativeBuildInputs = [
|
||||||
makeFlags = [ "PREFIX=${builtins.placeholder "out"}" ];
|
hareHook
|
||||||
|
which
|
||||||
|
];
|
||||||
|
|
||||||
|
makeFlags = [
|
||||||
|
"PREFIX=${builtins.placeholder "out"}"
|
||||||
|
"IP=${lib.getExe' iproute2 "ip"}"
|
||||||
|
"IPTABLES=${lib.getExe' iptables "iptables"}"
|
||||||
|
"PASTA=${lib.getExe' passt "pasta"}"
|
||||||
|
];
|
||||||
|
|
||||||
nativeCheckInputs = [
|
nativeCheckInputs = [
|
||||||
iproute2
|
iproute2 # for `ip`
|
||||||
procps # for `ps`
|
procps # for `ps`
|
||||||
util-linux # for `setsid`
|
util-linux # for `setsid`
|
||||||
which
|
which
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
// vim: set shiftwidth=2 :
|
// vim: set shiftwidth=2 :
|
||||||
|
|
||||||
|
use config;
|
||||||
use errors;
|
use errors;
|
||||||
use errors::ext;
|
use errors::ext;
|
||||||
use fmt;
|
use fmt;
|
||||||
@@ -10,22 +11,25 @@ use os::exec;
|
|||||||
use restrict;
|
use restrict;
|
||||||
use rt;
|
use rt;
|
||||||
use rt::ext;
|
use rt::ext;
|
||||||
|
use strings;
|
||||||
use unix;
|
use unix;
|
||||||
|
|
||||||
fn setup_pasta(net: restrict::net_subset) void = {
|
fn setup_pasta(net: restrict::net_subset) void = {
|
||||||
// `pasta PID [options]`: creates a device in the netns of PID.
|
// `pasta PID [options]`: creates a device in the netns of PID.
|
||||||
// ordering:
|
// ordering:
|
||||||
// 1. fork
|
// 1. fork into (P, C1)
|
||||||
// 2. child: enter netns, signal parent.
|
// 2. child C1: enter netns, signal parent P.
|
||||||
// 3. parent: fork and spawn `pasta --pid /proc/fd/$N`
|
// 3. parent P: fork and spawn `pasta --pid /proc/fd/$N` (C2)
|
||||||
// then dumbly wait on child until completion
|
// then dumbly wait on child (C2) until completion
|
||||||
// 4. child: wait for `pasta` to signal readiness on fd `$N`
|
// 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()!;
|
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()) {
|
match (os::exec::fork()) {
|
||||||
case let child_pid: os::exec::process =>
|
case let child_pid: os::exec::process =>
|
||||||
io::close(pipe_child_wr)!;
|
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);
|
// log::printfln("[namespace/pasta] notify {}", notify_fd_path);
|
||||||
// defer free(notify_fd_path);
|
// defer free(notify_fd_path);
|
||||||
|
|
||||||
// TODO: append dns, gateway arguments to `pasta`.
|
// TODO: append dns argument to `pasta`.
|
||||||
yield rt::ext::execvpe(
|
let pasta_args = [
|
||||||
"pasta",
|
"pasta",
|
||||||
[
|
"--ipv4-only",
|
||||||
"pasta",
|
// pasta `up`s `lo` regardless of this flag; `--config-net` just tells
|
||||||
"--ipv4-only",
|
// it to assign an IP and routes to the new device it creates
|
||||||
// pasta `up`s `lo` regardless of this flag; `--config-net` just tells
|
"--config-net",
|
||||||
// it to assign an IP and routes to the new device it creates
|
// port forwards:
|
||||||
"--config-net",
|
"-u", "none",
|
||||||
// port forwards:
|
"-t", "none",
|
||||||
"-u", "none",
|
"-U", "none",
|
||||||
"-t", "none",
|
"-T", "none",
|
||||||
"-U", "none",
|
// "-U", "53", #< if using the host's DNS
|
||||||
"-T", "none",
|
// "-T", "53", #< if using the host's DNS
|
||||||
// "-U", "53", #< if using the host's DNS
|
"--outbound-if4", net.dev,
|
||||||
// "-T", "53", #< if using the host's DNS
|
"--gateway", net.gateway,
|
||||||
"--outbound-if4", net.dev,
|
"--netns-only",
|
||||||
"--gateway", net.gateway,
|
// pidstr,
|
||||||
"--netns-only",
|
"--netns", netns_path,
|
||||||
// pidstr,
|
// "--pid", notify_fd_path,
|
||||||
"--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(),
|
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
|
// forward dns to the desired endpoint
|
||||||
let dnsdest = fmt::asprintf("{}:53", net.dns);
|
let dnsdest = fmt::asprintf("{}:53", net.dns);
|
||||||
defer free(dnsdest);
|
defer free(dnsdest);
|
||||||
let rc = rt::ext::shellvpe(
|
let rc = rt::ext::shellvpe_or_default(
|
||||||
|
config::IPTABLES,
|
||||||
"iptables",
|
"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
|
// 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.
|
// 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",
|
||||||
[
|
[
|
||||||
"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);
|
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",
|
||||||
[
|
[
|
||||||
"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.
|
// run the provided command in a new process, and wait for its return.
|
||||||
// under the hood:
|
// under the hood:
|
||||||
// 1. fork
|
// 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) = {
|
fn wait_child(child_pid: os::exec::process) (os::exec::status | os::exec::error) = {
|
||||||
for (true) {
|
for (true) {
|
||||||
match (os::exec::wait(&child_pid)) {
|
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.
|
// 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.
|
// 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 = {
|
fn _execvpe(path: str, name: str, argv: []str, envp: []str = []) rt::errno = {
|
||||||
let status = rt::ENOENT;
|
let status = rt::ENOENT;
|
||||||
let pieces = strings::split(path, ":");
|
let pieces = strings::split(path, ":");
|
||||||
|
Reference in New Issue
Block a user