如何检测外壳是否由SSH控制?


69

我想从shell脚本(更具体地说是.zshrc)中检测它是否通过SSH控制。我尝试了HOST变量,但它始终是运行Shell的计算机的名称。我可以访问SSH会话来自的主机名吗?比较两者将解决我的问题。

每次我登录时,都会显示一条消息,说明上次登录时间和主机:

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

这意味着服务器具有此信息。

Answers:


90

这是我在我中使用的标准~/.profile

  • 如果定义了变量SSH_CLIENT或之一,SSH_TTY则为ssh会话。
  • 如果登录外壳程序的父进程名称为sshd,则它是一个ssh会话。
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

(为什么要在外壳配置中而不是会话启动时对其进行测试?)



1
如果要启用从远程Shell进行ssh代理转发(因为需要在要从其转发的每个shell中设置环境变量),则可能需要在Shell配置中执行此操作,除非我遗漏了什么?
2014年

@underrun我不明白你的意思。如果您在同一会话中运行另一个Shell,它将继承由设置的环境变量.profile。这与代理转发有什么关系?
吉尔斯2014年

1
@underrun如果要测试是否存在SSH代理转发,请测试该SSH_AUTH_SOCK变量。但是,为什么要在这种情况下运行SSH代理?如果您没有代理转发就登录,是不是要启动代理?如果还没有代理([ -n "$SSH_AUTH_SOCK" ] || eval $(ssh-agent)),为什么不启动代理?
吉尔斯2014年

1
@Praxeolitic SSH_*还在SSH会话开头的shell的子进程中设置变量,例如,如果通过SSH启动屏幕会话(如果需要,应在启动会话之前取消设置变量)。我认为测试父进程的原因是我在sshd定义任何环境变量之前就开始这样做。
吉尔斯

21

您应该能够通过检查SSH_TTYSSH_CONNECTIONSSH_CLIENT变量。


1
还要在其中添加这些内容env_keepsudoers以使其可跨su命令使用:)
Thomas G.

10

我只是在Linux中使用Bash遇到了同样的问题。我首先使用了环境变量SSH_CONNECTION,但随后意识到,如果您未设置它,则不会设置su -

上面的lastlog解决方案在su或之后均无效su -

最后,我正在使用who am i,如果是SSH连接,它会在末尾显示远程IP(或主机名)。su之后也可以使用。

使用Bash正则表达式,可以工作:

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

如果zsh不支持正则表达式,则可以使用grep,cut,sed或其他任何方式以相同的方式实现相同的目的。

出于好奇,下面是我在root的.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

另一种可行的替代方法su是递归搜索sshd父流程:

#!/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 $?

如果将该函数添加到.bashrc,则可以将其用作 if is_ssh; then ...


1
在远程tmux会话中不起作用,如果通过IPv6登录并且不存在DNS反向名称,也会出现问题。
bene 2013年

@bene:什么不起作用?正则表达式还是不who am i显示您的IPv6地址?
mivk

1)who am i在远程tmux会话中不返回任何内容。2)IPv6地址可能包含您的正则表达式不允许的冒号。这可能很棘手,因为对我而言(xterm)who am i包含(:0.0)在X个会话中。
bene 2013年

@bene:我刚刚添加的替代解决方案也应该适用于IPv6。我不了解tmux,但它也可以在中使用screen
mivk 2013年

7

我认为Gilles和Cakemox的答案很好,但只是为了完整性...

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

来自pam_lastlog1

您可以pam_lastlog使用lastlog2命令打印信息,例如

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

与本地登录相比

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

进行SSH登录。

在我的系统上,这可以提取出来

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

last例如,w也可能会有所帮助

$ 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文档pam_lastlog
2 Linux / FreeBSD lastlog(8)手册页。


1

首先查看您的环境并找到正确的选项

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

您可以加入许多这些环境变量,以根据它们的存在来触发特定操作。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.