107

I want to detect from a shell script (more specifically .zshrc) if it is controlled through SSH. I tried the HOST variable but it's always the name of the computer which is running the shell. Can I access the hostname where the SSH session is coming from? Comparing the two would solve my problem.

Every time I log in there is a message stating the last login time and host:

Last login: Fri Mar 18 23:07:28 CET 2011 from max on pts/1
Last login: Fri Mar 18 23:11:56 2011 from max

This means the server has this information.

stribika
  • 5,454

9 Answers9

147

Here are the criteria I use in my ~/.profile:

  • If one of the variables SSH_CLIENT or SSH_TTY is defined, it's an ssh session.
  • If the login shell's parent process name is sshd, it's an ssh session.
if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then
  SESSION_TYPE=remote/ssh
# many other tests omitted
else
  case $(ps -o comm= -p "$PPID") in
    sshd|*/sshd) SESSION_TYPE=remote/ssh;;
  esac
fi

(Why would you want to test this in your shell configuration rather than your session startup?)

  • 4
    Worked great thanks! https://github.com/balupton/dotfiles/commit/70c6dae9d489810d27d3ee551548d5ca2badb286 – balupton Nov 06 '13 at 06:52
  • 1
    you might want to do this in your shell configuration if you want to enable ssh agent forwarding from your remote shell (since the environment vars need to be set in each shell you want to forward from) unless i'm missing something? – underrun Apr 14 '14 at 16:31
  • @underrun I don't understand your point. If you run another shell in the same session, it inherits the environment variables set by .profile. And what does this have to do with agent forwarding? – Gilles 'SO- stop being evil' Apr 14 '14 at 20:41
  • @Gilles if i want to us agent forwarding from a machine i'm ssh'd into already i will want to eval $(ssh-agent) but only if i've connected over ssh. i need both the ssh-agent process and the env vars right? – underrun Apr 15 '14 at 18:36
  • 1
    @underrun If you want to test for the presence of SSH agent forwarding, test for the SSH_AUTH_SOCK variable. But why would you run an SSH agent in that case? Did you mean start an agent if you're logged in without agent forwarding? Why not start an agent if there isn't already one ([ -n "$SSH_AUTH_SOCK" ] || eval $(ssh-agent))? – Gilles 'SO- stop being evil' Apr 15 '14 at 19:11
  • @Gilles - doh! yes. that is much better. that works whether i log in locally (and my window manager sets it up) or if i ssh in and i have to start my own agent. thanks! – underrun Apr 15 '14 at 21:26
  • Thanks for this. Could you add a brief explanation of why you recommend both tests? When should we expect SSH_CLIENT and SSH_TTY to be set but for the parent process to not be sshd and vice-versa? – Praxeolitic Mar 07 '17 at 20:53
  • 1
    @Praxeolitic The SSH_* variables are also set in subprocesses of a shell that's at the head of an SSH session, for example if you start a screen session over SSH (you should unset the variables before starting the session if you care). I think the reason for testing the parent process is that I started doing that before sshd defined any environment variables. – Gilles 'SO- stop being evil' Mar 07 '17 at 21:02
  • Actually, it would be great to see the "many other tests omitted"; would you be willing to share how you determine the session type in your profile? – Jonathan H Jun 12 '18 at 17:03
29

You should be able to check via the SSH_TTY, SSH_CONNECTION, or SSH_CLIENT variables.

Cakemox
  • 1,951
13

I just had the same problem in Linux, using Bash. I first used the environment variable SSH_CONNECTION, but then realized that it is not set if you su -.

The lastlog solution above didn't work either after su or su -.

Finally, I am using who am i, which shows the remote IP (or the hostname) at the end if it's an SSH connection. It also works after su.

Using Bash regular expressions, this works:

if [[ $(who am i) =~ \([-a-zA-Z0-9\.]+\)$ ]] ; then echo SSH; else echo no; fi

If zsh doesn't support regular expressions, the same can be achieved in many different ways with grep, cut, sed, or whatever.

For the curious, below is what I use this for, in root's .bashrc :

    # We don't allow root login over ssh.
    # To enable root X forwarding if we are logged in over SSH, 
    # use the .Xauthority file of the user who did su

    w=$(who am i)
    if [[ $w =~ \([-a-zA-Z0-9\.]+\)$ ]] ; then
        olduser=${w/ .*/}
        oldhome=$(getent passwd $olduser | cut -d: -f 6)
        [ -f "$oldhome/.Xauthority" ] \
          && export XAUTHORITY=$oldhome/.Xauthority
    fi

An alternative which also works with su would be to recursively search for sshd through the parent processes:

#!/bin/bash

function is_ssh() {
  p=${1:-$PPID}
  read pid name x ppid y < <( cat /proc/$p/stat )
  # or: read pid name ppid < <(ps -o pid= -o comm= -o ppid= -p $p) 
  [[ "$name" =~ sshd ]] && { echo "Is SSH : $pid $name"; return 0; }
  [ "$ppid" -le 1 ]     && { echo "Adam is $pid $name";  return 1; }
  is_ssh $ppid
}

is_ssh $PPID
exit $?

If the function is added to .bashrc, it can be used as if is_ssh; then ...

mivk
  • 3,596
  • 2
    doesn’t work in remote tmux sessions and also has issues if logged in via IPv6 and no DNS reverse name exists. – bene Feb 10 '13 at 16:58
  • @bene: what doesn't work? The regular expression, or does who am i not show your IPv6 address? – mivk Feb 10 '13 at 19:08
  • who am i doesn’t return anything in a remote tmux session. 2) IPv6 address might contain colons which your regex doesn’t allow. This might be tricky since who am i contains (:0.0) in X sessions for me (xterm).
  • – bene Feb 11 '13 at 15:05
  • @bene: The alternative solution which I just added should also work with IPv6. I don't know about tmux, but it does also work in screen. – mivk Dec 15 '13 at 14:54
  • 1
    I found it's easier to test with [[ $(pstree -s $$) = *sshd* ]] if the parent is a SSH connection. See here for details. – emk2203 Nov 29 '22 at 17:57