public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: Stefano Brivio <sbrivio@redhat.com>
To: Christian Korneck <christian@korneck.de>
Cc: passt-dev@passt.top
Subject: Re: [PATCH] pasta: Add --no-pidns to keep spawned command in caller's PID namespace
Date: Tue, 08 Sep 2026 11:45:18 +0200 (CEST)	[thread overview]
Message-ID: <20260908114518.3af2d1c2@elisabeth> (raw)
In-Reply-To: <20260906131758.121019-1-christian@korneck.de>

Christian, thanks for the patch. At a glance, it looks pretty good to
me (see also my reply to David).

There's just one thing I wanted to mention for your awareness before
continuing with it, though: in the past couple of months, we had
several reports of bubblewrap being started by pasta (directly or
indirectly), see in particular:

  https://archives.passt.top/passt-user/671252c8-88f6-45b7-b719-b82786e84bb7@gnedt.at/

  https://bugs.passt.top/show_bug.cgi?id=204

  https://archives.passt.top/passt-dev/20260731132601.422518-1-rlawrence@tamu.edu/
  (still pending)

so, while these kind of changes obviously appear useful for somebody,
there's still the question of whether you want to consider spawning
bubblewrap separately and make it join a network namespace connected by
pasta (might need changes in bubblewrap, or something like
https://github.com/reubenfirmin/bubblewrap-tui#why-pasta).

-- 
Stefano

On Sun,  6 Sep 2026 15:17:58 +0200
Christian Korneck <christian@korneck.de> wrote:

> 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


      parent reply	other threads:[~2026-09-08  9:45 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-06 13:17 Christian Korneck
2026-09-08  6:03 ` David Gibson
2026-09-08  9:45   ` Stefano Brivio
2026-09-08 11:09     ` David Gibson
2026-09-08  9:45 ` Stefano Brivio [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260908114518.3af2d1c2@elisabeth \
    --to=sbrivio@redhat.com \
    --cc=christian@korneck.de \
    --cc=passt-dev@passt.top \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).