使用 subprocess.Popen 通过 SSH 或 SCP 发送密码

2024-11-04 08:43:00
admin
原创
164
摘要:问题描述:我正在尝试scp使用 运行 (安全复制) 命令subprocess.Popen。登录需要我发送密码:from subprocess import Popen, PIPE proc = Popen(['scp', "user@10.0.1.12:...

问题描述:

我正在尝试scp使用 运行 (安全复制) 命令subprocess.Popen。登录需要我发送密码:

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.

确信密码是正确的。我可以通过手动调用scpshell 轻松验证密码。那么为什么这不管用呢?

请注意,有许多类似的问题,询问subprocess.Popen并发送自动 SSH 或 FTP 登录的密码:

如何使用 Python 脚本在 Linux 中设置用户密码?

使用子进程发送密码

由于我使用的是 Python 3,所以这些问题的答案不起作用或不适用。


解决方案 1:

ssh这是一个使用密码的函数pexpect

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

解决方案 2:

OpenSSHscp实用程序调用该ssh程序与远程主机建立 SSH 连接,而 ssh 进程负责处理身份验证。该ssh实用程序不接受命令行或标准输入中的密码。我相信这是 OpenSSH 开发人员的故意决定,因为他们认为人们应该使用更安全的机制,如基于密钥的身份验证。任何调用 ssh 的解决方案都将遵循以下方法之一:

  1. 使用SSH 密钥进行身份验证,而不是密码。

  2. 使用sshpass、expect或类似工具自动响应密码提示。

  3. 使用(滥用)SSH_ASKPASS 功能ssh通过调用另一个命令来获取密码,如此处或此处所述,或在此处的一些答案中所述。

  4. 让 SSH 服务器管理员启用基于主机的身份验证并使用它。请注意,基于主机的身份验证仅适用于某些网络环境。请参阅此处和此处的其他说明。

  5. 使用 perl、python、java 或您喜欢的语言编写您自己的 ssh 客户端。大多数现代编程语言都有可用的 ssh 客户端库,您可以完全控制客户端如何获取密码。

  6. 下载ssh 源代码并构建一个ssh按您想要的方式工作的修改版本。

  7. 使用不同的 ssh 客户端。还有其他可用的 ssh 客户端,既有免费的也有商业的。其中一个可能比 OpenSSH 客户端更适合您的需求。

在这种特殊情况下,鉴于您已经scp从 python 脚本调用,似乎其中一种方法是最合理的:

  1. 使用pexpect(python expect 模块)来调用scp它并输入密码。

  2. 使用paramiko(python ssh 实现)来执行此 ssh 任务,而不是调用外部程序。

解决方案 3:

您链接的第二个答案建议您使用Pexpect(这通常是与需要输入的命令行程序交互的正确方法)。

解决方案 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)

解决方案 5:

我猜有些应用程序使用 stdin 与用户交互,有些应用程序使用终端交互。在这种情况下,当我们使用 PIPE 写入密码时,我们正在写入 stdin。但 SCP 应用程序从终端读取密码。由于子进程无法使用终端与用户交互,而只能使用 stdin 交互,因此我们不能使用子进程模块,我们必须使用 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='
')

    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)

解决方案 7:

这是我根据@Kobayashi 和@sjbx 发布的代码进行重写的,但目的是执行 scp 请求,因此感谢他们两位。

def scp(host, user, password, from_dir, to_dir, timeout=300, recursive=False):
    fname = tempfile.mktemp()
    fout = open(fname, 'w')

    scp_cmd = 'scp'
    if recursive:
        scp_cmd += ' -r'
    scp_cmd += f' {user}@{host}:{from_dir} {to_dir}'
    child = pexpect.spawnu(scp_cmd, timeout=timeout)
    child.expect(['[pP]assword: '])
    child.sendline(str(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
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1124  
  IPD(Integrated Product Development,集成产品开发)流程是一种广泛应用于高科技和制造业的产品开发方法论。它通过跨职能团队的紧密协作,将产品开发周期缩短,同时提高产品质量和市场成功率。在IPD流程中,CDCP(Concept Decision Checkpoint,概念决策检查点)是一个关...
IPD培训课程   79  
  研发IPD(集成产品开发)流程作为一种系统化的产品开发方法,已经在许多行业中得到广泛应用。它不仅能够提升产品开发的效率和质量,还能够通过优化流程和资源分配,显著提高客户满意度。客户满意度是企业长期成功的关键因素之一,而IPD流程通过其独特的结构和机制,能够确保产品从概念到市场交付的每个环节都围绕客户需求展开。本文将深入...
IPD流程   70  
  IPD(Integrated Product Development,集成产品开发)流程是一种以跨职能团队协作为核心的产品开发方法,旨在通过优化资源分配、提高沟通效率以及减少返工,从而缩短项目周期并提升产品质量。随着企业对产品上市速度的要求越来越高,IPD流程的应用价值愈发凸显。通过整合产品开发过程中的各个环节,IPD...
IPD项目管理咨询   82  
  跨部门沟通是企业运营中不可或缺的一环,尤其在复杂的产品开发过程中,不同部门之间的协作效率直接影响项目的成败。集成产品开发(IPD)作为一种系统化的项目管理方法,旨在通过优化流程和增强团队协作来提升产品开发的效率和质量。然而,跨部门沟通的复杂性往往成为IPD实施中的一大挑战。部门之间的目标差异、信息不对称以及沟通渠道不畅...
IPD是什么意思   74  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用