Search code examples
pythonwindowssshparamikoscp

Problems using Python to SCP transfer files on Windows using os, Popen, and Paramiko


I am trying to get a Python script to SCP transfer a file to a SSH server to which I have root access and no password. I am on Windows and running;

scp test.txt root@<ip-address>:/data

works like a charm.

I have tried several different approaches to get Python to do this, but I run into trouble no matter what:

  1. With os:

    import os
    my_scp = r'C:\Windows\System32\OpenSSH\scp.exe'
    os.system(my_scp + ' test.txt root@<ip-address>:/data')
    

    I get:

    The specified path not found (translated so wording may be different)

  2. With Popen:

    import subprocess
    p = subprocess.Popen(['scp', 'test.txt', 'root@<ip-address>:/data'])
    sts = os.waitpid(p.pid, 0)`
    

    I get:

    FileNotFoundError: [WinError 2] The specified file was not found (again, translated)

    I have tried pointing to OpenSSH\scp.exe in a number of ways but to no avail.

  3. With Paramiko:

    from paramiko import SSHClient
    from scp import SCPClient
    ssh = SSHClient()
    ssh.load_system_host_keys()
    ssh.connect('root@<ip-address>:data')
    with SCPClient(ssh.get_transport()) as scp:
        scp.put('test.txt', 'test.txt')`
    

    I get:

    Traceback (most recent call last):
      File ".\my_script.py", line 6, in <module>
        ssh.connect('root@<ip-address>:data')
      File "C:\Users\myself\AppData\Local\Programs\Python\Python37-32\lib\site-packages\paramiko\client.py", line 334, in connect
        to_try = list(self._families_and_addresses(hostname, port))
      File "C:\Users\myself\AppData\Local\Programs\Python\Python37-32\lib\site-packages\paramiko\client.py", line 204, in _families_and_addresses
        hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM
      File "C:\Users\myself\AppData\Local\Programs\Python\Python37-32\lib\socket.py", line 748, in getaddrinfo
        for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
    socket.gaierror: [Errno 11003] getaddrinfo failed
    

I am running Python 3.7.3 in Windows 10.


Update - running scp -v:

PS C:\scp_test> scp -v test.txt [email protected]:/data
Executing: program ssh.exe host 169.254.108.26, user root, command scp -v -t /data
OpenSSH_for_Windows_7.6p1, LibreSSL 2.6.4
debug1: Connecting to 169.254.108.26 [169.254.108.26] port 22.
debug1: Connection established.
debug1: key_load_public: No such file or directory
debug1: identity file C:\\Users\\myself/.ssh/id_rsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file C:\\Users\\myself/.ssh/id_rsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file C:\\Users\\myself/.ssh/id_dsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file C:\\Users\\myself/.ssh/id_dsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file C:\\Users\\myself/.ssh/id_ecdsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file C:\\Users\\myself/.ssh/id_ecdsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file C:\\Users\\myself/.ssh/id_ed25519 type -1
debug1: key_load_public: No such file or directory
debug1: identity file C:\\Users\\myself/.ssh/id_ed25519-cert type -1
debug1: Local version string SSH-2.0-OpenSSH_for_Windows_7.6
debug1: Remote protocol version 2.0, remote software version dropbear_2017.75
debug1: no match: dropbear_2017.75
debug1: Authenticating to 169.254.108.26:22 as 'root'
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: [email protected]
debug1: kex: host key algorithm: ecdsa-sha2-nistp521
debug1: kex: server->client cipher: aes128-ctr MAC: hmac-sha2-256 compression: none
debug1: kex: client->server cipher: aes128-ctr MAC: hmac-sha2-256 compression: none
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: Server host key: ecdsa-sha2-nistp521 SHA256:RNaIuHs4U+5p8kQrcB+0pwCoKab3j6DNCk5hShNzpj4
debug1: Host '169.254.108.26' is known and matches the ECDSA host key.
debug1: Found key in C:\\Users\\myself/.ssh/known_hosts:4
debug1: rekey after 4294967296 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug1: SSH2_MSG_NEWKEYS received
debug1: rekey after 4294967296 blocks
debug1: pubkey_prepare: ssh_get_authentication_socket: No such file or directory
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentication succeeded (none).
Authenticated to 169.254.108.26 ([169.254.108.26]:22).
debug1: channel 0: new [client-session]
debug1: Entering interactive session.
debug1: pledge: network
debug1: Sending command: scp -v -t /data
Sending file modes: C0666 5430 test.txt
Sink: C0666 5430 test.txt
test.txt
100% 5430     5.3KB/s   00:00
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug1: channel 0: free: client-session, nchannels 1
Transferred: sent 7080, received 1376 bytes, in -7.0 seconds
debug1: Exit status 0

Update II: I got it running by re-installing Python as 64-bit and using os to call OpenSSH.


Solution

  • You are using Paramiko SSHClient.connect incorrectly.

    The first argument of SSHClient.connect is hostname (what can be also IP address as in your case). No username or anything else can be there. Username goes to username argument. The destination path (which has nothing to do with SSH connection) goes only to SCPClient.put.

    This should work:

    ssh = SSHClient()
    ssh.load_system_host_keys()
    ssh.connect('<ip-address>', username='root')
    with SCPClient(ssh.get_transport()) as scp:
        scp.put('test.txt', '/data/test.txt')
    

    Regarding your attempts to use scp: Just a wild guess, but I assume you use Win32-OpenSSH build of OpenSSH. It comes in 64-bit version only. If you use 32-bit Python, it cannot find 64-bit OpenSSH tools, like scp, because they are in 64-bit version of C:\Windows\System32. You can access it via magic name C:\Windows\sysnative\OpenSSH\sftp. Read about File System Redirector. Though you should not run a console application to implement SCP. Use native Python SCP implementation, like SCPClient.