管道 ssh sudo 的输出

Piping the output of ssh sudo

有时我需要在远程服务器上以 root 身份 运行 命令,并在我的本地服务器上解析命令的输出。远程服务器不允许通过 ssh 进行 root 登录,但是 sudo 配置为需要密码。我需要做的一个简化示例是

ssh remote sudo echo bar | tr bar foo

(显然,在这个简化的例子中,没有充分的理由需要在不同的机器上 运行 echotr:这只是一个玩具示例来解释我我正在努力。)

如果我运行上面的命令,我得到一个错误,sudo 无法提示输入密码:

richard@local:~$ ssh remote sudo echo bar | tr bar foo
sudo: no tty present and no askpass program specified

我可以尝试解决此问题的一种方法是将 -t 选项添加到 ssh。如果我这样做,sudo 会等待并接受密码,但是 ssh 的伪终端的输出会转到标准输出,这意味着 sudo 提示消息会通过管道传输到 tr 并且不显示给用户。如果用户不知道 sudo 正在等待密码,他们会认为脚本已挂起,将提示消息传递给管道可能会中断进一步的处理:

richard@local:~$ ssh -t remote sudo echo bar | tr bar foo
[sudo] posswood foo oichood: 
foo

(这个公认的愚蠢示例显示提示已由 tr 命令处理,输出通过管道传输到。)

我看到的另一种尝试解决此问题的方法是将 -S 选项添加到 sudo。如果我这样做,sudo 会在 stderr 上提示输入密码,因此提示不会通过管道传递。这很好,但是 sudo 也接受标准输入的密码,这意味着它会回显到终端,任何从用户的肩膀上看的人都可以看到它:

richard@local:~$ ssh remote sudo -S echo bar | tr bar foo
[sudo] password for richard: p8ssw0rd
foo

我发现用这两个选项解决问题的方法并不优雅,但如果用户第一次输入错误的密码,我的解决方法就会遇到问题。这本身就是一个问题。这方面的例子是:

richard@local:~$ echo "[sudo] password for $USER:"; \
  ssh -t remote sudo echo bar | tail +2 | tr bar foo

richard@local:~$ (read -s password; echo $password; echo >&2) \
  | ssh remote sudo -S echo bar | tr bar foo

我相信一定有一个好的解决方案,因为这似乎不是一件不寻常的事情。有什么想法吗?

我想出的最佳解决方案是使用 sudo -S 并禁用本地回显,这样在您输入密码时就不会显示密码:

$ { stty -echo; ssh remote sudo -S echo hello; stty echo; echo 1>&2; }
[sudo] password for user:
hello

这让 sudo 负责密码提示,因此如果用户输入错误的密码,它也能正常工作。

我认为使用 ssh -t 的任何解决方案都无法正常工作,因为它结合了 stderr 和 stdout。