Mailing List Archive

Provide a way to source shell-specific startup files for noninteractive commands
(searched through the list archives and elsewhere and didn't find any prior relevant discussions.)

I'm one of Pyenv project maintainers. Just now, we've had a critical bug (https://github.com/pyenv/pyenv/issues/2367) that was happening
with some shell setups under SSH with Bash compiled with SSH_SOURCE_BASHRC which is enabled in some Linux distributions (notably, Debian-based).
(Basically, a recently-added nested `bash -c` call was misbehaving, only under those specific circumstances.)

Now, the behavior of that compilation option is rather questionable and I'm not defending it.

However, the distro maintainers' decision to enable it looks like a workaround for the fact that sshd does not provide a way to run the
standard shell startup files when running noninteractive commands. This makes `ssh user@host <command>` rather useless because the <command>
would run in an incomplete -- and most importantly, different -- environment than a usual shell command, leading to all sorts of surprising
breakage and counterintuitive behavior.

Sshd does provide `~/.ssh/rc` as a replacement -- but it's run with `sh` and separately from the main shell -- which disallows using
shell-specific functionality like installing shell functions. (That is important. E.g. Pyenv requires a shell function installed to use some
subcommands.)
Having to prepend every <command> with `source ~/.bash_profile;` (that some suggest at e.g. StackOverflow) is horrible UX.
In this light, SSH_SOURCE_BASHRC does begin to look like a lesser evil...

I see two ways to fix this underlying problem and hopefully eliminate the need for workarounds like that.

1) Run noninteractive shells as login shells just like interactive ones (e.g. `bash -lc` instead of `bash -c`).
This seems to be the most logical and straightforward way. In fact, I don't quite see why there had to be a difference in the first place.
(My guess is that this was a compatibility measure because at the time, existing shell startup files weren't written with noninteractive
login shells in mind, e.g. printing stuff like motd unconditionally.)

2) In addition to `~/.ssh/rc`, source `~/.ssh/<shell>rc`, and do that in the main shell. This would allow distro maintainers to provide a
stock file as part of a shell installation that could call standard startup files or anything else as needed.

--
Regards,
Ivan

_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Provide a way to source shell-specific startup files for noninteractive commands [ In reply to ]
On Sun, 22 May 2022, Ivan Pozdeev wrote:

> 2) In addition to `~/.ssh/rc`, source `~/.ssh/<shell>rc`, and do that in the

As shell maintainer, I’d be very cross if this happened.

The shell manpages clearly document in which circumstances
their various startup files are sourced. If you need to
have them sourced, fulfill the respective requirements;
usually, this means use the ssh(1) -t option.

bye,
//mirabilos
--
15:41?<Lo-lan-do:#fusionforge> Somebody write a testsuite for helloworld :-)
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Provide a way to source shell-specific startup files for noninteractive commands [ In reply to ]
On Sun, 22 May 2022, Thorsten Glaser wrote:

> On Sun, 22 May 2022, Ivan Pozdeev wrote:
>
> > 2) In addition to `~/.ssh/rc`, source `~/.ssh/<shell>rc`, and do that in the

Erm, my quoted context was off.

Anyway the point stands.

Goodnight,
//mirabilos
--
[17:15:07] Lukas Degener: Kleines Asterix-Latinum für Softwaretechniker:
veni, vidi, fixi(t) ;-)
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Provide a way to source shell-specific startup files for noninteractive commands [ In reply to ]
On 22.05.2022 2:00, Thorsten Glaser wrote:
> On Sun, 22 May 2022, Ivan Pozdeev wrote:
>
>> 2) In addition to `~/.ssh/rc`, source `~/.ssh/<shell>rc`, and do that in the
> As shell maintainer, I’d be very cross if this happened.
>
> The shell manpages clearly document in which circumstances
> their various startup files are sourced. If you need to
> have them sourced, fulfill the respective requirements;
> usually, this means use the ssh(1) -t option.

From what I can see, there's no way to pass options to the shell that would run the command.
As per https://github.com/openssh/openssh-portable/blob/bedb93415b60db3dfd704a3d525e82adb14a2481/session.c#L1709-L1713 ,
it's always run as `<shell> -c <command>` which makes it non-login and non-interactive regardless of whether a terminal is allocated.

> bye,
> //mirabilos

--
Regards,
Ivan

_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Provide a way to source shell-specific startup files for noninteractive commands [ In reply to ]
On 22.05.22 00:52, Ivan Pozdeev wrote:
> However, the distro maintainers' decision to enable it looks like a
> workaround for the fact that sshd does not provide a way to run the
> standard shell startup files when running noninteractive commands. This
> makes `ssh user@host <command>` rather useless because the <command>
> would run in an incomplete -- and most importantly, different --
> environment than a usual shell command, leading to all sorts of
> surprising breakage and counterintuitive behavior.

And simultaneously makes it a useful simulation of having said command
executed by cron (where the shell is a non-interactive one, the
environment different, stdin "not a tty", yadda yadda, too) for debugging.

> I see two ways to fix this underlying problem and hopefully eliminate
> the need for workarounds like that.
>
> 1) Run noninteractive shells as login shells just like interactive ones
> (e.g. `bash -lc` instead of `bash -c`).

Let's see, what does this server here do all day in terms of SSH ... ?

> # grep Accepted /var/log/secure | awk '{ print $9; }' | sort | uniq -c
> 403 accounting
> 21813 nagios
> 3 root

I wonder what /var/log/wtmp and the output of "last" would look like if
all the Nagios checks were to be forced to use an interactive shell?

On 22.05.22 01:00, Thorsten Glaser wrote:
> usually, this means use the ssh(1) -t option.

Hmm, are you sure about that?

From (my) bash manpage:

> INVOCATION
> A login shell is one whose first character of argument zero is
> a -, or one started with the --login option.
>
> An interactive shell is one started without non-option arguments
> and without the -c option whose standard input and error are both
> connected to terminals (as determined by isatty(3)), or one
> started with the -i option. PS1 is set and $- includes i if bash
> is interactive
[...]
> When bash is invoked as an interactive login shell, or as a
> non-interactive shell with the --login option, it first [...]
> After [...], it looks for ~/.bash_profile, ~/.bash_login, and
> ~/.profile, in that order, and reads and executes commands from
> the first one that exists and is readable.
[...]
> When an interactive shell that is not a login shell is started,
> bash reads and executes commands from ~/.bashrc, if that file exists.

Let's try that ... :

> $ ssh SameServer 'echo $-'
> hBc
> $ ssh -t SameServer 'echo $-'
> hBc

However, note that with bash and forwarded env vars, at least, one could
work around SSH not providing a means to manually set --login or -i
options for the remote shell:

> When bash is started non-interactively, to run a shell script, for
> example, it looks for the variable BASH_ENV in the environment,
> expands its value if it appears there, and uses the expanded value
> as the name of a file to read and execute.
Regards,
--
Jochen Bern
Systemingenieur

Binect GmbH
Re: Provide a way to source shell-specific startup files for noninteractive commands [ In reply to ]
On 23.05.2022 12:52, Jochen Bern wrote:
> On 22.05.22 00:52, Ivan Pozdeev wrote:
>> However, the distro maintainers' decision to enable it looks like a workaround for the fact that sshd does not provide a way to run the
>> standard shell startup files when running noninteractive commands. This makes `ssh user@host <command>` rather useless because the
>> <command> would run in an incomplete -- and most importantly, different -- environment than a usual shell command, leading to all sorts
>> of surprising breakage and counterintuitive behavior.
>
> And simultaneously makes it a useful simulation of having said command executed by cron (where the shell is a non-interactive one, the
> environment different, stdin "not a tty", yadda yadda, too) for debugging.
>

True. It's however not SSH's job to be a kludge due to a lack of debugging facilities in Cron (and a bad one, too, since its environment is
still a bit different).

>> I see two ways to fix this underlying problem and hopefully eliminate the need for workarounds like that.
>>
>> 1) Run noninteractive shells as login shells just like interactive ones (e.g. `bash -lc` instead of `bash -c`).
>
> Let's see, what does this server here do all day in terms of SSH ... ?
>
>> # grep Accepted /var/log/secure | awk '{ print $9; }' | sort | uniq -c
>>     403 accounting
>>   21813 nagios
>>       3 root
>
> I wonder what /var/log/wtmp and the output of "last" would look like if all the Nagios checks were to be forced to use an interactive shell?
>

We're talking about a login shell, not an interactive shell here. From what I can see in the sshd(8) manpage, whether a login journal entry
is created rather depends on whether a terminal is being allocated. For a noninteractive login shell, there's no need to (as a reminder, a
login shell is a shell run with `-l/--login` or with `-` at the start of argv[0] -- nothing to do with terminals).

sshd(8):

> LOGIN PROCESS
>      When a user successfully logs in, sshd does the following:
>            1.   If the login is on a tty, and no command has been specified, prints last login time and /etc/motd (unless prevented in the
>                 configuration file or by ~/.hushlogin; see the FILES section).
>            2.   If the login is on a tty, records login time.
>            3.   Checks /etc/nologin; if it exists, prints contents and quits (unless root).
>            4.   Changes to run with normal user privileges.
>            5.   Sets up basic environment.
>            6.   Reads the file ~/.ssh/environment, if it exists, and users are allowed to change their environment.  See the
>                 PermitUserEnvironment option in sshd_config(5).
>            7.   Changes to user's home directory.
>            8.   If ~/.ssh/rc exists and the sshd_config(5) PermitUserRC option is set, runs it; else if /etc/ssh/sshrc exists, runs it;
>                 otherwise runs xauth(1).  The “rc” files are given the X11 authentication protocol and cookie in standard input.  See
>                 SSHRC, below.
>            9.   Runs user's shell or command.  All commands are run under the user's login shell as specified in the system password data?
>                 base.
>


> On 22.05.22 01:00, Thorsten Glaser wrote:
>> usually, this means use the ssh(1) -t option.
>
> Hmm, are you sure about that?
>
> From (my) bash manpage:
>
>> INVOCATION
>>        A login shell is one whose first character of argument zero is
>>        a -, or one started with the --login option.
>>
>>        An  interactive shell is one started without non-option arguments
>>        and without the -c option whose standard input and error are both
>>        connected to terminals (as determined by isatty(3)), or one
>>        started with the -i option. PS1 is set and $- includes i if bash
>>        is interactive
> [...]
>>        When  bash  is  invoked as an interactive login shell, or as a
>>        non-interactive shell with the --login option, it first [...]
>>        After [...], it looks for ~/.bash_profile, ~/.bash_login, and
>>        ~/.profile, in that order, and reads and executes commands from
>>        the first one that exists and is readable.
> [...]
>>        When  an  interactive  shell that is not a login shell is started,
>>        bash reads and executes commands from ~/.bashrc, if that file exists.
>
> Let's try that ... :
>
>> $ ssh SameServer 'echo $-'
>> hBc
>> $ ssh -t SameServer 'echo $-'
>> hBc
>
> However, note that with bash and forwarded env vars, at least, one could work around SSH not providing a means to manually set --login or
> -i options for the remote shell:
>
>>        When bash is started non-interactively, to run a shell script, for
>>        example, it looks for the variable BASH_ENV in the environment,
>>        expands its value if it appears there, and uses the expanded value
>>        as the name of a file to read and execute.
> Regards,
>
> _______________________________________________
> openssh-unix-dev mailing list
> openssh-unix-dev@mindrot.org
> https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev

--
Regards,
Ivan

_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev