使用Popen执行scp而无需输入密码

时间:2018-04-16 13:02:09

标签: python passwords pipe popen scp

我有以下脚本

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))

用于使用特定用户test_file的登录名复制位于/home/user/data的远程目录10.0.0.2中的user。为此,我必须使用 scp。不允许密钥验证(不要问为什么,它只是事情是如何,我无法改变它们。)

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

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

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

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

1 个答案:

答案 0 :(得分:0)

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

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

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

以下是来自sshpass source的评论,其中介绍了如何设法欺骗输入:

/*
   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),然后在子进程中使得slave成为进程的控制pt。然后它可以执行scp命令。

我不知道你是否可以从Python开始使用它,但好消息是标准库确实包含了使用伪终端的功能:https://docs.python.org/3.6/library/pty.html