有人投票关闭此帖,理由是“不清楚你在问什么”。显然,太过彻底并不是一件好事,所以我就简洁一点吧:
当您通过 SSH 连接到远程主机时:是否可以直接执行程序,而不是在用户的默认 shell 下执行。
- 网络是隔离的
- 网络上的每个人都可以访问每台机器的 shell
- IT 可以进行任何必要的更改,只要这些更改不会增加某人获取 root shell 的风险(超出现有可能性)。
- 作为类似事情可能实现的证明:
sftp
ssh“子系统”能够在不运行用户默认 shell 的情况下执行,而子系统scp
仍然使用它。- 我尝试实现一个子系统,但它仍然在用户的默认 shell 下执行。
我认为这个问题没有好的解决方案(除非说服大量非 *nix 人士放弃他们多年来一直依赖的拐杖),但我想我会提出这个问题,希望我错了。
以下是不太简洁的问题。
在我的办公室里,有很多人(3-500 多人)都在使用各种奇怪而怪异的配置。很难预测我们编写的软件对特定用户的行为方式,因为他们的环境可能完全违背我们的预期。
为了解决这个问题,我改变了我们的构建方式,剥离了用户的环境,这样它就不会影响构建,并且基于该策略的成功,我们进一步将软件的执行包装在执行相同操作的 shell 脚本中。
不幸的是,启动器仍然会受到用户环境的影响。当远程执行启动器时,用户.cshrc
通常.login
会有一堆垃圾,导致启动器在开始执行之前出现长时间的延迟。
通常我会倾向于说“修复你的垃圾”,但我们不谈论*nix-savvy 人;这是他们多年来依赖的拐杖,他们更倾向于继续将他们的配置问题归咎于我们的软件。
简而言之,我正在寻找一种方法来完全短路他们的愚蠢的环境脚本。
为了模拟这种情况,我将默认 shell 设置为 tcsh 并输入hostname; sleep 10
我的.cshrc
,然后运行了一些测试:
- sftp 不打印/休眠 — — 不过,在这种情况下,目标机器是 solaris,其中 sftp 内置于 sshd,而且我没有想到要针对 Linux 机器进行测试。
ptree
针对服务于 sftp 连接的进程树运行显示它不在 shell 下运行。 - scp 打印文本并休眠 10 秒
- 通过 ssh 在远程主机上执行命令的每种变体都会导致命令在用户的默认 shell 下运行;我试过:
- 显而易见--通过 ssh 传递命令
command=
在.ssh/authorized_keys
- 我在我的个人 Linux 中创建了一个自定义“子系统”
/etc/ssh/sshd_config
- 在我的计算机上启用
PermitUserEnvironment
以尝试设置 SHELL- 根本不起作用:
SHELL=/bin/bash ssh -o "SendEnv SHELL" localhost
- 有点用:使用
environment=
或~/.ssh/enviornment
设置 SHELL- 在设置了 SHELL 的情况下,它确实可以工作,但它实际上是在我的真实默认 shell 下执行的
- 根本不起作用:
我设法用来netcat
通过现有的 ssh 会话转发 shell,但这看起来像是用大锤来打鸡蛋一样——既混乱又极不合适。
我们组织中的一个人建议创建另一个用户,无论通过什么方式,每个人都可以通过 ssh 以该用户身份登录,然后::多云气泡解决方案和大量挥手::让程序以原始用户身份运行。
我有点怀疑这样做是否可行。这可能不像使用 netcat 转发 shell 那么糟糕,或者像临时重命名它们那样笨拙而脆弱.cshrc
,但我仍然不喜欢这样做。
欢迎任何建议。
答案1
应该可以使用子系统实现您想要的功能,但我相当肯定它需要编写自定义 sshd 二进制文件,因为 OpenSSH 代码仅提供 sftp 子系统,并且仅为其他定义设置了一个可扩展的框架。
答案2
至少使用 Bash 时,带有“-t”参数的 ssh(例如ssh server1 -t '/foo/bar/script.sh'
)将运行远程命令而无需执行“~/.bashrc”(bash 相当于“~/.cshrc/login”)。
答案3
/etc/ssh/sshd_config.d
使用以下内容创建自定义配置文件:
Match User yourUserName
SetEnv SHELL=/usr/bin/bash # Or whatever other shell you're comfortable with
答案4
主意
以下解决方案并不像您希望的那样优雅,但它确实有效。
在服务器上,对于每个用作某些用户的默认 shell 的 shell 以及每个可能用作默认 shell 的 shell(即 中的每个 shell)
/etc/shells
,创建一个包装器。每个包装器都应该用一种默认不处理愚蠢文件的语言编写。
每个包装器都应该这样做:
检查是否
SSH_OVERRIDE_SHELL
为非空变量。如果
SSH_OVERRIDE_SHELL
非空,包装器应执行到不处理愚蠢文件的干净 shell。在 POSIX shell 中,这可能看起来像这样:exec env -u SSH_OVERRIDE_SHELL /path/to/real/bash --norc --noprofile "$@"
请注意,最终 real
bash
将替换包装器,因为env
它本身会执行(至少在我的 Debian 中是这样的)。因此,如果包装器被调用,sshd
那么最终它将就像bash
被调用一样sshd
。这bash
将接收包装器获得的所有参数,加上--norc
和--noprofile
。如果
SSH_OVERRIDE_SHELL
未设置或为空,包装器应执行用户的真实默认 shell。在 POSIX shell 中,这可能看起来像这样:exec env -u SSH_OVERRIDE_SHELL /path/to/real/relevant_shell "$@"
我曾经
env
取消设置SSH_OVERRIDE_SHELL
,但您可以使用包装器语言提供的任何内容来预先修改环境。然后您需要允许 SSH 客户端推送到
SSH_OVERRIDE_SHELL
服务器。您需要AcceptEnv SSH_OVERRIDE_SHELL
在sshd_config
服务器上。请记住,对于每个关键字(如AcceptEnv
),将使用第一个获得的值,因此如果一行AcceptEnv
已经存在,那么您需要添加SSH_OVERRIDE_SHELL
到它而不是添加单独的行。重新加载 SSH 服务器。服务器上默认 shell 实际上是我们的包装器之一的客户端现在可以
bash
像这样远程运行一些 shell 代码:# client-side SSH_OVERRIDE_SHELL=whatever ssh -o SendEnv=SSH_OVERRIDE_SHELL user@server 'shell code here'
任何不向服务器
ssh user@server
发送非空值的用法都将像以前一样工作。同样,在环境中没有非空值的情况下在服务器上使用包装器的任何用法都将像包装器是它所替换的真实外壳一样工作。SSH_OVERRIDE_SHELL
SSH_OVERRIDE_SHELL
潜在问题
用包装器替换/bin/bash
等/usr/bin/zsh
并将真实 shell 移至其他地方可能会干扰将来升级服务器。apt-get upgrade
覆盖这些文件的升级(如 )将覆盖包装器。我update-alternatives
对 Debian 了解不够多,无法判断它是否可以解决这个问题(至少在 Debian 及其衍生产品中)。一般来说,请考虑采用略有不同的方法。
略有不同的方法
稍有不同的方法是将所有 shell 保留在它们应该在的位置,并将包装器放在专用目录(或目录树)中。 中的每个条目/etc/shells
都应更新并指向相应的包装器。 对于您想要为其实施此解决方案的每个用户,默认 shell(如 中的/etc/passwd
)都应更新并指向相应的包装器。 此后,受影响的用户将使用包装器,并且(至少没有sudo
访问权限的普通用户)将只能选择一个包装器作为他们的登录 shell(使用chsh
)。
请注意,此方法允许您(或者更确切地说是 IT 部门)出于任何原因让某些用户不受影响。我认为,尤其是 root 应该独自一人,但至少也要让一个 sudoer 独自一人。如果包装器出现问题,您希望至少有一名管理员能够登录。
受影响的用户可能会问为什么他们的登录 shell 和输出正式echo "$SHELL"
发生了变化;但实际上他们不应该感受到任何差异。请注意,每个包装器可能会“修复”变量SHELL
并使其看起来好像包装器不存在一样。
概念验证
这是我在我的服务器上所做的(以 root 身份):
# prepare directory structure
mkdir -p /shell_wrappers/bin
mkdir -p /shell_wrappers/usr/bin
# prepare symlinks
ln -s /shell_wrappers/wrapper /shell_wrappers/bin/bash
ln -s /shell_wrappers/wrapper /shell_wrappers/usr/bin/zsh
# create an universal wrapper
>/shell_wrappers/wrapper cat <<'EOF'
#!/bin/sh
SHELL="${SHELL#/shell_wrappers}"
if [ -n "$SSH_OVERRIDE_SHELL" ]; then
unset SSH_OVERRIDE_SHELL
exec /bin/bash --norc --noprofile "$@"
else
unset SSH_OVERRIDE_SHELL
exec "$SHELL" "$@"
fi
EOF
# make the wrapper executable
chmod +x /shell_wrappers/wrapper
包装器是通用的。要包装路径为 的 shell /bar/baz/shell
,您需要一个位于 的符号链接/shell_wrappers/bar/baz/shell
。该符号链接必须指向/shell_wrappers/wrapper
。
接下来我修改/etc/ssh/sshd_config
并添加了SSH_OVERRIDE_SHELL
。AcceptEnv
由于已经存在其他标记,因此在我的情况下,结果行是:
AcceptEnv LANG LC_* SSH_OVERRIDE_SHELL
我保存了文件并重新加载了 SSH 服务器服务。
然后我习惯vipw
修改我的普通用户条目。我将其更改/bin/bash
为/shell_wrappers/bin/bash
。在我的一些测试中,我使用了/shell_wrappers/usr/bin/zsh
。
是的,这有效。我故意echo foo
在服务器上的启动脚本中注入了类似命令。本地命令如ssh kamil@server :
did print foo
;但是这个:
SSH_OVERRIDE_SHELL=1 ssh -o SendEnv=SSH_OVERRIDE_SHELL kamil@server :
做过不是。
然后使用/shell_wrappers/usr/bin/zsh
我在服务器上的登录 shell,我在本地执行以下操作:
ssh kamil@server 'ls -l /proc/$$/exe;:'
并且它显示了外壳是zsh
(打印后foo
);但是这是:
SSH_OVERRIDE_SHELL=1 ssh -o SendEnv=SSH_OVERRIDE_SHELL kamil@server 'ls -l /proc/$$/exe;:'
显示bash
(不打印)。包装器foo
就是用和运行的。bash
--norc
--noprofile
另外,我尝试scp
使用 SCP 而不是 SFTP 来访问文件;请参阅这个答案). 嘈杂的启动脚本(即foo
正在打印的脚本)打破它. 我们的解决方案有效:
SSH_OVERRIDE_SHELL=1 scp -o SendEnv=SSH_OVERRIDE_SHELL kamil@server:file1 ./
可能的改进
在通用包装器中,我曾经
unset
取消设置SSH_OVERRIDE_SHELL
。如前所述,您可以使用exec env …
它。env -i
如果这是您想要的,它允许您使用空环境。exec /bin/bash --norc --noprofile "$@"
在你到达某个所需位置之前cd
。或者你可以运行一些附加代码(例如logger "SSH_OVERRIDE_SHELL used. $SSH_CONNECTION"
)。你掌控一切。