public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
* [PATCH] pasta: Add --no-pidns to keep spawned command in caller's PID namespace
@ 2026-09-06 13:17 Christian Korneck
  0 siblings, 0 replies; only message in thread
From: Christian Korneck @ 2026-09-06 13:17 UTC (permalink / raw)
  To: passt-dev; +Cc: Christian Korneck

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>
---
 conf.c                      | 11 +++++++++++
 passt.1                     | 12 ++++++++++++
 passt.h                     |  2 ++
 pasta.c                     | 18 +++++++++++-------
 test/pasta_options/no_pidns | 25 +++++++++++++++++++++++++
 test/run                    |  1 +
 6 files changed, 62 insertions(+), 7 deletions(-)
 create mode 100644 test/pasta_options/no_pidns

diff --git a/conf.c b/conf.c
index faf2681..16b2f40 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"
@@ -1348,6 +1349,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:";
@@ -1589,6 +1591,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;
@@ -1948,6 +1956,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
@@ -704,6 +704,18 @@ is bound to the filesystem, and the given path is deleted, or if the target
 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
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 5aa56b7..2d916e1 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))
@@ -243,6 +245,7 @@ static int pasta_spawn_cmd(void *arg)
 void pasta_start_ns(struct ctx *c, uid_t uid, gid_t gid, bool config_idmaps,
 		    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 = {
@@ -293,10 +296,11 @@ void pasta_start_ns(struct ctx *c, uid_t uid, gid_t gid, bool config_idmaps,
 	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
-- 
2.55.0


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-09-06 13:18 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-06 13:17 [PATCH] pasta: Add --no-pidns to keep spawned command in caller's PID namespace Christian Korneck

Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).