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

2024-11-04 08:43:00
admin
原创
211
摘要:问题描述:我正在尝试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
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用