使用 subprocess.Popen 通过 SSH 或 SCP 发送密码
Posted
技术标签:
【中文标题】使用 subprocess.Popen 通过 SSH 或 SCP 发送密码【英文标题】:Sending a password over SSH or SCP with subprocess.Popen 【发布时间】:2013-02-16 11:59:30 【问题描述】:我正在尝试使用subprocess.Popen
运行scp
(安全复制)命令。登录需要我发送密码:
from subprocess import Popen, PIPE
proc = Popen(['scp', "user@10.0.1.12:/foo/bar/somefile.txt", "."], stdin = PIPE)
proc.stdin.write(b'mypassword')
proc.stdin.flush()
这会立即返回错误:
user@10.0.1.12's password:
Permission denied, please try again.
我确定密码是正确的。我通过在 shell 上手动调用 scp
来轻松验证它。那么为什么这不起作用呢?
注意,有很多类似的问题,询问subprocess.Popen
和发送自动SSH或FTP登录密码:
How can I set a users password in linux from a python script?Use subprocess to send a password
这些问题的答案不起作用和/或不适用,因为我使用的是 Python 3。
【问题讨论】:
我不熟悉这段代码,但你尝试使用 -p 参数?也是作为验证方法的密钥交换? -p 我的意思是“-p mypassword user@10.0.1.12:/foo/bar/somefile.txt”我不知道你能不能这样做 @diego2k 据我所知,scp 不接受通过命令行输入密码的开关(它的手册页不包含任何内容)。一般来说,在命令行上提供密码不是一个好习惯,因为这将被记录在 .bash_history 或许您可以通过创建authorized_keys
文件来解决此问题。
如果您要复制.
(一个目录),scp
不需要-r
吗?如果您正在努力让pexpect
工作,我在下面添加了一个功能应该会有所帮助。
【参考方案1】:
这是一个使用pexpect
的密码ssh
的函数:
import pexpect
import tempfile
def ssh(host, cmd, user, password, timeout=30, bg_run=False):
"""SSH'es to a host using the supplied credentials and executes a command.
Throws an exception if the command doesn't return 0.
bgrun: run command in the background"""
fname = tempfile.mktemp()
fout = open(fname, 'w')
options = '-q -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oPubkeyAuthentication=no'
if bg_run:
options += ' -f'
ssh_cmd = 'ssh %s@%s %s "%s"' % (user, host, options, cmd)
child = pexpect.spawn(ssh_cmd, timeout=timeout) #spawnu for Python 3
child.expect(['[pP]assword: '])
child.sendline(password)
child.logfile = fout
child.expect(pexpect.EOF)
child.close()
fout.close()
fin = open(fname, 'r')
stdout = fin.read()
fin.close()
if 0 != child.exitstatus:
raise Exception(stdout)
return stdout
使用scp
应该可以实现类似的操作。
【讨论】:
谢谢。这帮助了我。我正在查,但你能告诉我这条线是什么意思吗? '-q -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oPubkeyAuthentication=no' @alfonso 这些是您可以在命令行上或在您的.ssh/config
中传递给ssh 的选项,而无需-o
。第一个告诉 ssh 如果主机已更改其密钥,则不要抱怨,因为自从您上次使用它以来,它可能已重新安装。第二个说使用 /dev/null 而不是 .ssh/known_hosts 所以如果它被重新安装并带有重复条目警告,它将再次停止抱怨。不使用强制输入密码的密钥的最后几天。
对于python 3,应该使用spawnu
而不是spawn
,根据***.com/a/26835855/2118777来避免write() argument must be str, not bytes
import tempfile
和import pexpect
要加,pip install pexpect
或者pip3 install pexpect
可以安装需要的包【参考方案2】:
OpenSSH scp
实用程序调用ssh
程序以建立与远程主机的 SSH 连接,并且 ssh 进程处理身份验证。 ssh
实用程序不接受命令行或其标准输入中的密码。我相信这是 OpenSSH 开发人员深思熟虑的决定,因为他们认为人们应该使用更安全的机制,例如基于密钥的身份验证。任何调用 ssh 的解决方案都将遵循以下方法之一:
-
使用SSH key 代替密码进行身份验证。
使用sshpass、expect 或类似工具自动响应密码提示。
使用(滥用)SSH_ASKPASS 功能来获取
ssh
通过调用另一个命令来获取密码,描述为here 或here,或在某些答案中here。
让 SSH 服务器管理员启用 host-based authentication 并使用它。请注意,基于主机的身份验证仅适用于某些网络环境。请参阅附加说明 here 和 here。
使用 perl、python、java 或您喜欢的语言编写您自己的 ssh 客户端。大多数现代编程语言都有 ssh 客户端库,您可以完全控制客户端获取密码的方式。
下载ssh source code 并构建ssh
的修改版本,以您想要的方式工作。
使用不同的 ssh 客户端。有免费的和商业的other ssh clients 可用。其中之一可能比 OpenSSH 客户端更适合您的需求。
在这种特殊情况下,鉴于您已经从 python 脚本调用 scp
,似乎其中一种方法是最合理的方法:
-
使用 pexpect(python 的 expect 模块)调用
scp
并将密码提供给它。
使用paramiko(python ssh 实现)来执行此 ssh 任务,而不是调用外部程序。
【讨论】:
【参考方案3】:您链接的第二个答案建议您使用 Pexpect(这通常是与需要输入的命令行程序进行交互的正确方法)。其中有一个fork 适用于您可以使用的python3。
【讨论】:
注意:常规的pexpect 现在支持 Python 3。【参考方案4】:Pexpect 有一个专门用于此的库:pxssh
http://pexpect.readthedocs.org/en/stable/api/pxssh.html
import pxssh
import getpass
try:
s = pxssh.pxssh()
hostname = raw_input('hostname: ')
username = raw_input('username: ')
password = getpass.getpass('password: ')
s.login(hostname, username, password)
s.sendline('uptime') # run a command
s.prompt() # match the prompt
print(s.before) # print everything before the prompt.
s.logout()
except pxssh.ExceptionPxssh as e:
print("pxssh failed on login.")
print(e)
【讨论】:
导入 pxssh \snap install microk8s【参考方案5】:我猜有些应用程序使用标准输入与用户交互,有些应用程序使用终端交互。在这种情况下,当我们使用 PIPE 写入密码时,我们正在写入标准输入。但 SCP 应用程序从终端读取密码。由于 subprocess 不能使用终端与用户交互,但只能使用 stdin 交互,我们不能使用 subprocess 模块,我们必须使用 pexpect 使用 scp 复制文件。
欢迎指正。
【讨论】:
【参考方案6】:这是我基于 pexpect 的 scp 函数。除了密码,它还可以处理通配符(即多个文件传输)。 要处理多个文件传输(即通配符),我们需要通过 shell 发出命令。参考pexpect FAQ。
import pexpect
def scp(src,user2,host2,tgt,pwd,opts='',timeout=30):
''' Performs the scp command. Transfers file(s) from local host to remote host '''
cmd = f'''/bin/bash -c "scp opts src user2@host2:tgt"'''
print("Executing the following cmd:",cmd,sep='\n')
tmpFl = '/tmp/scp.log'
fp = open(tmpFl,'wb')
childP = pexpect.spawn(cmd,timeout=timeout)
try:
childP.sendline(cmd)
childP.expect([f"user2@host2's password:"])
childP.sendline(pwd)
childP.logfile = fp
childP.expect(pexpect.EOF)
childP.close()
fp.close()
fp = open(tmpFl,'r')
stdout = fp.read()
fp.close()
if childP.exitstatus != 0:
raise Exception(stdout)
except KeyboardInterrupt:
childP.close()
fp.close()
return
print(stdout)
可以这样使用:
params =
'src': '/home/src/*.txt',
'user2': 'userName',
'host2': '192.168.1.300',
'tgt': '/home/userName/',
'pwd': myPwd(),
'opts': '',
scp(**params)
【讨论】:
以上是关于使用 subprocess.Popen 通过 SSH 或 SCP 发送密码的主要内容,如果未能解决你的问题,请参考以下文章
实时 subprocess.Popen 通过 stdout 和 PIPE
通过 subprocess.Popen 在 python 中执行 R 脚本
如何从 subprocess.Popen 使用 STDIN [重复]