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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15166973/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 19:10:04  来源:igfitidea点击:

Sending a password over SSH or SCP with subprocess.Popen

pythonlinuxsshpython-3.xsubprocess

提问by Channel72

I'm trying to run an scp(secure copy) command using subprocess.Popen. The login requires that I send a password:

我正在尝试scp使用subprocess.Popen. 登录要求我发送密码:

from subprocess import Popen, PIPE

proc = Popen(['scp', "[email protected]:/foo/bar/somefile.txt", "."], stdin = PIPE)
proc.stdin.write(b'mypassword')
proc.stdin.flush()

This immediately returns an error:

这立即返回一个错误:

[email protected]'s password:
Permission denied, please try again.

I'm certainthe password is correct. I easily verify it by manually invoking scpon the shell. So why doesn't this work?

确定密码是正确的。我通过scp在 shell 上手动调用来轻松验证它。那么为什么这不起作用呢?

Note, there are many similar questions to this, asking about subprocess.Popenand sending a password for automated SSH or FTP login:

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

How can I set a users password in linux from a python script?
Use subprocess to send a password

如何从python脚本在linux中设置用户密码?
使用子进程发送密码

The answer(s) to these questions don't work and/or don't apply because I am using Python 3.

这些问题的答案不起作用和/或不适用,因为我使用的是 Python 3。

采纳答案by entropy

The second answer you linked suggests you use Pexpect(which is usually the right way to go about interacting with command line programs that expect input). There is a forkof it which works for python3 which you can use.

您链接的第二个答案建议您使用 Pexpect(这通常是与需要输入的命令行程序进行交互的正确方法)。它有一个分支,适用于您可以使用的 python3。

回答by sjbx

Here's a function to sshwith a password using pexpect:

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

import pexpect

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

Something similar should be possible using scp.

使用scp.

回答by Tommy

Pexpect has a library for exactly this: pxssh

Pexpect 有一个专门用于此的库:pxssh

http://pexpect.readthedocs.org/en/stable/api/pxssh.html

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)

回答by Raju Pitta

I guess some applications interact with the user using stdin and some applications interact using terminal. In this case when we write the password using PIPE we are writing to stdin. But SCP application reads the password from terminal. As subprocess cannot interact with user using terminal but can only interact using stdin we cannot use the subprocess module and we must use pexpect for copying the file using scp.

我猜有些应用程序使用 stdin 与用户交互,有些应用程序使用终端进行交互。在这种情况下,当我们使用 PIPE 写入密码时,我们正在写入标准输入。但是 SCP 应用程序从终端读取密码。由于子进程不能使用终端与用户交互,而只能使用标准输入进行交互,我们不能使用子进程模块,我们必须使用 pexpect 使用 scp 复制文件。

Feel free for corrections.

随意更正。

回答by Kenster

The OpenSSH scputility invokes the sshprogram to make the SSH connection to the remote host, and the ssh process handles authentication. The sshutility doesn't accept a password on the command line or on its standard input. I believe this is a deliberate decision on the part of the OpenSSH developers, because they feel that people should be using more secure mechanisms like key-based authentication. Any solution for invoking ssh is going to follow one of these approaches:

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

  1. Use an SSH keyfor authentication, instead of a password.
  2. Use sshpass, expect, or a similar tool to automate responding to the password prompt.
  3. Use (abuse) the SSH_ASKPASS feature to get sshto get the password by invoking another command, described hereor here, or in some of the answers here.
  4. Get the SSH server administrator to enable host-based authenticationand use that. Note that host-based authentication is only suitable for certain network environments. See additional notes hereand here.
  5. Write your own ssh client using perl, python, java, or your favorite language. There are ssh client libraries available for most modern programming languages, and you'd have full control over how the client gets the password.
  6. Download the ssh source codeand build a modified version of sshthat works the way you want.
  7. Use a different ssh client. There are other ssh clientsavailable, both free and commercial. One of them might suit your needs better than the OpenSSH client.
  1. 使用SSH 密钥而不是密码进行身份验证。
  2. 使用sshpassexpect或类似的工具来自动响应密码提示。
  3. 使用(滥用) SSH_ASKPASS 功能ssh通过调用另一个命令来获取密码,在此处此处此处的某些答案中进行了描述
  4. 让 SSH 服务器管理员启用基于主机的身份验证并使用它。请注意,基于主机的身份验证仅适用于某些网络环境。请参阅此处此处的附加说明。
  5. 使用 perl、python、java 或您喜欢的语言编写您自己的 ssh 客户端。有适用于大多数现代编程语言的 ssh 客户端库,您可以完全控制客户端如何获取密码。
  6. 下载ssh 源代码并构建一个ssh按照您想要的方式工作的修改版本。
  7. 使用不同的 ssh 客户端。还有其他 ssh 客户端可用,包括免费的和商业的。其中之一可能比 OpenSSH 客户端更适合您的需求。

In this particular case, given that you're already invoking scpfrom a python script, it seems that one of these would be the most reasonable approach:

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

  1. Use pexpect, the python expect module, to invoke scpand feed the password to it.
  2. Use paramiko, the python ssh implementation, to do this ssh task instead of invoking an outside program.
  1. 使用python expect 模块pexpect调用scp并向其提供密码。
  2. 使用python ssh 实现paramiko来执行此 ssh 任务,而不是调用外部程序。

回答by Ryan Y

Here is my scp function based on pexpect. It can handle wildcards (i.e. multiple file transfer), in addition to the password. To handle multiple file transfer (i.e. wildcards), we need to issue a command via a shell. Refer to pexpect FAQ.

这是我基于 pexpect 的 scp 函数。除了密码之外,它还可以处理通配符(即多文件传输)。要处理多个文件传输(即通配符),我们需要通过 shell 发出命令。请参阅pexpect 常见问题解答

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)

It can be used this way:

它可以这样使用:

params = {
    'src': '/home/src/*.txt',
    'user2': 'userName',
    'host2': '192.168.1.300',
    'tgt': '/home/userName/',
    'pwd': myPwd(),
    'opts': '',
}

scp(**params)