web-dev-qa-db-ja.com

SSHまたはSCPを介してサブプロセスでパスワードを送信します。

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()

これはすぐにエラーを返します:

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

私は確かですパスワードは正しいです。シェルでscpを手動で呼び出すことで簡単に確認できます。では、なぜこれが機能しないのですか?

subprocess.Popenについて質問し、自動化されたSSHまたはFTPログインのパスワードを送信することについて、これと同様の多くの質問があることに注意してください。

pythonスクリプト)からLinuxでユーザーパスワードを設定するにはどうすればよいですか?
サブプロセスを使用してパスワードを送信する

Python 3.を使用しているため、これらの質問に対する回答が機能しないか、適用されません。

11
Channel72

リンクした2番目の回答は、Pexpectを使用することを提案しています(これは通常、入力を期待するコマンドラインプログラムと対話するための正しい方法です)。 fork があり、使用できるpython3で動作します。

7
entropy

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

scpを使用すると、同様のことが可能になるはずです。

12
sjbx

OpenSSH scpユーティリティはsshプログラムを呼び出してリモートホストへのSSH接続を確立し、sshプロセスが認証を処理します。 sshユーティリティは、コマンドラインまたはその標準入力でパスワードを受け入れません。これはOpenSSH開発者側の意図的な決定だと思います。なぜなら、人々はキーベースの認証のようなより安全なメカニズムを使用すべきだと感じているからです。 sshを呼び出すためのソリューションはすべて、次のいずれかのアプローチに従います。

  1. パスワードではなく、認証に SSH key を使用します。
  2. sshpassexpect 、または同様のツールを使用して、パスワードプロンプトへの応答を自動化します。
  3. SSH_ASKPASS機能を使用して(乱用して)sshを取得し、 here または here で説明されている別のコマンドを呼び出すことにより、または一部の回答でパスワードを取得します- ここ
  4. SSHサーバー管理者に ホストベースの認証 を有効にしてもらい、それを使用します。ホストベースの認証は、特定のネットワーク環境にのみ適していることに注意してください。追加の注記 ここ および ここ を参照してください。
  5. Perl、Python、Java、またはお気に入りの言語を使用して、独自のSSHクライアントを作成します。最新のプログラミング言語で使用できるsshクライアントライブラリがあり、クライアントがパスワードを取得する方法を完全に制御できます。
  6. sshソースコード をダウンロードし、sshの修正バージョンをビルドして、希望どおりに動作させます。
  7. 別のSSHクライアントを使用してください。 その他のsshクライアント があり、無料と商用の両方があります。それらの1つは、OpenSSHクライアントよりもニーズに適している可能性があります。

この特定のケースでは、pythonスクリプトからscpをすでに呼び出していることを考えると、これらの1つが最も合理的なアプローチであると思われます。

  1. pexpect 、python expectモジュール)を使用してscpを呼び出し、パスワードをそれに渡します。
  2. paramiko 、python ssh実装)を使用して、外部プログラムを呼び出す代わりにこのsshタスクを実行します。
8
Kenster

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
Tommy

一部のアプリケーションはstdinを使用してユーザーと対話し、一部のアプリケーションは端末を使用して対話すると思います。この場合、PIPEを使用してパスワードを書き込むときは、stdinに書き込みます。しかし、SCPアプリケーションは端末からパスワードを読み取ります。サブプロセスはターミナルを使用してユーザーと対話することはできませんが、stdinを使用してのみ対話することができるため、サブプロセスモジュールを使用することはできず、scpを使用してファイルをコピーするためにpexpectを使用する必要があります。

自由に修正してください。

1
Raju Pitta

これが、pexpectに基づく私のscp関数です。パスワードに加えて、ワイルドカード(つまり、複数のファイル転送)を処理できます。複数のファイル転送(つまり、ワイルドカード)を処理するには、シェルを介してコマンドを発行する必要があります。 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)
1
Ryan Y