【问题标题】:Execute scp using Popen without having to enter password使用 Popen 执行 scp 无需输入密码
【发布时间】:2018-09-26 05:37:54
【问题描述】:

我有以下脚本

test.py

#!/usr/bin/env python2

from subprocess import Popen, PIPE, STDOUT
proc = Popen(['scp', 'test_file', 'user@192.168.120.172:/home/user/data'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)

out, err = proc.communicate(input='userpass\n')
print('stdout: ' + out)
print('stderr: ' + str(err))

这意味着使用给定用户user的登录名将test_file复制到位于10.0.0.2的远程目录/home/user/data中。为此,我必须使用 scp。不允许密钥验证(不要问为什么,事情就是这样,我无法更改)。

即使我将userpass 传递给进程,我仍然会在终端内收到输入密码的提示。我只想在本地机器上运行 test.py,然后远程获取文件而无需任何用户交互。

虽然我没有正确使用communicate(),所以我手动调用了

proc.stdin.write('userpass\n')
proc.stdin.flush()
out, err = proc.communicate()

但没有任何改变,我仍然收到密码提示。

【问题讨论】:

  • 可以改用公钥认证吗?这样你就完全不用担心密码了。
  • 不,正如我所提到的,scp 是唯一的方法。我也对此感到不安,但事实就是如此。 :DI 知道我可以使用expect/pexepect(但它需要在系统上安装一些软件,我不允许这样做),sshpass(与第一点相同),关键身份验证是不去等等。
  • 我不是在问scp,我是在问你是否可以使用scp支持的公钥认证来代替密码认证。
  • scp 是一种使用 SSH 协议进行连接和身份验证的远程复制方法。您应该可以在此处使用基于密钥的身份验证。您拥有远程主机上用户的密码。究竟是什么让您无法在源计算机上为用户创建密钥对以将公钥添加到远程用户的 .ssh/authorized_keys?公司政策?
  • @Duncan,我是说没有密钥认证。事物的设置方式也将不可用。为了省略这种讨论,我将把它添加到问题中。

标签: python passwords pipe popen scp


【解决方案1】:

scpssh 尝试读取密码时,它们不会从stdin 读取密码。相反,他们打开/dev/tty 并直接从连接的终端读取密码。

sshpass 通过创建自己的虚拟终端并在该终端控制的子进程中生成 sshscp 来工作。这基本上是拦截密码提示的唯一方法。推荐的解决方案是使用公钥认证,但你说你不能这样做。

如果您说您无法安装 sshpass 并且也无法使用安全形式的身份验证,那么您唯一能做的就是在您自己的代码中重新实现 sshpass。 sshpass 本身是在 GPL 下获得许可的,因此如果您复制现有代码,请确保不要侵犯其 copyleft。

这是来自sshpasssource 的评论,描述了它如何设法欺骗输入:

/*
   Comment no. 3.14159
   This comment documents the history of code.
   We need to open the slavept inside the child process, after "setsid", so that it becomes the controlling
   TTY for the process. We do not, otherwise, need the file descriptor open. The original approach was to
   close the fd immediately after, as it is no longer needed.
   It turns out that (at least) the Linux kernel considers a master ptty fd that has no open slave fds
   to be unused, and causes "select" to return with "error on fd". The subsequent read would fail, causing us
   to go into an infinite loop. This is a bug in the kernel, as the fact that a master ptty fd has no slaves
   is not a permenant problem. As long as processes exist that have the slave end as their controlling TTYs,
   new slave fds can be created by opening /dev/tty, which is exactly what ssh is, in fact, doing.
   Our attempt at solving this problem, then, was to have the child process not close its end of the slave
   ptty fd. We do, essentially, leak this fd, but this was a small price to pay. This worked great up until
   openssh version 5.6.
   Openssh version 5.6 looks at all of its open file descriptors, and closes any that it does not know what
   they are for. While entirely within its prerogative, this breaks our fix, causing sshpass to either
   hang, or do the infinite loop again.
   Our solution is to keep the slave end open in both parent AND child, at least until the handshake is
   complete, at which point we no longer need to monitor the TTY anyways.
 */

所以 sshpass 正在做的是打开一个伪终端设备(使用posix_openpt),然后分叉并在子进程中使从属进程成为进程的控制点。然后它可以执行scp 命令。

我不知道你是否可以通过 Python 实现这一点,但好消息是标准库确实包含用于处理伪终端的函数:https://docs.python.org/3.6/library/pty.html

【讨论】:

  • 我对你回复的第一段很感兴趣。如果scpssh 使用/dev/tty 是不是不可能写到tty?还是以一种无法访问的方式封装?
  • 是的,我已经扩展了我的答案。 sshpass 确实创建了一个新的 tty 并确保该命令将其视为控制终端。听起来还是有点乱。
  • 该死的...我会再次尝试与这一切作斗争。对于 1) 已经在工作的东西和 2) 使用 GPL 代码来说,工作量太大了。仍然将其标记为答案,因为它确实提供了一些指向何处查看。
猜你喜欢
  • 1970-01-01
  • 2011-05-07
  • 2013-09-22
  • 2019-02-15
  • 2013-11-24
  • 1970-01-01
  • 2011-11-23
  • 2011-04-19
  • 2019-08-25
相关资源
最近更新 更多