在 ssh 中不分配终端有什么好处?

Cha*_*ens 78 ssh tty

每隔一段时间我就会做一些类似的事情

ssh user@host sudo thing
Run Code Online (Sandbox Code Playgroud)

我还记得 ssh 默认不分配伪 tty。为什么不呢?如果我别名ssh为 ,我会失去什么好处ssh -t

And*_*w B 83

主要区别在于交互性的概念。这类似于在脚本中本地运行命令,而不是自己键入命令。不同之处在于远程命令必须选择默认值,而非交互式是最安全的。(通常是最诚实的)

标准输入

  • 如果分配了 PTY,应用程序可以检测到这一点,并知道在不破坏事物的情况下提示用户进行额外输入是安全的。如果没有终端,有很多程序会跳过提示用户输入的步骤,这是一件好事。否则会导致脚本不必要地挂起。
  • 您的输入将在命令期间发送到远程服务器。这包括控制序列。虽然Ctrl-c中断通常会导致 ssh 命令的循环立即中断,但您的控制序列将被发送到远程服务器。这导致需要“敲击”击键以确保它在控制权离开ssh 命令时到达,但在下一个 ssh 命令开始之前。

我会警告不要ssh -t在无人参与的脚本中使用,例如 cron。要求远程命令以交互方式输入的非交互式 shell 会带来各种麻烦。

您还可以测试您自己的 shell 脚本中是否存在终端。要使用较新版本的 bash 测试 STDIN:

# fd 0 is STDIN
[ -t 0 ]; echo $?
Run Code Online (Sandbox Code Playgroud)

标准输出

  • 当别名ssh为 时ssh -t,您可以期望在行尾获得额外的回车符。你可能看不到它,但它就在那里;它将显示为通过^M管道传输到cat -e. 然后,您必须付出额外的努力来确保不会将此控制代码分配给您的变量,尤其是在您要将输出插入数据库时​​。
  • 还有一个风险是程序会假设它们可以呈现对文件重定向不友好的输出。通常,如果您要将 STDOUT 重定向到文件,程序会识别出您的 STDOUT 不是终端并省略任何颜色代码。如果 STDOUT 重定向来自ssh 客户端的输出,并且有一个与客户端的远程端相关联的 PTY,则远程程序无法进行这样的区分,最终输出文件中将出现终端垃圾。将输出重定向到连接远程端的文件仍应按预期工作。

这是与之前相同的 bash 测试,但针对 STDOUT:

# fd 1 is STDOUT
[ -t 1 ]; echo $?
Run Code Online (Sandbox Code Playgroud)

虽然可以解决这些问题,但您不可避免地会忘记围绕它们设计脚本。我们所有人都会在某个时候这样做。您的团队成员也可能没有意识到/记得这个别名已经到位,这反过来会在他们编写使用您的别名的脚本时给您带来问题。

Aliasing sshtossh -t是一种违反最小意外设计原则的情况;人们会遇到他们意想不到的问题,并且可能不明白是什么导致了这些问题。

  • 人们几乎给人的印象是,我曾在一个已经完成此工作的团队中工作过…… (12认同)

Ant*_*gan 37

SSH 转义/控制字符和二进制文件的传输

还没有在其他的答案被提及一个优点是,工作时没有一个伪终端,所述SSH转义字符~C不支持; 这使得程序可以安全地传输可能包含这些和其他控制字符的二进制文件。

如果没有伪终端,字符将“按原样”传输到远程主机:

$ printf "one\ntwo\n~Cthree\n" | ssh user@host tee 1
one
two
~Cthree
Run Code Online (Sandbox Code Playgroud)

如果您尝试强制分配伪终端,则包含~C会导致ssh>打印提示以允许用户输入 SSH 命令,从而中断传输。

$ printf "one\ntwo\n~Cthree\n" | ssh -tt user@host tee 2

ssh>
one
two
three
one
two
three
Run Code Online (Sandbox Code Playgroud)

~.序列更糟糕的是,因为它导致正在传输没有数据:

$ printf "one\ntwo\n~.three\n" | ssh -tt user@host tee 2
Connection to host closed.
Run Code Online (Sandbox Code Playgroud)

软件流程控制

当分配伪终端时,软件流控制字符(XON/XOFF)也可能被特殊处理。

$ printf "one\ntwo\n^Sthree\n" | ssh user@host tee 1
one
two
three

$ ssh user@host cat -vet 1
one$
two$
^Sthree$
Run Code Online (Sandbox Code Playgroud)

使用伪终端,该Ctrl-S字符被解释为暂停输入流的信号,因此在遇到该字符后不再发送数据(除非Ctrl-Q在输入流中后面跟着一个字符)。

在这种情况下,强制分配伪终端会导致远程端的文件为空。

$ printf "one\ntwo\n^Sthree\n" | ssh -tt user@host tee 2
one
two
Run Code Online (Sandbox Code Playgroud)

在这种情况下,强制分配伪终端会导致远程端的文件包含所有三行但没有控制字符:

$ printf "one\ntwo\n^Sthree^Q\n" | ssh -tt user@host tee 2
one
two
three
one
two
three

$ ssh user@host cat -vet 2
one$
two$
three$
Run Code Online (Sandbox Code Playgroud)

行尾字符的转换

使用伪终端,换行符(十进制 10,0AASCII 中的十六进制)也被转换为CRLF序列(0D0AASCII 中的十六进制)。

从前面的例子:

$ ssh -t user@host cat 1 | cat -vet
one^M$
two^M$
^Sthree^M$
Connection to host closed.
Run Code Online (Sandbox Code Playgroud)

使用伪终端复制二进制文件

$ ssh -t anthony@remote_host 'cat /usr/bin/free' > ~/free
Connection to remote_host closed.
Run Code Online (Sandbox Code Playgroud)

不使用伪终端复制二进制文件:

$ ssh anthony@remote_host 'cat /usr/bin/free' > ~/free2
Run Code Online (Sandbox Code Playgroud)

这两个文件不一样:

$ diff ~/free*
Binary files /home/anthony/free and /home/anthony/free2 differ
Run Code Online (Sandbox Code Playgroud)

用伪终端复制的那个被破坏了:

$ chmod +x ~/free*
$ ./free
Segmentation fault
Run Code Online (Sandbox Code Playgroud)

而另一个不是:

$ ./free2
             total       used       free     shared    buffers     cached
Mem:       2065496    1980876      84620          0      48264    1502444
-/+ buffers/cache:     430168    1635328
Swap:      4128760        112    4128648
Run Code Online (Sandbox Code Playgroud)

通过 SSH 传输文件

这对于诸如scprsync使用 SSH 进行数据传输的程序尤其重要。此的SCP协议如何工作的详细描述中解释了SCP协议如何由文本协议消息和二进制文件数据的混合物。


OpenSSH 帮助保护您免受自己的伤害

值得注意的是,即使使用了该-t标志,OpenSSHssh客户端如果检测到其stdin流不是终端,也会拒绝分配伪终端:

$ echo testing | ssh -t anthony@remote_host 'echo $TERM'
Pseudo-terminal will not be allocated because stdin is not a terminal.
dumb
Run Code Online (Sandbox Code Playgroud)

您仍然可以强制 OpenSSH 客户端使用以下命令分配伪终端-tt

$ echo testing | ssh -tt anthony@remote_host 'echo $TERM'
xterm
Run Code Online (Sandbox Code Playgroud)

在任何一种情况下,它(明智地)都不关心是否stdout或被stderr重定向:

$ ssh -t anthony@remote_host 'echo $TERM' >| ssh_output
Connection to remote_host closed.
Run Code Online (Sandbox Code Playgroud)

  • 这是我没有考虑过的非常有趣的一点,感谢您添加此答案! (2认同)

小智 6

在远程主机上,我们必须进行以下设置:

/etc/sudoers
...
Defaults requiretty
Run Code Online (Sandbox Code Playgroud)

没有须藤

$ ssh -T user@host echo -e 'foo\\nbar' | cat -e
foo$
bar$
Run Code Online (Sandbox Code Playgroud)

并使用 sudo

$ ssh -T user@host sudo echo -e 'foo\\nbar' | cat -e
sudo: sorry, you must have a tty to run sudo
Run Code Online (Sandbox Code Playgroud)

使用 sudo 我们得到额外的回车符

$ ssh -t user@host sudo echo -e 'foo\\nbar' | cat -e
foo^M$
      bar^M$
            Connection to localhost closed.
Run Code Online (Sandbox Code Playgroud)

解决方案是禁用将换行符转换为回车换行符stty -onlcr

$ ssh -t user@host stty -onlcr\; sudo echo -e 'foo\\nbar' | cat -e
foo$
    bar$
        Connection to localhost closed.
Run Code Online (Sandbox Code Playgroud)