Allow pasta to take a command to execute

When not given an existing PID or network namspace to attach to, pasta
spawns a shell.  Most commands which can spawn a shell in an altered
environment can also run other commands in that same environment, which can
be useful in automation.

Allow pasta to do the same thing; it can be given an arbitrary command to
run in the network and user namespace which pasta creates.  If neither a
command nor an existing PID or netns to attach to is given, continue to
spawn a default shell, as before.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
David Gibson 2022-08-26 14:58:39 +10:00 committed by Stefano Brivio
parent c188736cd8
commit 1392bc5ca0
4 changed files with 51 additions and 25 deletions

27
conf.c
View file

@ -550,7 +550,8 @@ static int conf_ns_pid(char *userns, char *netns, const char *arg)
return 0; return 0;
} }
return -EINVAL; /* Not a PID, later code will treat as a command */
return 0;
} }
/** /**
@ -1498,14 +1499,18 @@ void conf(struct ctx *c, int argc, char **argv)
check_root(c); check_root(c);
if (c->mode == MODE_PASTA && optind + 1 == argc) { if (c->mode == MODE_PASTA) {
ret = conf_ns_pid(userns, netns, argv[optind]); if (*netns && optind != argc) {
if (ret < 0) err("Both --netns and PID or command given");
usage(argv[0]); usage(argv[0]);
} else if (c->mode == MODE_PASTA && *userns } else if (optind + 1 == argc) {
&& !*netns && optind == argc) { ret = conf_ns_pid(userns, netns, argv[optind]);
err("--userns requires --netns or PID"); if (ret < 0)
usage(argv[0]); usage(argv[0]);
} else if (*userns && !*netns && optind == argc) {
err("--userns requires --netns or PID");
usage(argv[0]);
}
} else if (optind != argc) { } else if (optind != argc) {
usage(argv[0]); usage(argv[0]);
} }
@ -1519,7 +1524,11 @@ void conf(struct ctx *c, int argc, char **argv)
if (ret < 0) if (ret < 0)
usage(argv[0]); usage(argv[0]);
} else { } else {
pasta_start_ns(c); if (*userns) {
err("Both --userns and command given");
usage(argv[0]);
}
pasta_start_ns(c, argc - optind, argv + optind);
} }
} }

14
passt.1
View file

@ -15,7 +15,10 @@
[\fIOPTION\fR]... [\fIOPTION\fR]...
.br .br
.B pasta .B pasta
[\fIOPTION\fR]... [\fIPID\fR] [\fIOPTION\fR]... [\fICOMMAND\fR [\fIARG\fR]...]
.br
.B pasta
[\fIOPTION\fR]... \fIPID\fR
.br .br
.B pasta .B pasta
[\fIOPTION\fR]... \fB--netns\fR [\fIPATH\fR|\fINAME\fR] [\fIOPTION\fR]... \fB--netns\fR [\fIPATH\fR|\fINAME\fR]
@ -62,10 +65,11 @@ or with the \fBqrap\fR(1) wrapper.
equivalent functionality to network namespaces, as the one offered by equivalent functionality to network namespaces, as the one offered by
\fBpasst\fR for virtual machines. \fBpasst\fR for virtual machines.
If PID or --netns are given, \fBpasta\fR associates to an existing user and If PID or --netns are given, \fBpasta\fR associates to an existing
network namespace. Otherwise, \fBpasta\fR creates a new user and network user and network namespace. Otherwise, \fBpasta\fR creates a new user
namespace, and spawns an interactive shell within this context. A \fItap\fR and network namespace, and spawns the given command or a default shell
device within the network namespace is created to provide network connectivity. within this context. A \fItap\fR device within the network namespace
is created to provide network connectivity.
For local TCP and UDP traffic only, \fBpasta\fR also implements a bypass path For local TCP and UDP traffic only, \fBpasta\fR also implements a bypass path
directly mapping Layer-4 sockets between \fIinit\fR and target namespaces, directly mapping Layer-4 sockets between \fIinit\fR and target namespaces,

33
pasta.c
View file

@ -108,6 +108,7 @@ netns:
struct pasta_setup_ns_arg { struct pasta_setup_ns_arg {
struct ctx *c; struct ctx *c;
int euid; int euid;
char **argv;
}; };
/** /**
@ -119,7 +120,6 @@ struct pasta_setup_ns_arg {
static int pasta_setup_ns(void *arg) static int pasta_setup_ns(void *arg)
{ {
struct pasta_setup_ns_arg *a = (struct pasta_setup_ns_arg *)arg; struct pasta_setup_ns_arg *a = (struct pasta_setup_ns_arg *)arg;
char *shell;
if (!a->c->netns_only) { if (!a->c->netns_only) {
char buf[BUFSIZ]; char buf[BUFSIZ];
@ -139,29 +139,42 @@ static int pasta_setup_ns(void *arg)
FWRITE("/proc/sys/net/ipv4/ping_group_range", "0 0", FWRITE("/proc/sys/net/ipv4/ping_group_range", "0 0",
"Cannot set ping_group_range, ICMP requests might fail"); "Cannot set ping_group_range, ICMP requests might fail");
shell = getenv("SHELL") ? getenv("SHELL") : "/bin/sh"; execvp(a->argv[0], a->argv);
if (strstr(shell, "/bash"))
execve(shell, ((char *[]) { shell, "-l", NULL }), environ);
else
execve(shell, ((char *[]) { shell, NULL }), environ);
perror("execve"); perror("execvp");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/** /**
* pasta_start_ns() - Fork shell in new namespace if target ns is not given * pasta_start_ns() - Fork command in new namespace if target ns is not given
* @c: Execution context * @c: Execution context
* @argc: Number of arguments for spawned command
* @argv: Command to spawn and arguments
*/ */
void pasta_start_ns(struct ctx *c) void pasta_start_ns(struct ctx *c, int argc, char *argv[])
{ {
struct pasta_setup_ns_arg arg = { .c = c, .euid = geteuid() }; struct pasta_setup_ns_arg arg = {
.c = c,
.euid = geteuid(),
.argv = argv,
};
char *shell = getenv("SHELL") ? getenv("SHELL") : "/bin/sh";
char *sh_argv[] = { shell, NULL };
char *bash_argv[] = { shell, "-l", NULL };
char ns_fn_stack[NS_FN_STACK_SIZE]; char ns_fn_stack[NS_FN_STACK_SIZE];
c->foreground = 1; c->foreground = 1;
if (!c->debug) if (!c->debug)
c->quiet = 1; c->quiet = 1;
if (argc == 0) {
if (strstr(shell, "/bash")) {
arg.argv = bash_argv;
} else {
arg.argv = sh_argv;
}
}
pasta_child_pid = clone(pasta_setup_ns, pasta_child_pid = clone(pasta_setup_ns,
ns_fn_stack + sizeof(ns_fn_stack) / 2, ns_fn_stack + sizeof(ns_fn_stack) / 2,
(c->netns_only ? 0 : CLONE_NEWNET) | (c->netns_only ? 0 : CLONE_NEWNET) |

View file

@ -6,7 +6,7 @@
#ifndef PASTA_H #ifndef PASTA_H
#define PASTA_H #define PASTA_H
void pasta_start_ns(struct ctx *c); void pasta_start_ns(struct ctx *c, int argc, char *argv[]);
void pasta_ns_conf(struct ctx *c); void pasta_ns_conf(struct ctx *c);
void pasta_child_handler(int signal); void pasta_child_handler(int signal);
int pasta_netns_quit_init(struct ctx *c); int pasta_netns_quit_init(struct ctx *c);