SSH – 即使没有 Shell 也强制在登录时执行命令

SSH – Force Command execution on login even without Shell

我正在创建一个没有 shell 的受限用户,仅用于端口转发,我需要在通过 pubkey 登录时执行脚本,即使用户是通过 ssh -N user@host 连接的,但不询问shell.

的 SSH 服务器

该脚本应在使用 pubkey 进行身份验证的连接时警告管理员,因此连接的用户不应跳过脚本的执行(例如,通过使用 ssh -N 连接)。

我试了没用:

当用户要求 shell 时一切正常,但如果仅记录端口转发且没有 shell (ssh -N),则它不起作用。

ForceCommand 选项在没有 PTY 的情况下运行,除非客户端请求一个。因此,您实际上并没有 shell 来按照您期望的方式执行脚本。此外,OpenSSH SSHD_CONFIG(5) 手册页明确指出:

The command is invoked by using the user's login shell with the -c option.

这意味着如果您禁用了用户的登录 shell,或将其设置为类似 /bin/false 的内容,则 ForceCommand 将无法工作。假设:

  1. 用户有一个明智的shell定义,
  2. 您的目标脚本是可执行的,并且
  3. 您的脚本有合适的 shebang 行

然后以下内容应该在您的 global sshd_config 文件中工作,一旦使用正确的用户名和自定义脚本的完全限定路径名进行了正确修改:

Match User foo
    ForceCommand /path/to/script.sh

我是OP的作者;我得出的结论是,到现在为止(OpenSSH_6.9p1 Ubuntu-2, OpenSSL 1.0.2d 9 Jul 2015),我需要使用 SSH 是不可能实现的,但是我发现了一个很棒的软件,它使用加密的 SPAuthentication 打开 SSH 端口,它是新版本(到这个 post 的日期,它是 GitHub master 分支)有一个功能,总是执行用户授权成功的命令。

FWKNOP - 加密单数据包授权

FWKNOP 设置 iptables 规则,允许在通过 UDP 发送的单个加密数据包上访问给定端口。然后在授权之后,它允许授权用户在给定时间内访问,例如 30 秒,在此之后关闭端口,保持连接打开。

1。要在 Ubuntu linux 上安装:

迄今为止 Ubuntu 存储库上的当前版本 (2.6.0-2.1build1) 仍然不允许在成功的 SPA 上执行命令; (请改用 GitHub 中的 2.6.8)

在客户端机器上:

sudo apt-get install fwknop-client

在服务器端:

sudo apt-get install fwknop-server

这是一个关于如何设置客户端和服务器机器的教程 https://help.ubuntu.com/community/SinglePacketAuthorization

Then,设置完成后,服务器端:

  1. 编辑/etc/default/fwknop-server
  2. 将行 START_DAEMON="no" 更改为 START_DAEMON="yes"
  3. 然后运行:

    sudo service fwknop-server stop

    sudo service fwknop-server start

2。警告管理员成功的 SPA(电子邮件、pushover 脚本等)

因此,如上所述,Ubuntu 存储库 (2.6.0-2.1build1) 中存在的当前版本无法在成功的 SPA 上执行命令。如果您从 OP 开始需要此功能,但它将在 fwknop 版本(2.6.8)中发布,如此处所述:

https://github.com/mrash/fwknop/issues/172

因此,如果您现在需要使用它,您可以从具有 CMD_CYCLE_OPEN 选项的 github 分支 master 构建。

3。有关 fwknop

的更多资源

https://help.ubuntu.com/community/SinglePacketAuthorization

https://github.com/mrash/fwknop/(GitHub 上的项目)

http://www.cipherdyne.org/fwknop/(项目站点)

https://www.digitalocean.com/community/tutorials/how-to-use-fwknop-to-enable-single-packet-authentication-on-ubuntu-12-04(DO 社区教程)

我是 OP 的作者。此外,您可以实现一个简单的 logwatcher,如下所示 python3,它会不断读取文件并在行包含模式时执行命令。

logwatcher.python3

#!/usr/bin/env python3

# follow.py
#
# Follow a file like tail -f.

import sys
import os
import time

def follow(thefile):
    thefile.seek(0,2)
    while True:
        line = thefile.readline()
        if not line:
            time.sleep(0.5)
            continue
        yield line

if __name__ == '__main__':
    logfilename = sys.argv[1]
    pattern_string = sys.argv[2]
    command_to_execute = sys.argv[3]

    print("Log filename is: {}".format(logfilename))

    logfile = open(logfilename, "r")
    loglines = follow(logfile)

    for line in loglines:
        if pattern_string in line:
            os.system(command_to_execute)

用法

  1. 使上述脚本可执行:

chmod +x logwatcher.python3

  1. 添加一个 cronjob 以在重启后启动它

crontab -e

然后把这一行写在那里并保存:

@reboot /home/YOURUSERNAME/logwatcher.python3 "/var/log/auth.log" "session opened for user" "/sbin/myscript.sh"

此脚本的第一个参数是要监视的日志文件第二个参数是要在中查找的字符串它。 第三个参数是在文件中找到该行时要执行的脚本

最好使用更可靠的东西 start/restart 脚本以防它崩溃。

如果你只需要运行一个脚本你可以依赖pam_exec

基本上你在/etc/pam.d/sshd配置中引用你需要运行的脚本:

session optional pam_exec.so seteuid /path/to/script.sh

经过一些测试后,您可能希望将 optional 更改为 required

请参阅此回答“bash - How do I set up an email alert when a ssh login is successful? - Ask Ubuntu”以获取类似请求。

实际上在脚本中只有有限的环境变量子集可用:

LANGUAGE=en_US.UTF-8
PAM_USER=bitnami
PAM_RHOST=192.168.1.17
PAM_TYPE=open_session
PAM_SERVICE=sshd
PAM_TTY=ssh
LANG=en_US.UTF-8
LC_ALL=en_US.UTF-8
PWD=/

如果您想从 authorized_keys 获取用户信息,此脚本可能会有所帮助:

#!/bin/bash
# Get user from authorized_keys
# pam_exec_login.sh
# * [ssh - What is the SHA256 that comes on the sshd entry in auth.log? - Server Fault](https://serverfault.com/questions/888281/what-is-the-sha256-that-comes-on-the-sshd-entry-in-auth-log)
# * [bash - How to get all fingerprints for .ssh/authorized_keys(2) file - Server Fault](https://serverfault.com/questions/413231/how-to-get-all-fingerprints-for-ssh-authorized-keys2-file)

# Setup log
b=$(basename [=12=]| cut -d. -f1)
log="/tmp/${b}.log"

function timeStamp () {
  echo "$(date '+%b %d %H:%M:%S') ${HOSTNAME} $b[$$]:"
}

# Check if opening a remote session with sshd
if [ "${PAM_TYPE}" != "open_session" ] || [ $PAM_SERVICE != "sshd" ] || [ $PAM_RHOST == "::1" ]; then
  exit $PAM_SUCCESS
fi

# Get info from auth.log
authLogLine=$(journalctl -u ssh.service |tail -100 |grep "sshd\[${PPID}\]" |grep "${PAM_RHOST}")
echo ${authLogLine} >> ${log}

PAM_USER_PORT=$(echo ${authLogLine}| sed -r 's/.*port (.*) ssh2.*//')
PAM_USER_SHA256=$(echo ${authLogLine}| sed -r 's/.*SHA256:(.*)//')

# Get details from .ssh/authorized_keys
authFile="/home/${PAM_USER}/.ssh/authorized_keys"
PAM_USER_authorized_keys=""

while read l; do
  if [[ -n "$l" && "${l###}" = "$l" ]]; then
    authFileSHA256=$(ssh-keygen -l -f <(echo "$l"))
    if [[ "${authFileSHA256}" == *"${PAM_USER_SHA256}"* ]]; then
      PAM_USER_authorized_keys=$(echo ${authFileSHA256}| cut -d" " -f3)
      break
    fi
  fi
done < ${authFile}

if [[ -n ${PAM_USER_authorized_keys} ]]
then
  echo "$(timeStamp) Local user: ${PAM_USER}, authorized_keys user: ${PAM_USER_authorized_keys}" >> ${log}
else
  echo "$(timeStamp) WARNING: no matching user in authorized_keys" >> ${log}
fi