9

I'm trying to connect through SFTP to a remote server from Python (using Paramiko) to automate the retrieval of files.

System Versions: OS: Mac OS X Lion Python: 2.7.1 Paramiko: 1.7.7.2

My smallest example:

key_file = '/absolute/path/to/.ssh/id_rsa_key'  # NOT .pub
key_passphrase = 'id_rsa_key_passphrase'

host = 'ftp.test.com'
port = 22
username = 'my_ftp_username'
password = 'my_ftp_password'

# SSH Key
my_key = paramiko.RSAKey.from_private_key_file(key_file, password=key_passphrase)

# SFTP Connection
transport = paramiko.Transport((host, port))
transport.connect(username=username, password=password, pkey=my_key)
sftp = paramiko.SFTPClient.from_transport(transport)

# Print something
print sftp.listdir()

# Close connections
sftp.close()
transport.close()

The above generates the following log output:

DEB [20120606-16:20:46.121] thr=1   paramiko.transport: starting thread (client mode): 0x8ae7dd0L
INF [20120606-16:20:46.241] thr=1   paramiko.transport: Connected (version 2.0, client All)
DEB [20120606-16:20:46.242] thr=1   paramiko.transport: kex algos:['diffie-hellman-group1-sha1', 'diffie-hellman-group-exchange-sha1'] server key:['ssh-rsa'] client encrypt:['aes256-cbc', 'aes192-cbc'] server encrypt:['aes256-cbc', 'aes192-cbc'] client mac:['hmac-sha1', 'hmac-sha1-96'] server mac:['hmac-sha1', 'hmac-sha1-96'] client compress:['none'] server compress:['none'] client lang:[''] server lang:[''] kex follows?False
DEB [20120606-16:20:46.242] thr=1   paramiko.transport: Ciphers agreed: local=aes256-cbc, remote=aes256-cbc
DEB [20120606-16:20:46.242] thr=1   paramiko.transport: using kex diffie-hellman-group1-sha1; server key type ssh-rsa; cipher: local aes256-cbc, remote aes256-cbc; mac: local hmac-sha1, remote hmac-sha1; compression: local none, remote none
DEB [20120606-16:20:46.673] thr=1   paramiko.transport: Switch to new keys ...
DEB [20120606-16:20:46.706] thr=2   paramiko.transport: Attempting password auth...
DEB [20120606-16:20:47.112] thr=1   paramiko.transport: userauth is OK
INF [20120606-16:20:50.288] thr=1   paramiko.transport: Authentication continues...
DEB [20120606-16:20:50.288] thr=1   paramiko.transport: Methods: ['password', 'publickey']
DEB [20120606-16:20:50.305] thr=2   paramiko.transport: [chan 1] Max packet in: 34816 bytes
WAR [20120606-16:20:50.405] thr=1   paramiko.transport: Oops, unhandled type 3
INF [20120606-16:23:53.582] thr=1   paramiko.transport: Disconnect (code 11): Idle connection

Does anyone know what "Oops, unhandled type 3" in the log means? That seems to be when the whole thing falls apart. Alternatively, if anyone sees something I'm doing terribly wrong in the code that would be helpful as well.

ystan-
  • 1,474
  • 1
  • 19
  • 43
rebekswr
  • 93
  • 1
  • 1
  • 3
  • This (unfixed) bug appears to cover the issue you're seeing: https://github.com/paramiko/paramiko/issues/519 – Symmetric Feb 27 '16 at 00:21

4 Answers4

2

I realize that this question is nearly 4 years old, however I hit this same problem and found a working solution!

Referencing the doc: http://docs.paramiko.org/en/2.4/api/transport.html

In the docs, under the connect() method it mentions that:

This is a shortcut for start_client, get_remote_server_key, and Transport.auth_password or Transport.auth_publickey.

So you simply cannot use connect() if you need to do multi-factor auth, you have to negotiate it manually using the above mentioned methods and trigger BOTH authentications, using key auth first before password auth.

The below snippet works for me!

host = "some-host"
port = 22
sftp_key = "/some-key"
username = "some-user"
password = "some-pass"

sftp_key = paramiko.RSAKey.from_private_key_file(sftp_key)
transport = paramiko.Transport((host, port))
transport.start_client(event=None, timeout=15)
transport.get_remote_server_key()
transport.auth_publickey(username, sftp_key, event=None)
transport.auth_password(username, password, event=None)
sftp = paramiko.SFTPClient.from_transport(transport) 

And wrapped that whole thing in a function to handle pretty much any SFTP server you can throw at it. My error handling is omitted since it doesn't make sense here...

#Auth types: user_pass, key_only, key_and_pass
#You can pass a junk string in for password or sftp_key if not used
def connect_to_sftp(host, port, username, password, sftp_key, auth_type):
    try:
        transport = paramiko.Transport((host, port))
        if auth_type == "key_and_pass":
            sftp_key = paramiko.RSAKey.from_private_key_file(sftp_key)
            transport.start_client(event=None, timeout=15)
            transport.get_remote_server_key()
            transport.auth_publickey(username, sftp_key, event=None)
            transport.auth_password(username, password, event=None)
            #transport.connect(username = username, password = password, pkey = sftp_key)
        elif auth_type == "key_only":
            sftp_key = paramiko.RSAKey.from_private_key_file(sftp_key)
            transport.connect(username = username, pkey = sftp_key)
        elif auth_type == "user_pass":
            transport.connect(username = username, password = password)
        else:
            ## Do your own error handling :)
            print "uh-oh!"
        sftp = paramiko.SFTPClient.from_transport(transport)   
    except Exception, e:
        ## Do your own error handling :)
        print "uh-oh!"
    return sftp, transport
emmdee
  • 1,541
  • 3
  • 25
  • 46
  • Paramiko can handle two factor key and password authentication on its own, if you correctly use its high-level API, the `SSHClient`. See my answer to [Multi-factor authentication (password and key) with Paramiko](https://stackoverflow.com/q/28837089/850848#68949359). + Moreover using the low-level `Transport` class the way the above answer shows bypasses host key verification, what makes the connection insecure. – Martin Prikryl Sep 07 '21 at 11:00
1

You are using both password and key authentication to the server, which looks like a conflict. Try using the Transport.auth_publickey method to connect with. If it fails, the docs say to use the transport.get_exception function to collect more details.

Edit:

According to this, multifactor authentication means using both a password and a key. So, you should be able to use the auth_publickey function, followed by the auth_password method to get in.

Or, if you have sufficient access, you can adjust the ftp settings to only require key authentication.

Have you tried logging in using an ftp client, such as filezilla?

Spencer Rathbun
  • 14,510
  • 6
  • 54
  • 73
  • Interesting. When I try the path you suggested I see an array returned from auth_publickey. That array is ['password', 'publickey']. When I then go to look at the docs you linked (thank you) I see that the returned data is a "list of auth types permissible for the next stage of authentication (normally empty)". You wouldn't happen to know where documentation on the multi-stage authentication process is, or even an example/tutorial, would you? – rebekswr Jun 06 '12 at 21:55
  • @rebekswr ok, I've updated my answer with some stuff on multi-stage authentication. – Spencer Rathbun Jun 07 '12 at 12:46
  • I tried both calls, in both possible orders and they both always gave the same output as each other and when I called is_authenticated afterward I never was. I'm moving on to calling sftp from a command line since I don't need it to be portable. Thanks so much for your help. – rebekswr Jun 07 '12 at 22:19
0

The issue is coming from the fact that you using a rsa key format (key_file = '/absolute/path/to/.ssh/id_rsa_key').

Make sure you convert it into a SSH RSA using putty Key generator. I did and use the same code: It is working.

Markus Weninger
  • 11,931
  • 7
  • 64
  • 137
0

Big Sam eventually experienced this error after opening many connections within a for-loop and never closing the connections.

The exact error message was Oops, unhandled type 3 ('unimplemented')

You should always make sure that if you have:

client.connect(...)

You also have:

client.close()
Big Sam
  • 1,290
  • 14
  • 7