使用远程命令进行 SSH 并保持会话活动

使用远程命令进行 SSH 并保持会话活动

我想在设置一些环境变量的同时通过 ssh 连接到远程机器。

这么说吧,我不想在远程机器上做任何更改。

我想做类似的事情:

ssh remote-server "NAME=$NAME;"

上述命令存在以下问题:

  1. 我想让会话保持活动状态。但是命令退出后它就会关闭。

    我可以:

    ssh -t remote-server "NAME=$NAME; bash"
    

    但在随后的会话中,NAME是未定义的。

  2. 我也想/etc/motd打印。

    我还可以做:

    ssh -t remote-server "cat /etc/motd;NAME=$NAME; bash"
    

    它可以起作用(第一个问题仍然存在),但如果有更优雅的解决方案,请告诉我们。

答案1

总结: ssh -t remote-server "NAME=$NAME bash";之前没有bash),但请考虑其他方法。

在使用本答案中的任何内容之前,请先查看安全隐患部分。

当您运行ssh并向其提供特定命令时,它会使用配置为远程用户登录 shell 的任何 shell 来运行该命令。因此,效果取决于该 shell 是什么,但最流行的 shell 是 Bourne 风格的 shell。这包括sh、、、和bash其他各种 shell。它不包括和,它们可能是最流行的非 Bourne 风格的 shell zshkshtcshfish

您的目标是使用新的或更改的环境变量在远程计算机上运行bash,​​并且本地计算机上的 shell(您正在运行命令的地方ssh)可能是bash,但远程用户的登录 shell 不一定是bash。该 shell 列在远程计算机的密码数据库中;它包含在通过在远程计算机上运行检索到的记录中,其中getent passwd useruser是远程用户名。

在 Bourne 风格的 shell 中,环境变量是 shell 变量,但并非所有的 shell 变量都是环境变量。当你运行以下形式的程序时NAME=value完整命令,有三种情况:

  • 如果NAME已经是一个环境变量,它也是一个 shell 变量,并且分配给该 shell 变量也会更新环境变量。
  • 如果NAME它不是环境变量,但它作为 shell 变量存在,则分配给该 shell 变量会更新其值,但不会导致它成为环境变量。
  • 如果NAME未设置(即没有该名称的变量),则分配给它会创建具有指定值的 shell 变量,但这个 shell 变量不是环境变量。

要使 shell 变量成为所有后续命令的环境变量,您需要出口它与export内置的:

export NAME

内置export函数还支持赋值语法,用于为变量赋予新值(或其初始值)并同时导出它:

export NAME=value

因此,您有一个选择:

ssh -t remote-server "export NAME=$NAME; bash"

这与您的代码的不同之处在于添加了export

但 Bourne 风格的 shell 也支持设置具有特定值的环境变量仅在单个命令的持续时间内。这就是命令开头的赋值所起的作用。因此,你可以改用这个:

ssh -t remote-server "NAME=$NAME bash"

这与您的代码的不同之处在于删除了;导致分配被视为其自己的命令的。

为了覆盖非 Bourne 风格的登录 shell,您可以使用 shell 期望的任何语法。试图为了使用相同的语法覆盖尽可能多的 shell,您可以使用命令env来设置变量并运行该命令。即使运行它的 shell 不env支持语法。NAME=value

ssh -t remote-server "env NAME=$NAME bash"

假设你的 MOTD 是通常情况下显示,让它显示的最佳方式(以及实现您可能想要的其他效果)是告诉bash它充当登录 shell。一种方法是将标志传递--loginbash

ssh -t remote-server "export NAME=$NAME; bash --login"
ssh -t remote-server "NAME=$NAME bash --login"
ssh -t remote-server "env NAME=$NAME bash --login"

但是如果您愿意,您可以cat在运行命令之前保存文件,就像您之前所做的那样。

安全隐患

正如您所知(并希望如此),您自己的 shell$NAME在运行 之前会进行扩展ssh。如果它包含由偏僻的shell,你有问题。这意味着它在某些常见的简单情况下无法工作,例如如果该值包含空格。这也意味着你可能会意外地使用具有意想不到的效果的值。推论是,只有始终控制该变量的内容,这才是安全的。如果你自己设置了值,那没问题。如果该值可能由其他人设置,则该人可能会导致他们想要的任何命令在远程机器上运行。

你可以尝试通过改写来控制这种情况NAME='$NAME'。这在你故意做一些简单的事情时很有用,比如当$NAME可能包含空格时。但它并不涵盖所有情况,而且就安全而言,不幸的是,它根本没有提供任何缓解措施。变量可能包含该'字符。(如果您不知道哪个 shell 配置为远程用户的登录 shell,还存在非 Bourne 风格 shell 具有不同引用规则的问题。)

x="$y"请注意,这不会影响在 shell 中运行代码的情况,其中xy是变量,并且您想要将的值分配给yx这可行且安全。但这不是您在这里所做的。相反,您将的值(NAME无论它是什么)粘贴到远程计算机运行的代码中。当您无法完全控制该值时,通常没有安全的方法来执行此操作。

如果你控制价值,并且你知道自己在做什么,那么就可以了。否则,一种方法是尝试将其值修改为完全且安全引用的内容,并将其插入到在远程计算机上运行的命令中。这很难做到正确,但您可以编写代码将每个'字符替换为序列。可以使用命令的格式说明'\''符尝试比此处更复杂的引用情况。%qprintf

更好的办法是完全避免这种情况,并使用单独的机制将变量传递到远程系统。除了上面建议的任何命令外,您还应该考虑:

ssh -o SendEnv=NAME

但是,这不能满足您避免修改存储在远程计算机上的文件的要求。必须配置远程服务器以允许环境变量NAME传递。该方法以及其他类似的方法都PermitUserEnvironment需要/etc/sshd_config在远程机器上进行编辑。但如果你的目标只是避免以嵌入的方式编辑远程机器上的文件任何特定值NAME的任何文件中的环境变量,那么您应该使用SendEnv

相关内容