From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=none (p=none dis=none) header.from=korneck.de Authentication-Results: passt.top; dkim=pass (2048-bit key; unprotected) header.d=korneck-de.20251104.gappssmtp.com header.i=@korneck-de.20251104.gappssmtp.com header.a=rsa-sha256 header.s=20251104 header.b=v7oJ0Kt/; dkim-atps=neutral Received: from mail-wm1-x32e.google.com (mail-wm1-x32e.google.com [IPv6:2a00:1450:4864:20::32e]) by passt.top (Postfix) with ESMTPS id B231D5A0262 for ; Sun, 06 Sep 2026 15:18:05 +0200 (CEST) Received: by mail-wm1-x32e.google.com with SMTP id 5b1f17b1804b1-49b0d8bc2aaso33039405e9.0 for ; Sun, 06 Sep 2026 06:18:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=korneck-de.20251104.gappssmtp.com; s=20251104; t=1788700685; x=1789305485; darn=passt.top; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=kfdVcRKsWRVdq3vvoikbjTlVIaQngDlidSImUzGnV38=; b=v7oJ0Kt/MfQ9CGVa5yIHNqoEXwU2Q06JSFdVbqspGP7VCG/wuueUpfUG3a6TXdoL12 pNc5xan8svYh2d+3Hpx3XSQARZqY92V39ixe6zf+WTzZFpXauvcokeV0OyrjsI+Vi2mz gGjL31/HMgx6G03MPgYkUWZIULNQniQcbmVjvoJOcXGVIj0XjS43wtYirKd9IrRf1NM1 a937bG4s9e3TLxk+ip9Gj2kgk73Ol9eiT7pNlatScImwyII8+FvoSNTunqQvmBtQsgpH 1+Cj5CHoqDSs2JYFIrIvV/2a1QJz/IRX7edrfrgUjeGFN3hbosDkUWkXLJdkrAnSbpe/ BZgw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788700685; x=1789305485; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=kfdVcRKsWRVdq3vvoikbjTlVIaQngDlidSImUzGnV38=; b=ND6pTKXhGlXU5ff35XH0Y0TboF+SpKvn7VVL9zHnCGIDgoDxqj5uj4/GnUWNDsM5o7 Pu2vDlDD9pYjlCL3GZ0lHcaLvwWFRYu3hiAbtw3UUOywW1bFPAYmxyVEXsznu9COjBD8 eiaFuatTi/pauM5poWDJLANvHtbm+luork6gBfC8b2R6gycPKfXO73ENpO2BldlwmOsU EoOt9c99H7Isvt6+OQercCdtTK1GvQIE2zjG5lgJLSZyz5ZnY69mDZFbJW/NCXJiNR4A C9Kx7pBB+yf4bKgoYW0wKrwQtmnqSx8YnQeteovMBOfxZoUlVzY07vZ70heDX1zcjflf iy8A== X-Gm-Message-State: AFuF++nPjetgzbUdn+lDdQP5xtocO+SN+3bno2UYCLaqy7NOzR7mHics fa3CpmMKgOj7Ot4fGAUbLB7EI4LT2eqQ/nFz9agNh6UfpR7u/edrtTncMtsZq+Ry3Z3BaEr7uHx zpXwQwA== X-Gm-Gg: AYBFou1e1iT/5EgQCNPBjsQak3Um9vb0pMfFLjKoOIrthqNGsk8jvuCj5JJ82Hcz8Of mX3rIGWcWoDHO2EBtOuIb5l7eMxGZp3GpMq26gs9/Y39KJEoondhysUX3Fdd+6a+RCd3jzjLKBj Op9PgbDbSa/vr9UZOtorNZC2XXT904p3cIhIPZCKaDg3JC0TcCN/IHtb8vzmzurSXR/L04WvIWN A0DB0o/CgL9Uao9PbWxl/5L9y+GeVMwBW7yQpon6kJXQSZ2kAqY6PUlIPh1KhVNNYpOkR3o+/Dl zobpchLgRvH5QugaOVAZjsMElVs3QM+cEl6MgyptU1DTRiQZyQUmq1/S/eyMVjjv0/mo0SH/QgY Ym8AgtQGeRuaQwDnCS4XMwQQoEpq5lXBVx2RQNIYBPSQHV9fKyHbEgF5/ToytKgNyibRiTT4zgY /yzdbZHUWvSftM1g9pWcb1HsNqUFTZmG1cEfy1vJ3Nngi2xDNA5nXBrnquDLabv472kVzBtVIyf J605cm2sC+ClajNSHdHSGvfKGe8WZzE X-Received: by 2002:a05:600c:c173:b0:49c:fc6c:be18 with SMTP id 5b1f17b1804b1-49cfc6cc0b8mr145302615e9.30.1788700685064; Sun, 06 Sep 2026 06:18:05 -0700 (PDT) Received: from box.tail5a1d.ts.net ([2a02:3033:263:7c84:b668:57f6:6991:a647]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49ce5927b68sm327903075e9.1.2026.09.06.06.18.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 06 Sep 2026 06:18:04 -0700 (PDT) From: Christian Korneck To: passt-dev@passt.top Subject: [PATCH] pasta: Add --no-pidns to keep spawned command in caller's PID namespace Date: Sun, 6 Sep 2026 15:17:58 +0200 Message-ID: <20260906131758.121019-1-christian@korneck.de> X-Mailer: git-send-email 2.55.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: 2TXT34VEAXYPLIFJAK7CBG7XDCK6PHXK X-Message-ID-Hash: 2TXT34VEAXYPLIFJAK7CBG7XDCK6PHXK X-MailFrom: christian@korneck.de X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Christian Korneck X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: 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 --- 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