Отправка пароля через SSH или SCP с помощью подпроцесса. Открыть

Я пытаюсь запустить 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.

Я уверен, что пароль правильный. Я легко проверяю это вручную scp на оболочке. Так почему это не работает?

Обратите внимание, что есть много подобных вопросов, задающих subprocess.Popen и отправив пароль для автоматического входа по SSH или FTP:

Как я могу установить пароль пользователя в Linux из скрипта Python?
Используйте подпроцесс для отправки пароля

Ответы на эти вопросы не работают и / или не применяются, потому что я использую Python 3.

7 ответов

Решение

Второй ответ, который вы связали, предполагает, что вы используете Pexpect(который обычно является правильным способом взаимодействия с программами командной строки, ожидающими ввода). Есть форк этого, который работает для python3, который вы можете использовать.

Вот функция для ssh с помощью пароля с помощью 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)                                                                                                                            
    child.expect(['password: '])                                                                                                                                                                                                                                                                                               
    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,

OpenSSH scp Утилита вызывает ssh программа для установки SSH-соединения с удаленным хостом, а процесс ssh выполняет аутентификацию. ssh Утилита не принимает пароль в командной строке или при стандартном вводе. Я считаю, что это осознанное решение со стороны разработчиков OpenSSH, потому что они считают, что люди должны использовать более безопасные механизмы, такие как аутентификация на основе ключей. Любое решение для вызова ssh будет следовать одному из следующих подходов:

  1. Используйте ключ SSH для аутентификации вместо пароля.
  2. Используйте sshpass, ожидайте или аналогичный инструмент для автоматизации ответа на запрос пароля.
  3. Используйте (злоупотребляйте) функцию SSH_ASKPASS, чтобы получить ssh чтобы получить пароль, вызвав другую команду, описанную здесь или здесь, или в некоторых ответах здесь.
  4. Попросите администратора сервера SSH включить аутентификацию на основе хоста и использовать ее. Обратите внимание, что аутентификация на основе хоста подходит только для определенных сетевых сред. Смотрите дополнительные заметки здесь и здесь.
  5. Напишите свой собственный ssh-клиент, используя perl, python, java или ваш любимый язык. Для большинства современных языков программирования доступны клиентские библиотеки ssh, и вы сможете полностью контролировать, как клиент получает пароль.
  6. Загрузите исходный код ssh и создайте модифицированную версию ssh это работает так, как вы хотите.
  7. Используйте другой клиент SSH. Доступны и другие ssh-клиенты, как бесплатные, так и коммерческие. Один из них может удовлетворить ваши потребности лучше, чем клиент OpenSSH.

В данном конкретном случае, учитывая, что вы уже вызываете scp Из сценария Python кажется, что один из них будет наиболее разумным подходом:

  1. Используйте pexpect, модуль ожидания Python, чтобы вызвать scp и введите пароль к нему.
  2. Используйте paramiko, реализацию Python SSH, чтобы выполнить эту задачу SSH вместо вызова внешней программы.

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

Я предполагаю, что некоторые приложения взаимодействуют с пользователем с помощью stdin, а некоторые приложения взаимодействуют с помощью терминала. В этом случае, когда мы пишем пароль с помощью PIPE, мы пишем в stdin. Но приложение SCP считывает пароль с терминала. Поскольку подпроцесс не может взаимодействовать с пользователем с помощью терминала, но может взаимодействовать только с использованием stdin, мы не можем использовать модуль подпроцесса, и мы должны использовать pexpect для копирования файла с использованием scp.

Не стесняйтесь исправлений.

Вот моя функция scp, основанная на pexpect. Он может обрабатывать символы подстановки (т.е. многократную передачу файлов), в дополнение к паролю. Для обработки множественной передачи файлов (т.е. подстановочных знаков) нам нужно выполнить команду через оболочку. Обратитесь к 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)

Это переписывание, которое я сделал из кода, опубликованного @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
Другие вопросы по тегам