登录后如何限制SSH用户使用预定义的命令集?


85

这是一种安全性的想法。我们的员工应该可以访问linux服务器上的某些命令,但不是全部。他们应例如可以访问日志文件(less logfile)或启动其他命令(shutdown.sh/ run.sh)。

背景资料:

所有员工都使用相同的用户名访问服务器:我们的产品以“普通”用户权限运行,不需要“安装”。只需将其解压缩到您的用户目录中并运行它即可。我们管理“安装”应用程序的多台服务器。在每台机器上都有一个用户johndoe。我们的员工有时需要在命令行上访问该应用程序,以访问和检查日志文件或手动重新启动该应用程序。只有某些人具有完全命令行访问权限。

我们正在服务器上使用ppk身份验证。

如果employee1只能访问日志文件,而employee2也可以执行X等操作,那就太好了……

解决方案: 作为一种解决方案,我将使用command接受答案中所述的选项。我将制作自​​己的小Shell脚本,这将是某些员工可以执行的唯一文件。该脚本将提供几个可以执行的命令,但没有其他命令。我将使用下列参数authorized_keys从为表示在这里

command="/bin/myscript.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty
ssh-dss AAAAB3....o9M9qz4xqGCqGXoJw= user@host

这对我们来说已经足够安全了。谢谢,社区!


标准的Linux ACL基于权限的安全性还不够吗?您需要什么额外的功能?
詹姆斯·布雷迪

22
马塞尔,这是一个可怕的主意。
Vinko Vrsalovic

13
@ Vinko,@ PEZ:我添加了一些背景信息。不用说“愚蠢的想法”,您可以提供有价值的评论。您认为更好的主意是什么?
Marcel,

8
我仍然看不到让多个用户共享同一用户名的任何借口。
Eldelshell

3
“我自己的小shell脚本”?听起来很危险。除非您是专家,否则可能有很多方法可以使它摆脱困境。我宁愿相信一个编写良好,经过调试和维护的程序(已经提到了几个)。
bortzmeyer

Answers:


68

您还可以将键限制为允许的命令(在authorized_keys文件中)。

也就是说,用户不会通过ssh登录,然后只有一组受限制的命令,而只能被允许通过ssh执行这些命令(例如“ ssh somehost bin / showlogfile”)


看起来很有趣。可以定义多个命令吗?
Marcel

12
本文为您提供了使用Authorized_keys文件的多个命令的一些选项: linuxjournal.com/article/8257
Bash

@ rd834 ...:非常感谢。我认为这给了我一个“好的”解决方案...(增加了问题)。我将这个答案视为“正确”。
Marcel

11
O'Reilly SSH书对如何执行此操作进行了很好的解释,包括通过设置一个查看SSH_ORIGINAL_COMMAND环境变量的脚本来允许多个命令。 oreilly.com/catalog/sshtdg/chapter/ch08.html
Alex Dupuy

5
这个serverfault答案很好地提示了如何通过使用导出的SSH_ORIGINAL_COMMAND变量来允许多个命令。
MattBianco 2015年

32

sshrsh通过使用密码文件中的用户外壳程序执行命令来遵循传统。

这意味着我们无需ssh任何配置即可解决此问题。

如果您不希望用户具有外壳程序访问权限,则只需用脚本替换该用户的外壳程序即可。如果您看一下,/etc/passwd将会看到有一个为每个用户分配一个shell命令解释器的字段。该脚本既用作交互式登录的外壳ssh user@host ,也用作命令的外壳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>

我们的“外壳”接收-c样式调用,将整个命令作为一个参数,就像/bin/sh接收它的方式一样。

如您所见,我们现在可以做的是进一步开发脚本,以便它可以识别使用-c参数调用了脚本的情况,然后解析字符串(例如通过模式匹配)。可以通过递归调用将允许的那些字符串传递给真实的shell /bin/bash -c <string>。拒绝案例可以打印一条错误消息并终止(包括-c丢失的案例)。

您必须谨慎编写此代码。我建议只写肯定的比赛,只允许非常具体的事情,而不允许其他任何事情。

注意:如果是root,仍然可以通过覆盖su命令中的shell来登录此帐户,例如su -s /bin/bash foo。(选择替代shell。)非root用户无法执行此操作。

这是一个示例脚本:限制用户仅使用sshgit访问下的存储库/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-upload-packgit-receive-pack不要有破洞或逃生舱,这将使用户对系统的访问。

这是这种限制方案所固有的。用户已通过身份验证,可以在某个安全域中执行代码,而我们正在限制将该域限制为子域。例如,如果您允许用户vim在特定文件上运行命令以对其进行编辑,则该用户只需使用即可获得外壳程序:!sh[Enter]


6
说真的,这是非常危险的。什么会阻止我执行git-receive-pack '/git/';dd if=/dev/urandom of=/dev/sda
雪人

@Yeti我们这里有指令注入孔吗?这需要解决。另外,我对文件模式的滥用对我来说case似乎并不正确。
卡兹(Kaz)

1
是的,/bin/bash -c "$2"是不安全的(类似于SQL注入的工作方式)。您可以使用PHP之类的“魔术引号”过滤字符串。但是,绝对确保安全性的简单方法是手动调用命令,然后在双引号中传递参数。因为整个过程的安全性取决于该命令是最弱的链接(较难验证)。最有趣的是看您的答案有22个投票,但没人注意到这一点;)您会更新自己的答案吗?
雪人

1
@Yeti是的;我已经做了。我用替换了-c参数的脚本替换了脚本,set然后仅从脚本中获取前两个单词。第一个必须是允许的git-命令,第二个必须是规范化的路径,对照允许的前缀进行检查,并且还检查是否存在。您能想出任何办法打破这一点吗?
卡兹(Kaz),

1
除非有人为任何给定命令创建别名(或覆盖命令),否则不会,Bash双引号应该是安全的(如果不是,则这是Bash中的错误)。
Yeti

15

您要寻找的是受限制的外壳。Bash提供了这样一种模式,在该模式下,用户只能执行其主目录中存在的命令(并且他们不能移动到其他目录),这可能对您足够了。

我发现这个线程是非常说明性的,如果有点陈旧。


1
如果用户从较少的提示中执行“!/ bin / sh”或类似的操作怎么办?
PEZ

3
@Ubersoldat:请长大并淡化所有帖子中的侵略性。他在问限制是否也只适用于bash或子进程(要回答他的问题,事实并非如此)。

受限制的shell的一个主要问题是,为了保护它的安全,必须使用.profile或.bashrc来限制PATH,禁用内置程序等,但是这些文件仅用于交互式shell。如果用户使用ssh运行远程命令,则不会调用它们。这允许具有ssh访问权限的用户使用SHELL = / bin / rbash进行“ ssh remotehost bash”之类的操作,以获取非交互但不受限制的shell。您需要按照HD的建议使用SSH强制命令,但这可以防止PEZ要求进行外壳转义(一旦PATH被锁定-默认情况下包含/ bin:/ usr / bin)。
Alex Dupuy

2
我回想一下,当在sshd下非交互运行时,bash调用.bashrc(不是.profile)。但是,必须确保显式设置PATH并禁用.bashrc中的内置函数和别名-更改为不在PATH或.ssh /或.bashrc中的子目录也是一个好主意。使用任何可以写入任意文件的命令都会产生问题-这些问题并不总是很明显,例如sed'w'命令可用于覆盖.bashrc并中断。如果您可以使用chroot监狱,它将永远更加安全,并且ssh强制命令将受到更多限制。
Alex Dupuy

4

您应该获得`rssh',受限制的外壳

您可以按照上面提到的限制指南进行操作,它们非常容易解释,并且易于遵循。了解术语“ chroot jail”,以及如何有效地实现sshd / terminal配置,等等。

由于大多数用户都是通过sshd访问终端的,因此您可能还应该研究SSH守护程序配置文件sshd_conifg,以通过SSH应用某些限制。但是要小心。正确理解您要实现的目标,因为不正确的配置可能会很可怕。


1
请注意,对于要允许scp / sftp / rdist / rsync / cvs(并且不需要访问chroot监狱之外的任何东西)的特殊情况,pizzashack.org / rssh是一个很好的解决方案(可能是最好的)。 ,它不能解决原始发布者的一般问题,后者希望用户能够查看日志文件并运行某些运行/关闭脚本。
Alex Dupuy

2
rssh已从Debian Buster中删除。我认为新的热点是GNU高峰
托马斯

1
@Thomas我认为rssh现在已不再维护,并且存在一个已知的安全问题:sourceforge.net/p/rssh/mailman/message/36519118
Max Barraclough

4

您为什么不编写自己的登录外壳?为此,使用Bash非常简单,但是您可以使用任何语言。

Bash中的示例

使用您喜欢的编辑器来创建文件/root/rbash.sh(可以是任何名称或路径,但应为chown root:rootand chmod 700):

#!/bin/bash

commands=("man" "pwd" "ls" "whoami")
timestamp(){ date +'%Y-%m-%s %H:%M:%S'; }
log(){ echo -e "$(timestamp)\t$1\t$(whoami)\t$2" > /var/log/rbash.log; }
trycmd()
{
    # Provide an option to exit the shell
    if [[ "$ln" == "exit" ]] || [[ "$ln" == "q" ]]
    then
        exit
        
    # You can do exact string matching for some alias:
    elif [[ "$ln" == "help" ]]
    then
        echo "Type exit or q to quit."
        echo "Commands you can use:"
        echo "  help"
        echo "  echo"
        echo "${commands[@]}" | tr ' ' '\n' | awk '{print "  " $0}'
    
    # You can use custom regular expression matching:
    elif [[ "$ln" =~ ^echo\ .*$ ]]
    then
        ln="${ln:5}"
        echo "$ln" # Beware, these double quotes are important to prevent malicious injection
        
        # For example, optionally you can log this command
        log COMMAND "echo $ln"
    
    # Or you could even check an array of commands:
    else
        ok=false
        for cmd in "${commands[@]}"
        do
            if [[ "$cmd" == "$ln" ]]
            then
                ok=true
            fi
        done
        if $ok
        then
            $ln
        else
            log DENIED "$cmd"
        fi
    fi
}

# Optionally show a friendly welcome-message with instructions since it is a custom shell
echo "$(timestamp) Welcome, $(whoami). Type 'help' for information."

# Optionally log the login
log LOGIN "$@"

# Optionally log the logout
trap "trap=\"\";log LOGOUT;exit" EXIT

# Optionally check for '-c custom_command' arguments passed directly to shell
# Then you can also use ssh user@host custom_command, which will execute /root/rbash.sh
if [[ "$1" == "-c" ]]
then
    shift
    trycmd "$@"
else
    while echo -n "> " && read ln
    do
        trycmd "$ln"
    done
fi

您所要做的就是将此可执行文件设置为登录外壳程序。例如,编辑您的/etc/passwd文件,并替换用户当前的登录外壳/bin/bash/root/rbash.sh

这只是一个简单的示例,但是您可以根据需要将其设置为高级。请注意不要通过更改自己和唯一用户的登录shell来锁定自己。并始终测试奇怪的符号和命令以查看其是否真正安全。

您可以使用进行测试su -s /root/rbash.sh

注意,请确保匹配整个命令,并注意使用通配符!更好地排除猛砸符号,例如;&&&||$,和反引号以确保万无一失。

根据您给予用户的自由度,它不会比这更安全。我发现通常我只需要使一个用户只能访问一些相关命令,在那种情况下,这确实是更好的解决方案。但是,您是否希望给予更多自由,监禁和许可可能更合适。错误很容易犯,只有在为时已晚时才注意到。


我真的很喜欢这种方法。您是否知道如何处理“ service httpd restart”之类的多字命令?
Farzan

2
@Farzan您可以在变量中执行命令,例如ln="service httpd restart",使用:${ln[@]}。仍然要确保考虑安全性问题,在这种情况下,仅用于字母数字字符和空格的白名单将很有用。但是您也可以执行以下操作:if [[ "$ln" == "restart-httpd"];then service httpd restart;fi,并使用诸如此类的简单命令。
雪人

/root/rbash.sh: Permission denied 我也尝试了chmod 777
Christian

@Christian,您添加/root/rbash.sh到了/etc/shells吗?这取决于分布。您还可以尝试暂时禁用它selinux,并检查日志消息中的文件中的线索(查看时间戳)/var/log
雪人

@yeti不,有人解释说要添加到/ root :)现在尝试。
基督教徒


1

GNU Rush可能是实现此目的的最灵活,最安全的方法:

GNU Rush是受限制的用户外壳程序,设计用于提供对其资源(例如svn或git信息库,scp等)的远程访问有限的站点。使用复杂的配置文件,GNU Rush使您可以完全控制用户执行的命令行以及对系统资源的使用,例如虚拟内存,CPU时间等。


0

解决此问题的另一种方法是使用POSIX ACL,它需要文件系统的支持,但是可以像在Windows上具有相同控件的方式对linux中的所有命令进行细调(只是没有更好的UI) )。链接

要研究的另一件事是PolicyKit

您必须进行大量的搜索才能使所有功能正常运行,因为目前这绝对不是Linux的优势。


0

[披露:我写了sshdo,如下所述]

如果您希望登录是交互式的,那么设置受限制的外壳可能是正确的答案。但是,如果有一组实际要允许的命令(别无其他),并且可以通过ssh单独执行这些命令(例如ssh user @ host cmd arg blah blah),则可以使用通用的白名单控件ssh可能就是您所需要的。当命令在客户端以某种方式编写脚本并且不需要用户实际键入ssh命令时,这很有用。

有一个叫做sshdo的程序可以做到这一点。它控制可以通过传入的ssh连接执行哪些命令。可以从以下位置下载:

http://raf.org/sshdo/(在此处阅读手册页) https://github.com/raforg/sshdo/

它具有训练模式以允许尝试所有命令,并具有--learn选项以生成永久允许学习的命令所需的配置。然后可以关闭训练模式,并且不会执行任何其他命令。

它还具有--unlearn选项,以停止允许不再使用的命令,以便在需求随时间变化时保持严格的最小特权。

它允许什么是非常挑剔的。不允许带有任何参数的命令。仅允许使用完整的Shell命令。

但是它确实支持简单的模式来表示类似的命令,这些命令仅在命令行上显示的数字(例如,序列号或日期/时间戳)上有所不同。

就像ssh命令的防火墙或白名单控件一样。

并且它支持允许不同用户使用的不同命令。

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.