web-dev-qa-db-fra.com

Envoi d'un mot de passe via SSH ou SCP avec sous-processus.

J'essaie d'exécuter une commande scp (copie sécurisée) en utilisant subprocess.Popen. La connexion nécessite que j'envoie un mot de passe:

from subprocess import Popen, PIPE

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

Cela renvoie immédiatement une erreur:

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

Je suis certain le mot de passe est correct. Je le vérifie facilement en invoquant manuellement scp sur le shell. Alors pourquoi ça ne marche pas?

Remarque, il existe de nombreuses questions similaires à cela, concernant subprocess.Popen et envoi d'un mot de passe pour une connexion SSH ou FTP automatisée:

Comment puis-je définir un mot de passe utilisateur sous linux à partir d'un script python?
tiliser un sous-processus pour envoyer un mot de passe

La réponse à ces questions ne fonctionne pas et/ou ne s'applique pas car j'utilise Python 3.

11
Channel72

La deuxième réponse que vous avez liée suggère d'utiliser Pexpect (qui est généralement la bonne façon d'interagir avec les programmes de ligne de commande qui attendent une entrée). Il y en a fork qui fonctionne pour python3 que vous pouvez utiliser.

7
entropy

Voici une fonction pour ssh avec un mot de passe utilisant 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

Quelque chose de similaire devrait être possible en utilisant scp.

12
sjbx

L'utilitaire OpenSSH scp appelle le programme ssh pour établir la connexion SSH avec l'hôte distant et le processus ssh gère l'authentification. L'utilitaire ssh n'accepte pas de mot de passe sur la ligne de commande ou sur son entrée standard. Je pense que c'est une décision délibérée de la part des développeurs d'OpenSSH, car ils estiment que les gens devraient utiliser des mécanismes plus sécurisés comme l'authentification par clé. Toute solution pour appeler ssh va suivre l'une de ces approches:

  1. Utilisez un clé SSH pour l'authentification, au lieu d'un mot de passe.
  2. Utilisez sshpass , expect , ou un outil similaire pour automatiser la réponse à l'invite de mot de passe.
  3. Utilisez (abusez) la fonction SSH_ASKPASS pour obtenir ssh pour obtenir le mot de passe en appelant une autre commande, décrite ici ou ici , ou dans certaines des réponses - ici .
  4. Demandez à l'administrateur du serveur SSH d'activer authentification basée sur l'hôte et utilisez-le. Notez que l'authentification basée sur l'hôte ne convient qu'à certains environnements réseau. Voir les notes supplémentaires ici et ici .
  5. Écrivez votre propre client ssh en utilisant Perl, python, Java ou votre langue préférée. Il existe des bibliothèques client ssh disponibles pour la plupart des langages de programmation modernes, et vous auriez un contrôle total sur la façon dont le client obtient le mot de passe.
  6. Téléchargez le code source ssh et créez une version modifiée de ssh qui fonctionne comme vous le souhaitez.
  7. Utilisez un autre client ssh. Il y a autres clients ssh disponibles, à la fois gratuits et commerciaux. L'un d'eux pourrait mieux répondre à vos besoins que le client OpenSSH.

Dans ce cas particulier, étant donné que vous invoquez déjà scp à partir d'un script python, il semble que l'une d'entre elles serait l'approche la plus raisonnable:

  1. Utilisez pexpect , le module python expect, pour appeler scp et lui fournir le mot de passe.
  2. Utilisez paramiko , l'implémentation python ssh) pour effectuer cette tâche ssh au lieu d'invoquer un programme externe.
8
Kenster

Pexpect dispose d'une bibliothèque pour cela: 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

Je suppose que certaines applications interagissent avec l'utilisateur à l'aide de stdin et certaines applications interagissent à l'aide de terminal. Dans ce cas, lorsque nous écrivons le mot de passe à l'aide de PIPE, nous écrivons sur stdin. Mais l'application SCP lit le mot de passe du terminal. Comme le sous-processus ne peut pas interagir avec l'utilisateur à l'aide du terminal mais ne peut interagir qu'avec stdin, nous ne pouvons pas utiliser le module de sous-processus et nous devons utiliser pexpect pour copier le fichier à l'aide de scp.

N'hésitez pas à faire des corrections.

1
Raju Pitta

Voici ma fonction scp basée sur pexpect. Il peut gérer les caractères génériques (c'est-à-dire le transfert de fichiers multiples), en plus du mot de passe. Pour gérer plusieurs transferts de fichiers (c'est-à-dire des caractères génériques), nous devons émettre une commande via un shell. Reportez-vous à 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)

Il peut être utilisé de cette façon:

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

scp(**params)
1
Ryan Y