ssh遵循rsh传统,使用密码文件中的用户 shell 程序来执行命令。
这意味着我们可以在不涉及ssh任何配置的情况下解决这个问题。
如果您不希望用户能够拥有 shell 访问权限,那么只需将该用户的 shell 替换为脚本即可。如果您查看,/etc/passwd您会看到有一个字段为每个用户分配一个 shell 命令解释器。该脚本用作交互式登录ssh user@host 和命令的 shell ssh user@host command arg ...。
这是一个例子。我创建了一个用户foo,其外壳是一个脚本。该脚本打印消息my arguments are:后跟它的参数(每个在单独的行和尖括号中)并终止。在日志中,没有参数。这是发生的事情:
webserver:~# ssh foo@localhost
foo@localhost's password:
Linux webserver [ snip ]
[ snip ]
my arguments are:
Connection to localhost closed.
如果用户尝试运行命令,它看起来像这样:
webserver:~# ssh foo@localhost cat /etc/passwd
foo@localhost's password:
my arguments are:
<-c>
<cat /etc/passwd>
我们的“shell”接收一个-c样式调用,将整个命令作为一个参数,就像/bin/sh接收它的方式一样。
如您所见,我们现在可以做的是进一步开发脚本,以便它在使用-c参数调用它时识别大小写,然后解析字符串(例如通过模式匹配)。那些允许的字符串可以通过递归调用传递给真实的shell /bin/bash -c <string>。拒绝案例可以打印错误消息并终止(包括-c丢失时的案例)。
你必须小心你如何写这个。我建议只写正面匹配,只允许非常具体的事情,而不允许其他一切。
注意:如果您是,您仍然可以通过在命令root中覆盖 shell 来登录此帐户,如下所示。(选择的替代外壳。)非根不能这样做。susu -s /bin/bash foo
这是一个示例脚本:将用户限制为仅ssh用于git访问/git.
#!/bin/sh
if [ $# -ne 2 ] || [ "$1" != "-c" ] ; then
printf "interactive login not permitted\n"
exit 1
fi
set -- $2
if [ $# != 2 ] ; then
printf "wrong number of arguments\n"
exit 1
fi
case "$1" in
( git-upload-pack | git-receive-pack )
;; # continue execution
( * )
printf "command not allowed\n"
exit 1
;;
esac
# Canonicalize the path name: we don't want escape out of
# git via ../ path components.
gitpath=$(readlink -f "$2") # GNU Coreutils specific
case "$gitpath" in
( /git/* )
;; # continue execution
( * )
printf "access denied outside of /git\n"
exit 1
;;
esac
if ! [ -e "$gitpath" ] ; then
printf "that git repo doesn't exist\n"
exit 1
fi
"$1" "$gitpath"
当然,我们相信这些 Git 程序没有漏洞或逃生口,不会让用户访问系统git-upload-pack。git-receive-pack
这是这种限制方案所固有的。用户经过身份验证可以在某个安全域中执行代码,并且我们正在限制将该域限制为子域。例如,如果您允许用户vim在特定文件上运行命令来编辑它,则用户只需获得一个带有:!sh[Enter].