69

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.

90

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?)

  • 3
    Worked great thanks! github.com/balupton/dotfiles/commit/… – balupton Nov 6 '13 at 6: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
  • 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
  • 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 7 '17 at 21:02
21

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

  • 1
    Also add these to env_keep in sudoers to make it work across su commands:) – Thomas G. Mar 31 '16 at 17:00
10

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 ...

  • 1
    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
  • 1) 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
7

I think Gilles and Cakemox's answers are good, but just for completeness...

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

comes from pam_lastlog1.

You can print pam_lastlog information using the lastlog2 command, e.g.

$ lastlog -u mikel  
Username         Port     From             Latest
mikel            tty1                      Fri Jan 28 10:58:10 +1100 2011

for a local login, compared to

Username         Port     From             Latest
mikel            pts/9    mikel-laptop     Sat Mar 19 11:11:58 +1100 2011

for an SSH login.

On my system, this works to extract it

$ lastlog -u mikel | sed -ne '2{p;q}' | cut -c 27-42
mikel-laptop 

last and w could be helpful too, for example

$ TTY=$(tty)
$ last -n 1 ${TTY#/dev/} | sed -ne '1{p;q}'
mikel    pts/12       :0.0             Sat Mar 19 11:29   still logged in 


1 Linux/FreeBSD documentation for pam_lastlog.
2 Linux/FreeBSD lastlog(8) man pages.

1

Start by taking a look at your environment and finding the right option

printenv|grep SSH
SSH_CLIENT=192.168.1.xxx
SSH_CONNECTION=192.168.1.xxx
SSH_TTY=/dev/ttys021

You could hook into many of these environment variables to trigger specific actions based on their presence.

-1

This is to check all established connection from other user using SSH

netstat | grep ssh
  • This is not at all reliable. – DannyNiu May 14 at 8:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.