在SSH上正确使用sudo的方法是什么?

207
我有一个脚本,通过SSH在远程服务器上使用sudo运行另一个脚本。然而,当我输入密码时,它会显示在终端上。除此之外,它运行得很正常。
ssh user@server "sudo script"

怎样才能在通过SSH输入密码时,使得密码不会在我输入的过程中显示出来,这样才是正确的做法?

3
就我而言,寻找通过SSH进行sudo的方法的原因是,当尝试类似于 ssh <user@server> sudo <script> 的操作时,它没有起作用,因为我收到了 sudo:no tty present and no askpass program specified 的错误提示。 - knocte
8个回答

314
另一种方法是使用 -t 选项来打开 ssh
ssh -t user@server "sudo script"

查看man ssh
 -t      Force pseudo-tty allocation.  This can be used to execute arbi-
         trary screen-based programs on a remote machine, which can be
         very useful, e.g., when implementing menu services.  Multiple -t
         options force tty allocation, even if ssh has no local tty.

2
很好,我知道-t选项,只是不知道它适用于sudo提示。 - user507484
4
“-t”选项的作用是什么? - Vince
@Vince 请查看http://go2linux.garron.me/linux/2010/11/ssh-t-open-pseudo-tty-run-commands-remote-server-809。 - givemesnacks
4
这里无法正常工作:ssh -t localhost <<< "sudo touch file;"编辑:显然重要的是将命令作为参数提供,而不是通过标准输入(这在事后看起来很有意义)。 - lmat - Reinstate Monica
可以在不需要 -t 选项的情况下完成相同的操作,只需告诉sudo不要求终端即可。在这种情况下,它会从stdin中获取输入。有关详细信息和示例,请参见下面的答案。 - Kurt Fitzner
显示剩余3条评论

48

我可以用以下命令完全自动化它:

echo pass | ssh -tt user@server "sudo script"

优点:

  • 无需密码提示
  • 不会在远程机器bash历史记录中显示密码

关于安全性:正如Kurt所说,运行此命令将在您的本地bash历史记录中显示您的密码,最好将密码保存在不同的文件中或将所有命令保存在 .sh 文件中并执行它。注意:文件需要具有正确的权限,以便只有允许的用户可以访问它。


8
这段话的意思是:该命令会被记录在本地系统的bash历史记录中,包括密码。最好将密码存储在文件中,然后使用cat命令读取该文件。 - Kurt Fitzner
6
我知道-t的用法,但我没有仔细阅读手册,以至于没看到可以通过两次传递来使用它! 这就是我寻找了数月的内容! 作为安全性的增加,我只需在运行 read -s -p "Password: " pw 前设置一个密码变量,然后执行 echo "$pw" | ....。现在我已经将这个脚本整合成一个方便的程序 :)。 - kael
对我来说运行得非常好! - MiKr13
1
为什么不为您需要运行的特定用户和命令设置无密码sudo呢?这不是一个SSH问题... - nomen
5
关于防止密码出现在本地历史记录中,1)如果它是脚本中的一行,则不是问题。2)对于_交互式_ shell,如果HISTCONTROL设置为ignorespaceignoreboth(假设使用bash,其他shell类似),可以通过在命令前加一个空格来轻松避免本地历史记录。另一个安全问题是密码可能会显示在进程表中。 echo通常是shell内置的,因此它可能是OK,但我更愿意使用_here-string_:<<<pass - Robin A. Meade
显示剩余4条评论

13

在SSH中使用Sudo传递密码,无需tty:

您可以告诉sudo不要要求交互式密码验证并只从stdin获取密码,以此来在ssh中使用sudo而无需强制ssh具有伪终端(不使用ssh的“-t”开关)。这可以通过在sudo上使用“-S”开关来实现。这使得sudo监听stdin上的密码,并在看到换行符时停止监听。

示例1 - 简单远程命令

在此示例中,我们发送一个简单的whoami命令:

$ ssh user@server cat \| sudo --prompt="" -S -- whoami << EOF
> <remote_sudo_password>
root

我们告诉sudo不要发出提示,并从stdin获取输入。这使得sudo密码传递完全静默,所以你只会得到whoami的输出。
这种技术的好处是允许你通过ssh运行需要stdin输入的程序。这是因为sudo在第一行stdin上消耗了密码,然后让它运行的任何程序继续抓取stdin。
示例2 - 需要自己的stdin的远程命令
在下面的示例中,通过sudo执行远程命令"cat",并且我们提供了一些额外的stdin行以供远程cat显示。
$ ssh user@server cat \| sudo --prompt="" -S -- "cat" << EOF
> <remote_sudo_password>
> Extra line1
> Extra line2
> EOF
Extra line1
Extra line2

输出结果显示<remote_sudo_password>这一行被sudo使用,并且远程执行的cat随后会显示额外的行。
如果您想要使用ssh传递密码给特权命令而不使用命令行,则可以使用此方法。比如说,您想要通过ssh挂载一个远程加密容器。
示例3 - 挂载远程VeraCrypt容器
在此示例脚本中,我们正在通过sudo远程挂载VeraCrypt容器,而无需任何额外提示文本:
#!/bin/sh
ssh user@server cat \| sudo --prompt="" -S -- "veracrypt --non-interactive --stdin --keyfiles=/path/to/test.key /path/to/test.img /mnt/mountpoint" << EOF
SudoPassword
VeraCryptContainerPassword
EOF

需要注意的是,在上面所有的命令行示例中(除了脚本),命令行中的<< EOF结构将导致键入的所有内容,包括密码,记录在本地计算机的.bash_history文件中。因此,强烈建议您在实际使用时完全通过脚本进行操作,就像上面的veracrypt示例一样,或者如果在命令行上则将密码放在文件中并通过ssh重定向该文件。 示例1a - 不带本地命令行密码的示例1 因此,第一个示例将变为:
$ cat text_file_with_sudo_password | ssh user@server cat \| sudo --prompt="" -S -- whoami
root

示例2a - 示例2无本地命令行密码

第二个示例将变为:

$ cat text_file_with_sudo_password - << EOF | ssh va1der.net cat \| sudo --prompt="" -S -- cat
> Extra line1
> Extra line2
> EOF
Extra line1
Extra line2

如果你将整个脚本放在一个脚本中,则将密码放在单独的文件中是不必要的,因为脚本的内容不会出现在你的历史记录中。然而,如果你想允许不应该看到密码的用户执行脚本,那么将密码放在单独的文件中仍然可能有用。


在ssh远程命令中,为什么需要执行“cat”并将结果导入sudo?你能否只使用“sudo”作为主要的ssh远程命令? - joanpau
@jonpau 那个cat命令正在接受stdin并通过sudo进行管道传输,以便将其传递给sudo密码。它展示了如何通过stdin安全地通过ssh传输sudo密码。 - Kurt Fitzner
当systemd在按键时使sudo提示回显星号时,这是否会导致故障? - ThorSummoner
@ThorSummoner 不,它仍然可以正常工作。 - Kurt Fitzner
我认为在第一个例子中,在密码后面缺少EOF。 - danadam
显示剩余3条评论

7

假设您不想要密码提示:

ssh $HOST 'echo $PASSWORD | sudo -S $COMMMAND'

例子

ssh me@localhost 'echo secret | sudo -S echo hi' # outputs 'hi'

23
这非常危险,因为明文密码最终出现在命令行上。命令行是公开的,可以被系统上的每个其他用户和进程看到。例如,一个非特权用户使用"ps auxwwwww" 命令将显示所有正在运行的进程以及运行它们的命令行。 - Kurt Fitzner
6
更不用说把你的密码(以明文形式)放入bash_history文件中了。 - Layne Bernardo
1
根据您的shell配置(bash中的默认配置),在命令前面加上空格可以跳过将其添加到历史记录中。您也可以保留$PASSWORD,并在其前面加上空格前缀export PASSWORD=secret。只需确保在退出会话后不要让环境变量挂起即可。 - Legogris
另外,您可以使bash历史记录不记录任何包含特定字符串的命令,我已禁止存储任何export命令,以避免任何重要的令牌或秘密被记录在那里。 - theist
这并不总是危险的。但还是谢谢你的建议。 - Leathan
这并不总是危险的。但还是谢谢你的建议。 - undefined

4

最好的方法是ssh -t user@server "sudo <scriptname>",例如:ssh -t user@server "sudo reboot"。它首先会提示用户输入密码,然后再提示root用户输入密码(因为我们正在使用root权限运行脚本或命令)。

希望这能帮助你并解决你的疑问。


1

确认 @ofirule 的答案非常有效。 我甚至尝试了使用 sshpass,它也可以工作。以下是如何使用 sshpass:

echo $pass | sshpass -p $pass ssh -tt cloud_user@$ip "sudo su -"

你将直接进入根Shell


1

8
在这种情况下,我实际上需要一个密码,并直接键入它,所以这对我不起作用。 - darkfeline

0
echo $VAR_REMOTEROOTPASS | ssh -tt -i $PATH_TO_KEY/id_mykey $VAR_REMOTEUSER@$varRemoteHost 
echo \"$varCommand\" | sudo bash

3
请为您的代码提供解释。 - pppery

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接