9

I see an unexplained difference in behavior with ssh on Linux and FreeBSD, and it is the FreeBSD behavior that I would expect from the plain language of the manpages. So, something extra happens on Linux, but I don't know what it is.

I use bash as my normal login shell on all systems, both Linux and BSD. With a complex multi-stage setup involving the bash startup files, I set up my environment variables for all incarnations of bash that are either "login" or "interactive" shell, in the technical sense spelled out in the beginning of the bash manpage.

Now, the difference: when I ssh into a FreeBSD server with a command, for example ssh freebsd-host printenv , those variables are not set. Which, after some thinking, is what I should expect, since the shell in that case is neither login nor interactive. But, when I ssh in the same way into a Linux host, the variables are in fact set -- a sure sign that either the .bash_profile or the .bashrc file has been grokked somehow or other, despite the non-status of the shell.

My question is of course, why and how can Linux behave this way? What it is that makes the bash startup be run under these circumstances? All the sshd configuration is exactly the same and close to the upstream defaults, including PermitUserEnvironment=no. All the Linuxes are variants on Debian, if it matters.

A bonus anecdote: I stumbled into this when I read some manpages with ssh -t host man man and I noticed nothing unusual on a remote Linux, but on a remote FreeBSD the manpage came out ungodly narrow -- because my ubiquitous setting of MANWIDTH=100 wasn't there.

1

1 Answer 1

15

The difference is that Debian compiles Bash with the SSH_SOURCE_BASHRC feature enabled, while FreeBSD apparently doesn't.

Bash versions with this feature enabled will additionally source ~/.bashrc (and the system bashrc) even if they're non-login and non-interactive, as long as they find the $SSH_CLIENT variable set by sshd (and detect that they're not already running under a parent shell which would've done so).

shell.c-1132-  /* get the rshd/sshd case out of the way first. */
shell.c-1133-  if (interactive_shell == 0 && no_rc == 0 && login_shell == 0 &&
shell.c-1134-      act_like_sh == 0 && command_execution_string)
shell.c-1135-    {
shell.c-1136-#ifdef SSH_SOURCE_BASHRC
shell.c:1137:      run_by_ssh = (find_variable ("SSH_CLIENT") != (SHELL_VAR *)0) ||
shell.c-1138-              (find_variable ("SSH2_CLIENT") != (SHELL_VAR *)0);
shell.c-1139-#else
shell.c:1140:      run_by_ssh = 0;
shell.c-1141-#endif
shell.c-1142-#endif
shell.c-1143-
shell.c-1144-      /* If we were run by sshd or we think we were run by rshd, execute
shell.c-1145-    ~/.bashrc if we are a top-level shell. */
shell.c-1146-#if 1      /* TAG:bash-5.3 */
shell.c:1147:      if ((run_by_ssh || isnetconn (fileno (stdin))) && shell_level < 2)
shell.c-1148-#else
shell.c-1149-      if (isnetconn (fileno (stdin) && shell_level < 2)
shell.c-1150-#endif
shell.c-1151-   {
shell.c-1152-#ifdef SYS_BASHRC
shell.c-1153-#  if defined (__OPENNT)
shell.c-1154-     maybe_execute_file (_prefixInstallPath(SYS_BASHRC, NULL, 0), 1);
shell.c-1155-#  else
shell.c-1156-     maybe_execute_file (SYS_BASHRC, 1);
shell.c-1157-#  endif
shell.c-1158-#endif
shell.c-1159-     maybe_execute_file (bashrc_file, 1);
shell.c-1160-     return;
shell.c-1161-   }
5
  • Could you explain what this means? Not all of our users are comfortable reading C code.
    – terdon
    Commented Nov 28, 2024 at 10:38
  • 2
    Oh wow, I had no idea bash did something like this. To me, it definitely rises to the level of over-engineering. And it is not documented. Thanks for the info!
    – q.undertow
    Commented Nov 28, 2024 at 17:08
  • 3
    @q.undertow I think man bash in Debian does document this. man -P cat bash | grep -B 5 -A 5 ssh Commented Nov 29, 2024 at 7:10
  • 2
    @q.undertow Bash's startup/configuration file behaviour always baffles me. Other shells (notably zsh) seem much saner in that regard.
    – marcelm
    Commented Nov 29, 2024 at 13:03
  • @terdon It checks if this is an interactive shell, then if SSH_SOURCE_BASHRC is defined when compiled, it checks if the Variables SSH_CLIENT or SSH2_CLIENT are set. That would be true if ssh was used to invoke bash. If either of those were set, and this is the top level shell, i.e. not a child process, then check for bashrc and execute it.
    – OldTimer
    Commented Dec 7, 2024 at 9:31

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.