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

2024-11-04 08:43:00
admin
原创
38
摘要:问题描述:我正在尝试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
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用