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

Python 使用Popen执行scp,无需输入密码,python,passwords,pipe,popen,scp,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') prin

我有以下脚本

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))
这意味着使用给定用户的登录名
用户
在位于
10.0.0.2
的远程目录
/home/user/data
中复制
test\u文件
。为了做到这一点,我必须使用scp。不允许密钥身份验证(不要问为什么,这只是事情的现状,我无法更改它们)

即使我正在将
userpass
传送到进程,我仍然会在终端内部得到输入密码的提示。我只想在本地机器上运行test.py,然后远程设备在没有任何用户交互的情况下获取文件

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

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

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

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

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

如果正如您所说,您无法安装
sshpass
,也无法使用安全的身份验证形式,那么您唯一能做的就是在自己的代码中重新实现sshpass
sshpass
本身是根据GPL授权的,因此如果复制现有代码,请确保不要侵犯其版权

下面是来自
sshpass
的注释,它描述了如何设法欺骗输入:

/*
   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所做的是打开一个伪终端设备(使用),然后分叉,在子进程中使从进程成为进程的控制pt。然后它可以执行
scp
命令


我不知道您是否可以从Python中实现这一点,但好消息是,标准库确实包含用于处理伪终端的函数:

您可以改用公钥身份验证吗?那你就不必担心密码了。不,正如我提到的,scp是唯一的方法。我也为此感到不安,但事实就是这样D我知道我可以使用
expect
/
pexepect
(但它需要在系统上安装一些软件,我不允许这样做),
sshpass
(与第一点的原因相同),关键身份验证是禁止的,等等。我不是问
scp
,我在问您是否可以使用
scp
支持的公钥身份验证而不是密码身份验证。scp是一种远程复制方法,使用SSH协议进行连接和身份验证。您应该能够在此处使用基于密钥的身份验证。您拥有远程主机上用户的密码。到底是什么阻止您在源计算机上为用户创建密钥对以将公钥添加到远程用户的.ssh/authorized_密钥?公司政策?@Duncan,我是说没有关键认证。事情的设置方式也将不可用。为了省去这种讨论,我将把这一点添加到问题中。我对你答复的第一段很感兴趣。如果
scp
ssh
使用
/dev/tty
是否无法写入该
tty
?或者它的封装方式使人无法访问它?是的,我已经扩展了我的答案。sshpass确实创建了一个新的tty,并确保命令将其视为控制终端。听起来还是有点乱。该死的……我会再尝试一次来对抗这一切。对于1)已经在工作的东西和2)使用GPL代码是不可能的,这是太多的工作了。仍然将此标记为答案,因为它确实提供了一些指向何处的指针。