Repository: systemd/systemd
Pull request #37052: core,run0: fixlets/tweaks around $SHELL handling
Author: login: YHNdnzj
Labels: name: pid1
name: tests
name: run
Created: 2025-04-08T13:45:22Z | Merged: 2025-04-09T19:29:30Z

Fixes #35007

Linked issue #35007: run0 wrongly sets SHELL=/bin/bash environment variable for root
### systemd version the issue has been seen with

256

### Used distribution

Arch Linux

### Linux kernel version used

6.11.6-arch1-1

### CPU architectures issue was seen on

x86_64

### Component

systemd-run, run0

### Expected behaviour you didn't see

When running `run0` while having the root's shell set to `/usr/bin/zsh`, the `SHELL` environment variable should be set to that shell and not `/bin/bash`.

### Unexpected behaviour you saw

When running `run0` while having the root's shell set to `/usr/bin/zsh`, the `SHELL` environment variable is set to `/bin/bash`

### Steps to reproduce the problem

Set ZSH as root shell:
```bash
chsh -s /usr/bin/zsh
touch ~/.zshrc
```
In a new terminal, as root:
```
echo $SHELL
run0
echo $SHELL
echo $ZSH_VERSION
grep -oaP 'SHELL=[a-z/]+' /proc/$$/environ
```

### Additional program output to the terminal or log subsystem illustrating the issue

```bash
# echo $SHELL
/usr/bin/zsh
# run0
# echo $SHELL
/bin/bash
# echo $ZSH_VERSION
5.9
# grep -oaP 'SHELL=[a-z/]+' /proc/$$/environ
SHELL=/bin/bash
```

I had a look into the source code and I think it is related to the [call to `get_fixed_user`](https://github.com/systemd/systemd/blob/d980aee1e8e057b12fcbd20e8300248091d000d5/src/core/exec-invoke.c#L1862) which will end up setting the `SHELL` variable to `DEFAULT_USER_SHELL`, a build-time parameter that defaults to `/bin/bash`.

This could apparently be fixed by setting `User=root` in the transient unit created by run0 (not tested).

Linked issue comments: user: login: poettering
created_at: 2024-11-04T11:44:59Z
body: My educated guess is that this is fixed by e4b4d9cc7adf245950e8676be0e0f4a813069500

Any chance you can test that?
author_association: MEMBER
user: login: poettering
created_at: 2025-01-09T13:06:02Z
body: Let's close this. I am pretty sure this works now with v257. if not feel free to reopen.
author_association: MEMBER
user: login: Exagone313
created_at: 2025-01-09T13:19:48Z
body: I'm sorry I didn't follow up on your response.

I can still reproduce on v257 (still on Arch Linux build, package version 257.2-1). Same behavior with the steps provided.

They patch the source code before their build but it's only related to groups and doesn't have any impact on the present issue.

I will (?) see if I can reproduce on another OS, e.g. Debian testing which has 257 too.
author_association: NONE
user: login: eworm-de
created_at: 2025-04-08T10:15:14Z
body: > This could apparently be fixed by setting `User=root` in the transient unit created by run0 (not tested).

Like this?
```
% echo $SHELL
/bin/zsh
% run0 --property=User=root     
# echo $SHELL
/bin/bash
```

No, does not work.

Also note what `man run0` tells us:

> [...] If no command line is specified an interactive shell is invoked. The shell to invoke may be controlled via --setenv=SHELL=...  and currently defaults to the originating user's shell (i.e. not the target user's!) if operating locally, [...]

So the correct shell (the originating user's one) *is* invoked, but the `SHELL` variable is incorrectly. What ever shell the target user has configured is irrelevant here, no?
author_association: CONTRIBUTOR
user: login: eworm-de
created_at: 2025-04-08T10:17:56Z
body: BTW, my current workaround is calling `run0` with `--setenv=SHELL=$SHELL`. That works and brings the expected result, including the environment variable.
author_association: CONTRIBUTOR

Diff:
diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c
index 7e2cdd8c46cc1..9f2ce4d42b54f 100644
--- a/src/core/exec-invoke.c
+++ b/src/core/exec-invoke.c
@@ -854,6 +854,7 @@ static int ask_for_confirmation(const ExecContext *context, const ExecParameters
 
 static int get_fixed_user(
                 const char *user_or_uid,
+                bool prefer_nss,
                 const char **ret_username,
                 uid_t *ret_uid,
                 gid_t *ret_gid,
@@ -865,7 +866,8 @@ static int get_fixed_user(
         assert(user_or_uid);
         assert(ret_username);
 
-        r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell, USER_CREDS_CLEAN);
+        r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell,
+                           USER_CREDS_CLEAN|(prefer_nss ? USER_CREDS_PREFER_NSS : 0));
         if (r < 0)
                 return r;
 
@@ -1973,8 +1975,10 @@ static int build_environment(
          * could cause problem for e.g. getty, since login doesn't override $HOME, and $LOGNAME and $SHELL don't
          * really make much sense since we're not logged in. Hence we conditionalize the three based on
          * SetLoginEnvironment= switch. */
-        if (!c->user && !c->dynamic_user && p->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
-                r = get_fixed_user("root", &username, NULL, NULL, &home, &shell);
+        if (!username && !c->dynamic_user && p->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
+                assert(!c->user);
+
+                r = get_fixed_user("root", /* prefer_nss = */ false, &username, NULL, NULL, &home, &shell);
                 if (r < 0)
                         return log_exec_debug_errno(c,
                                                     p,
@@ -4904,7 +4908,14 @@ int exec_invoke(
                         u = NULL;
 
                 if (u) {
-                        r = get_fixed_user(u, &username, &uid, &gid, &pwent_home, &shell);
+                        /* We can't use nss unconditionally for root without risking deadlocks if some IPC services
+                         * will be started by pid1 and are ordered after us. But if SetLoginEnvironment= is
+                         * enabled *explicitly* (i.e. no exec_context_get_set_login_environment() here),
+                         * or PAM shall be invoked, let's consult NSS even for root, so that the user
+                         * gets accurate $SHELL in session(-like) contexts. */
+                        r = get_fixed_user(u,
+                                           /* prefer_nss = */ context->set_login_environment > 0 || context->pam_name,
+                                           &username, &uid, &gid, &pwent_home, &shell);
                         if (r < 0) {
                                 *exit_status = EXIT_USER;
                                 return log_exec_error_errno(context, params, r, "Failed to determine user credentials: %m");
diff --git a/src/run/run.c b/src/run/run.c
index e4ae5373b92e4..cc00f9304198c 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -1029,18 +1029,25 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
                 const char *e;
 
                 e = strv_env_get(arg_environment, "SHELL");
-                if (e)
+                if (e) {
                         arg_exec_path = strdup(e);
-                else {
+                        if (!arg_exec_path)
+                                return log_oom();
+                } else {
                         if (arg_transport == BUS_TRANSPORT_LOCAL) {
                                 r = get_shell(&arg_exec_path);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to determine shell: %m");
-                        } else
+                        } else {
                                 arg_exec_path = strdup("/bin/sh");
+                                if (!arg_exec_path)
+                                        return log_oom();
+                        }
+
+                        r = strv_env_assign(&arg_environment, "SHELL", arg_exec_path);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set $SHELL environment variable: %m");
                 }
-                if (!arg_exec_path)
-                        return log_oom();
 
                 l = make_login_shell_cmdline(arg_exec_path);
         }
diff --git a/test/units/TEST-74-AUX-UTILS.run.sh b/test/units/TEST-74-AUX-UTILS.run.sh
index f6f422a30572a..3229a522b7e43 100755
--- a/test/units/TEST-74-AUX-UTILS.run.sh
+++ b/test/units/TEST-74-AUX-UTILS.run.sh
@@ -248,13 +248,19 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
     run0 ls /
     assert_eq "$(run0 echo foo)" "foo"
     # Check if we set some expected environment variables
-    for arg in "" "--user=root" "--user=0" "--user=testuser"; do
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER"
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
+    for tu in "" "root" "0" "testuser"; do
+        assert_eq "$(run0 ${tu:+"--user=$tu"} bash -c 'echo $SUDO_USER')" "$USER"
+        assert_eq "$(run0 ${tu:+"--user=$tu"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
+        assert_eq "$(run0 ${tu:+"--user=$tu"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
 
         # Validate that we actually went properly through PAM (XDG_SESSION_TYPE is set by pam_systemd)
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
+        assert_eq "$(run0 ${tu:+"--user=$tu"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
+
+        if [[ -n "$tu" ]]; then
+            # Validate that $SHELL is set to login shell of target user when cmdline is supplied (not invoking shell)
+            TARGET_LOGIN_SHELL="$(getent passwd "$tu" | cut -d: -f7)"
+            assert_eq "$(run0 --user="$tu" printenv SHELL)" "$TARGET_LOGIN_SHELL"
+        fi
     done
     # Let's chain a couple of run0 calls together, for fun
     readarray -t cmdline < <(printf "%.0srun0\n" {0..31})


Reviews:
user: login: yuwata
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T13:50:38Z
user: login: YHNdnzj
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T13:52:04Z
user: login: yuwata
body: Please add a test case for the issue.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T17:46:03Z
user: login: yuwata
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T00:12:15Z
user: login: YHNdnzj
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T11:40:45Z
user: login: YHNdnzj
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T15:58:04Z
user: login: yuwata
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-09T17:09:15Z
user: login: yuwata
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T17:12:12Z
user: login: YHNdnzj
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T17:19:16Z
user: login: yuwata
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T17:27:53Z
user: login: yuwata
body: LGTM.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-09T17:41:05Z

Review comments:
diff_hunk: @@ -4904,7 +4906,13 @@ int exec_invoke(
                         u = NULL;
 
                 if (u) {
-                        r = get_fixed_user(u, &username, &uid, &gid, &pwent_home, &shell);
+                        /* We can't use nss unconditionally for root without risking deadlocks if some IPC services
+                         * will be started by pid1 and are ordered after us. But if SetLoginEnvironment= is
+                         * enabled explicitly, or PAM shall be invoked, let's consult NSS even for root,
path: src/core/exec-invoke.c
user: login: yuwata
body: Are there any implicit ordering dependency or so to avoid deadlocks when these settings specified??
created_at: 2025-04-08T13:50:38Z
author_association: MEMBER
diff_hunk: @@ -4904,7 +4906,13 @@ int exec_invoke(
                         u = NULL;
 
                 if (u) {
-                        r = get_fixed_user(u, &username, &uid, &gid, &pwent_home, &shell);
+                        /* We can't use nss unconditionally for root without risking deadlocks if some IPC services
+                         * will be started by pid1 and are ordered after us. But if SetLoginEnvironment= is
+                         * enabled explicitly, or PAM shall be invoked, let's consult NSS even for root,
path: src/core/exec-invoke.c
user: login: YHNdnzj
body: Nope, the idea is that PAM will be doing its own nss anyways. It doesn't matter if we query that in this case
created_at: 2025-04-08T13:52:04Z
author_association: MEMBER
diff_hunk: @@ -248,13 +248,17 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
     run0 ls /
     assert_eq "$(run0 echo foo)" "foo"
     # Check if we set some expected environment variables
-    for arg in "" "--user=root" "--user=0" "--user=testuser"; do
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER"
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
+    for arg in "" "root" "0" "testuser"; do
path: test/units/TEST-74-AUX-UTILS.run.sh
user: login: yuwata
body: Not a matter, but `arg` -> `user` ?
created_at: 2025-04-09T00:11:58Z
author_association: MEMBER
diff_hunk: @@ -248,13 +248,17 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
     run0 ls /
     assert_eq "$(run0 echo foo)" "foo"
     # Check if we set some expected environment variables
-    for arg in "" "--user=root" "--user=0" "--user=testuser"; do
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER"
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
+    for arg in "" "root" "0" "testuser"; do
+        assert_eq "$(run0 ${arg:+"--user=$arg"} bash -c 'echo $SUDO_USER')" "$USER"
+        assert_eq "$(run0 ${arg:+"--user=$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
+        assert_eq "$(run0 ${arg:+"--user=$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
 
         # Validate that we actually went properly through PAM (XDG_SESSION_TYPE is set by pam_systemd)
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
+        assert_eq "$(run0 ${arg:+"--user=$arg"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
+
+        # Validate that $SHELL is set to login shell of target user when cmdline is supplied (not invoking shell)
+        TARGET_LOGIN_SHELL="$(getent passwd "$arg" | cut -d: -f7)"
+        assert_eq "$(run0 ${arg:+"--user=$arg"} printenv SHELL)" "$TARGET_LOGIN_SHELL"
path: test/units/TEST-74-AUX-UTILS.run.sh
user: login: yuwata
body: maybe better to add test case with --machine=, to test the first commit ?
created_at: 2025-04-09T00:12:02Z
author_association: MEMBER
diff_hunk: @@ -248,13 +248,17 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
     run0 ls /
     assert_eq "$(run0 echo foo)" "foo"
     # Check if we set some expected environment variables
-    for arg in "" "--user=root" "--user=0" "--user=testuser"; do
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER"
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
+    for arg in "" "root" "0" "testuser"; do
+        assert_eq "$(run0 ${arg:+"--user=$arg"} bash -c 'echo $SUDO_USER')" "$USER"
+        assert_eq "$(run0 ${arg:+"--user=$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
+        assert_eq "$(run0 ${arg:+"--user=$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
 
         # Validate that we actually went properly through PAM (XDG_SESSION_TYPE is set by pam_systemd)
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
+        assert_eq "$(run0 ${arg:+"--user=$arg"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
+
+        # Validate that $SHELL is set to login shell of target user when cmdline is supplied (not invoking shell)
+        TARGET_LOGIN_SHELL="$(getent passwd "$arg" | cut -d: -f7)"
+        assert_eq "$(run0 ${arg:+"--user=$arg"} printenv SHELL)" "$TARGET_LOGIN_SHELL"
path: test/units/TEST-74-AUX-UTILS.run.sh
user: login: YHNdnzj
body: We actually can't. To test that run0 must be called without cmdline, which spawns an interactive shell...
created_at: 2025-04-09T11:40:45Z
author_association: MEMBER
diff_hunk: @@ -248,13 +248,17 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
     run0 ls /
     assert_eq "$(run0 echo foo)" "foo"
     # Check if we set some expected environment variables
-    for arg in "" "--user=root" "--user=0" "--user=testuser"; do
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER"
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
+    for arg in "" "root" "0" "testuser"; do
+        assert_eq "$(run0 ${arg:+"--user=$arg"} bash -c 'echo $SUDO_USER')" "$USER"
+        assert_eq "$(run0 ${arg:+"--user=$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
+        assert_eq "$(run0 ${arg:+"--user=$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
 
         # Validate that we actually went properly through PAM (XDG_SESSION_TYPE is set by pam_systemd)
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
+        assert_eq "$(run0 ${arg:+"--user=$arg"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
+
+        # Validate that $SHELL is set to login shell of target user when cmdline is supplied (not invoking shell)
+        TARGET_LOGIN_SHELL="$(getent passwd "$arg" | cut -d: -f7)"
+        assert_eq "$(run0 ${arg:+"--user=$arg"} printenv SHELL)" "$TARGET_LOGIN_SHELL"
path: test/units/TEST-74-AUX-UTILS.run.sh
user: login: YHNdnzj
body: An appropriate test case will be added in #37071 
created_at: 2025-04-09T15:58:03Z
author_association: MEMBER
diff_hunk: @@ -4906,7 +4908,13 @@ int exec_invoke(
                         u = NULL;
 
                 if (u) {
-                        r = get_fixed_user(u, &username, &uid, &gid, &pwent_home, &shell);
+                        /* We can't use nss unconditionally for root without risking deadlocks if some IPC services
+                         * will be started by pid1 and are ordered after us. But if SetLoginEnvironment= is
path: src/core/exec-invoke.c
user: login: yuwata
body: Hm, I noticed that SetLoginEnvironment= is enabled by default when User= or DynamicUser= is enabled.
So, many processes may be affected with the change, like resolved, networkd, timesyncd, and so on. Is it really safe?
If an NSS requires networking,,, what happens?
created_at: 2025-04-09T17:12:08Z
author_association: MEMBER
diff_hunk: @@ -4906,7 +4908,13 @@ int exec_invoke(
                         u = NULL;
 
                 if (u) {
-                        r = get_fixed_user(u, &username, &uid, &gid, &pwent_home, &shell);
+                        /* We can't use nss unconditionally for root without risking deadlocks if some IPC services
+                         * will be started by pid1 and are ordered after us. But if SetLoginEnvironment= is
path: src/core/exec-invoke.c
user: login: YHNdnzj
body: It's effectively enabled in the sense of exec_context_get_set_login_environment. The field itself is tristate.
created_at: 2025-04-09T17:19:16Z
author_association: MEMBER
diff_hunk: @@ -4906,7 +4908,13 @@ int exec_invoke(
                         u = NULL;
 
                 if (u) {
-                        r = get_fixed_user(u, &username, &uid, &gid, &pwent_home, &shell);
+                        /* We can't use nss unconditionally for root without risking deadlocks if some IPC services
+                         * will be started by pid1 and are ordered after us. But if SetLoginEnvironment= is
path: src/core/exec-invoke.c
user: login: yuwata
body: Ah, "enabled _explicitly_".
Could you extend the comment to mention that? Otherwise, we might mistakenly replace the condition with `exec_context_get_set_login_environment()` in a future.
created_at: 2025-04-09T17:27:53Z
author_association: MEMBER

PR comments:
user: login: eworm-de
created_at: 2025-04-08T14:38:46Z
body: Does not look like this fixes the issue...
author_association: CONTRIBUTOR
user: login: YHNdnzj
created_at: 2025-04-08T17:29:10Z
body: > Does not look like this fixes the issue...

Hmm, can you give it another go?
author_association: MEMBER
user: login: eworm-de
created_at: 2025-04-08T19:52:46Z
body: ~Yes, this one look a lot better now. 👍 Thanks a lot! ❤️~
author_association: CONTRIBUTOR
user: login: eworm-de
created_at: 2025-04-08T20:15:58Z
body: Oh, wait, no...

User `nepu` and `root` have set their shell to `/bin/zsh`:

```
% getent passwd nepu
nepu:x:1000:1000:Nepu User:/home/nepu:/bin/zsh
% ps         
    PID TTY          TIME CMD
   4660 pts/0    00:00:01 zsh
   5668 pts/0    00:00:00 ps
% echo $SHELL
/bin/zsh
% run0
# getent passwd root
root:x:0:0:root:/root:/bin/zsh
# ps
    PID TTY          TIME CMD
   5698 pts/1    00:00:00 zsh
   5720 pts/1    00:00:00 (sd-pam)
   5761 pts/1    00:00:00 ps
# echo $SHELL
/bin/zsh
```
This looks like expected now.

But let's configure `/bin/bash` for `root` now:

```
% getent passwd nepu
nepu:x:1000:1000:Nepu User:/home/nepu:/bin/zsh
% ps
    PID TTY          TIME CMD
   4660 pts/0    00:00:01 zsh
   5845 pts/0    00:00:00 ps
% echo $SHELL
/bin/zsh
% run0
# getent passwd root
root:x:0:0:root:/root:/bin/bash
# ps
    PID TTY          TIME CMD
   5881 pts/1    00:00:00 zsh
   5902 pts/1    00:00:00 (sd-pam)
   5954 pts/1    00:00:00 ps
# echo $SHELL
/bin/bash
```

Judging from documentation the shell from originating user is used:

> [...] If no command line is specified an interactive shell is invoked. The shell to invoke may be controlled via --setenv=SHELL=...  and currently defaults to the originating user's shell (i.e. not the target user's!) if operating locally, [...]

So the shell from `nepu` should be used, which is `/bin/zsh`. It is used, but why is `SHELL` set to `/bin/bash` then? Why is `nss` involved at all? The used process and the content of the variable should be taken from user, not `nss`?
author_association: CONTRIBUTOR
user: login: YHNdnzj
created_at: 2025-04-08T20:42:59Z
body: > Judging from documentation the shell from originating user is used

Ugh, I wasn't aware of this behavior at all. Thought it was the other way around...
author_association: MEMBER
user: login: eworm-de
created_at: 2025-04-09T08:20:00Z
body: Do you want to fix CI before I start another round of testing?
author_association: CONTRIBUTOR
user: login: eworm-de
created_at: 2025-04-09T11:44:53Z
body: > Do you want to fix CI before I start another round of testing?

Did anyway (with fe76ed7802be3f5982378d734987c5d5b0a52cd5)... Works for my use cases. 👍 Thanks! ❤️
author_association: CONTRIBUTOR
user: login: poettering
created_at: 2025-04-10T14:30:10Z
body: lgtm
author_association: MEMBER