aboutgitcodebugslistschat
diff options
context:
space:
mode:
authorChristian Korneck <christian@korneck.de>2026-09-06 15:17:58 +0200
committerStefano Brivio <sbrivio@redhat.com>2026-09-16 10:54:02 +0200
commit588b545dae741bec6fd7622a33c7852c06d72a59 (patch)
tree8d56f7d9e22278b3c61b71e25ab4c152cb9ebedd
parent3a890a678fbeb930d41274c0258c1905f43cc068 (diff)
downloadpasst-588b545dae741bec6fd7622a33c7852c06d72a59.tar
passt-588b545dae741bec6fd7622a33c7852c06d72a59.tar.gz
passt-588b545dae741bec6fd7622a33c7852c06d72a59.tar.bz2
passt-588b545dae741bec6fd7622a33c7852c06d72a59.tar.lz
passt-588b545dae741bec6fd7622a33c7852c06d72a59.tar.xz
passt-588b545dae741bec6fd7622a33c7852c06d72a59.tar.zst
passt-588b545dae741bec6fd7622a33c7852c06d72a59.zip
pasta: Add --no-pidns to keep spawned command in caller's PID namespaceHEADmaster
This is to allow running pasta inside a container without unmasking /proc for the whole container (Docker's --security-opt systempaths=unconfined, Podman's --security-opt unmask=ALL), which is undesirable as it exposes /proc/sysrq-trigger and other masked paths. In spawn mode, pasta clones the command with CLONE_NEWPID and mounts a new procfs instance on /proc, so that it matches the new PID namespace. Mounting procfs in a new user namespace requires a fully visible, unobstructed procfs. Container runtimes deliberately obstruct /proc (Docker, for example, masks /proc/kcore and friends and mounts /proc/sys read-only), so the mount is refused: Couldn't mount /proc: Operation not permitted We only warn and continue, leaving the command in a new PID namespace while the visible /proc still numbers processes in the outer one. Anything resolving its own PID through /proc then fails, for example bubblewrap: bwrap: open /proc/22/ns/ns failed: No such file or directory Add a --no-pidns option: skip CLONE_NEWPID for the spawned command and don't mount /proc, which is then not needed. User, network, mount, UTS and IPC namespaces, --config-net and port forwarding are unaffected. The option is rejected together with PID or --netns, as it only makes sense when we spawn the command ourselves. Add a test checking that, by default, the command runs in a new PID namespace, and that --no-pidns keeps it in the caller's one. Signed-off-by: Christian Korneck <christian@korneck.de> Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
-rw-r--r--conf.c11
-rw-r--r--passt.112
-rw-r--r--passt.h2
-rw-r--r--pasta.c18
-rw-r--r--test/pasta_options/no_pidns25
-rwxr-xr-xtest/run1
6 files changed, 62 insertions, 7 deletions
diff --git a/conf.c b/conf.c
index ea0f4bc..149a4c7 100644
--- a/conf.c
+++ b/conf.c
@@ -742,6 +742,7 @@ pasta_opts:
" implied if PATH or NAME are given without --userns\n"
" --no-netns-quit Don't quit if filesystem-bound target\n"
" network namespace is deleted\n"
+ " --no-pidns Don't spawn command in a new PID namespace\n"
" --config-net Configure tap interface in namespace\n"
" --no-copy-routes DEPRECATED:\n"
" Don't copy all routes to namespace\n"
@@ -1315,6 +1316,7 @@ void conf(struct ctx *c, int argc, char **argv)
{"stats", required_argument, NULL, 31 },
{"conf-path", required_argument, NULL, 'c' },
{"chroot-fallback", no_argument, NULL, 32 },
+ {"no-pidns", no_argument, NULL, 33 },
{ 0 },
};
const char *optstring = "+dqfel:hs:c:F:I:p:P:m:a:n:M:g:i:o:D:S:H:461t:u:T:U:";
@@ -1559,6 +1561,12 @@ void conf(struct ctx *c, int argc, char **argv)
case 32:
c->chroot_fallback = true;
break;
+ case 33:
+ if (c->mode != MODE_PASTA)
+ die("--no-pidns is for pasta mode only");
+
+ c->no_pidns = true;
+ break;
case 'd':
c->debug = 1;
c->quiet = 0;
@@ -1921,6 +1929,9 @@ void conf(struct ctx *c, int argc, char **argv)
else if (optind != argc)
die("Extra non-option argument: %s", argv[optind]);
+ if (c->no_pidns && *netns)
+ die("--no-pidns is incompatible with PID or --netns");
+
conf_open_files(c); /* Before any possible setuid() / setgid() */
isolate_user(c, uid, gid, !netns_only, userns);
diff --git a/passt.1 b/passt.1
index 53e072a..0b780fe 100644
--- a/passt.1
+++ b/passt.1
@@ -705,6 +705,18 @@ network namespace is represented by a procfs entry, and that entry is deleted,
representing the fact that a process with the given PID terminated.
.TP
+.BR \-\-no-pidns
+Don't create a new PID namespace for the spawned command or shell: keep it in
+the PID namespace \fBpasta\fR itself runs in, and don't mount a new
+\fIprocfs\fR instance on \fI/proc\fR for it. This is useful in environments
+where mounting \fIprocfs\fR is not permitted, such as containers, where the
+command would otherwise get a \fI/proc\fR view that doesn't match its own PID
+namespace. Note that, without a PID namespace, processes started by the
+command are not terminated once the command exits.
+
+This option can't be specified with a PID or with \-\-netns.
+
+.TP
.BR \-\-config-net
Configure networking in the namespace: set up addresses and routes as configured
or sourced from the host, and bring up the tap interface.
diff --git a/passt.h b/passt.h
index 51ccd4f..afd8e9f 100644
--- a/passt.h
+++ b/passt.h
@@ -195,6 +195,7 @@ struct ip6_ctx {
* @pasta_ifn: Name of namespace interface for pasta
* @pasta_ifi: Index of namespace interface for pasta
* @pasta_conf_ns: Configure namespace after creating it
+ * @no_pidns: Don't create a new PID namespace for spawned command
* @fwd: Forwarding tables
* @fwd_pending: Pending forward tables
* @no_tcp: Disable TCP operation
@@ -278,6 +279,7 @@ struct ctx {
char pasta_ifn[IF_NAMESIZE];
unsigned int pasta_ifi;
int pasta_conf_ns;
+ bool no_pidns;
struct fwd_table *fwd[PIF_NUM_TYPES];
struct fwd_table *fwd_pending[PIF_NUM_TYPES];
diff --git a/pasta.c b/pasta.c
index 140c102..7ba5d75 100644
--- a/pasta.c
+++ b/pasta.c
@@ -194,15 +194,17 @@ static int pasta_spawn_cmd(void *arg)
if (prctl(PR_SET_PDEATHSIG, SIGKILL))
die_perror("Couldn't set PR_SET_PDEATHSIG");
- /* We run in a detached PID and mount namespace: mount /proc over */
- if (mount("", "/proc", "proc", 0, NULL))
+ a = (const struct pasta_spawn_cmd_arg *)arg;
+
+ /* We run in a detached mount namespace, and, unless --no-pidns was
+ * given, in a detached PID namespace: mount /proc over
+ */
+ if (!a->c->no_pidns && mount("", "/proc", "proc", 0, NULL))
warn_perror("Couldn't mount /proc");
if (write_file("/proc/sys/net/ipv4/ping_group_range", "0 0"))
warn("Cannot set ping_group_range, ICMP requests might fail");
- a = (const struct pasta_spawn_cmd_arg *)arg;
-
conf_hostname_len = strlen(a->c->hostname);
if (conf_hostname_len > 0) {
if (sethostname(a->c->hostname, conf_hostname_len))
@@ -239,6 +241,7 @@ static int pasta_spawn_cmd(void *arg)
*/
void pasta_start_ns(struct ctx *c, int argc, char *argv[])
{
+ int flags = CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWUTS | CLONE_NEWNS;
char ns_fn_stack[NS_FN_STACK_SIZE]
__attribute__ ((aligned(__alignof__(max_align_t))));
struct pasta_spawn_cmd_arg arg = {
@@ -272,10 +275,11 @@ void pasta_start_ns(struct ctx *c, int argc, char *argv[])
sigaddset(&set, SIGUSR1);
sigprocmask(SIG_BLOCK, &set, NULL);
+ if (!c->no_pidns)
+ flags |= CLONE_NEWPID;
+
pasta_child_pid = do_clone(pasta_spawn_cmd, ns_fn_stack,
- sizeof(ns_fn_stack),
- CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNET |
- CLONE_NEWUTS | CLONE_NEWNS | SIGCHLD,
+ sizeof(ns_fn_stack), flags | SIGCHLD,
(void *)&arg);
if (pasta_child_pid == -1)
diff --git a/test/pasta_options/no_pidns b/test/pasta_options/no_pidns
new file mode 100644
index 0000000..f565b2f
--- /dev/null
+++ b/test/pasta_options/no_pidns
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# PASST - Plug A Simple Socket Transport
+# for qemu/UNIX domain socket mode
+#
+# PASTA - Pack A Subtle Tap Abstraction
+# for network namespace/tap device mode
+#
+# test/pasta_options/no_pidns - Check --no-pidns handling
+
+htools readlink
+
+test Command is spawned in a new PID namespace by default
+set PIDNS __STATEDIR__/pidns
+set OUT __STATEDIR__/no-pidns.out
+set ERR __STATEDIR__/no-pidns.err
+
+passt readlink /proc/self/ns/pid > __PIDNS__
+passt ./pasta -q -- readlink /proc/self/ns/pid > __OUT__ 2> __ERR__
+check [ "$(cat __PIDNS__)" != "$(cat __OUT__)" ]
+
+test --no-pidns keeps command in the caller's PID namespace
+passt ./pasta -q --no-pidns -- readlink /proc/self/ns/pid > __OUT__ 2> __ERR__
+check [ "$(cat __PIDNS__)" = "$(cat __OUT__)" ]
+check [ ! -s __ERR__ ]
diff --git a/test/run b/test/run
index c4073cc..14ddbbf 100755
--- a/test/run
+++ b/test/run
@@ -86,6 +86,7 @@ run() {
setup pasta_options
test pasta_options/log_to_file
test pasta_options/netns_only
+ test pasta_options/no_pidns
teardown pasta_options
setup build