5

I'm trying to connect to a remote host via pysftp:

try:
    with pysftp.Connection(inventory[0], username='transit',
                           private_key='~/.ssh/id_rsa.sftp', port=8055) as sftp:
        sftp.put('/home/me/test.file')
except Exception, err:
    print sys.exc_info()
    print err

However, I get a weird exception raised that I can't find much detail on.

(<class 'paramiko.ssh_exception.SSHException'>, SSHException('Bad host key from server',), <traceback object at 0x7fa76269c5a8>)
Bad host key from server

I found a related question that suggested I run ssh-keygen -R [host] to replace the key in my known_hosts file -- once I did that, I got a new error:

(<class 'paramiko.ssh_exception.SSHException'>, SSHException('No hostkey for host abc.com found.',), <traceback object at 0x7f6b8520cb48>)
No hostkey for host abc.com found.

Now, if I try to ssh to this host, it prompts me again to trust the key to be put back into my known_hosts file, which I accept and can happily ssh onto the box. After this step, if I attempt to run my script again, I get the original error Bad host key from server.

Am I doing something wrong here?

ninja edit: I should mention that the following works fine from the terminal:

sftp -i .ssh/id_rsa.sftp -oPort=8055 user@host.com
sftp> put /home/me/test.file

Paramiko Debug Output:

[2016-07-14 10:32:23,809] | paramiko.transport - DEBUG - starting thread (client mode): 0x7f1dab10L
[2016-07-14 10:32:23,810] | paramiko.transport - DEBUG - Local version/idstring: SSH-2.0-paramiko_2.0.1
[2016-07-14 10:32:23,836] | paramiko.transport - DEBUG - Remote version/idstring: SSH-2.0-mod_sftp/0.9.9
[2016-07-14 10:32:23,836] | paramiko.transport - INFO - Connected (version 2.0, client mod_sftp/0.9.9)
[2016-07-14 10:32:23,837] | paramiko.transport - DEBUG - kex algos:[u'ecdh-sha2-nistp256', u'ecdh-sha2-nistp384', u'ecdh-sha2-nistp521', u'diffie-hellman-group-exchange-sha256', u'diffie-hellman-group-exchange-sha1', u'diffie-hellman-group14-sha1', u'diffie-hellman-group1-sha1', u'rsa1024-sha1'] server key:[u'ssh-rsa', u'ssh-dss'] client encrypt:[u'aes256-ctr', u'aes192-ctr', u'aes128-ctr', u'aes256-cbc', u'aes192-cbc', u'aes128-cbc', u'blowfish-ctr', u'blowfish-cbc', u'cast128-cbc', u'arcfour256', u'arcfour128', u'3des-ctr', u'3des-cbc'] server encrypt:[u'aes256-ctr', u'aes192-ctr', u'aes128-ctr', u'aes256-cbc', u'aes192-cbc', u'aes128-cbc', u'blowfish-ctr', u'blowfish-cbc', u'cast128-cbc', u'arcfour256', u'arcfour128', u'3des-ctr', u'3des-cbc'] client mac:[u'hmac-sha2-256', u'hmac-sha2-512', u'hmac-sha1', u'hmac-sha1-96', u'hmac-md5', u'hmac-md5-96', u'hmac-ripemd160', u'umac-64@openssh.com'] server mac:[u'hmac-sha2-256', u'hmac-sha2-512', u'hmac-sha1', u'hmac-sha1-96', u'hmac-md5', u'hmac-md5-96', u'hmac-ripemd160', u'umac-64@openssh.com'] client compress:[u'none'] server compress:[u'none'] client lang:[u''] server lang:[u''] kex follows?False
[2016-07-14 10:32:23,837] | paramiko.transport - DEBUG - Kex agreed: diffie-hellman-group1-sha1
[2016-07-14 10:32:23,838] | paramiko.transport - DEBUG - Cipher agreed: aes128-ctr
[2016-07-14 10:32:23,838] | paramiko.transport - DEBUG - MAC agreed: hmac-sha2-256
[2016-07-14 10:32:23,838] | paramiko.transport - DEBUG - Compression agreed: none
[2016-07-14 10:32:23,935] | paramiko.transport - DEBUG - kex engine KexGroup1 specified hash_algo <built-in function openssl_sha1>
[2016-07-14 10:32:23,936] | paramiko.transport - DEBUG - Switch to new keys ...
[2016-07-14 10:32:23,952] | paramiko.transport - DEBUG - Bad host key from server
[2016-07-14 10:32:23,952] | paramiko.transport - DEBUG - Expected: ssh-rsa: '\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x01#\x00\x00\x01\x01\x00\xb9\x05\xddUF?o\xeet\xad\x06=\x83\xd6\xa8\xb1\x06\x03\xb7\xb5\xa5C\x80\x0c~\xa8\x83[w!\x16y\xe6@\xdajX\x90+c\xd4\x18X\xd7\xff\x05\x06\xe2\x19\x96\x8c&J\x0c\x1f\xd2\xefC\xa5\xc6\xbb\x1f\xc9N\xd7r\x12\xd29\x1f\xe1\xb6F\xe8\xdc(w~\x1f\xea\xea\xfdUT?^V\xe8=\'\xd60\xdc\xc44gYnn\xfe\xe8^?\x8b\xa8"\xda\xc7(\x18\xc3<\xa9\x1c\xa1)A3_\x00<$y\xbe\xd4$\xc2S\xe6\x93\xe3B^\xef\xe6\xbf\xb5\x88\xd6\x98EeP\x9aZm\xa7\xc5&\xffn\xc1lb\x1e[\x8d!*\xfa\xa3\xce\xb6\xe7\xd5U\r<E\xd2\xe1\x89.\xed%o\xefju\xde>=*b\xaeBO\xbf>\xe4/,\xd5@R\xc5\x9e\xac\xf5\x80\x04\xa9s)3\xb5\xbc\xc2\xcc\xe7\xa7\xb8\x04>uA\xb4O\xa6\xf3\xdb\x9c2\xb5"%n\x89\t\x8192\xe7#n\x82p\xee\xdf\x16]\xb3A\x93\xebm|\xb9\xd7\nXsmw\x81\xa9'
[2016-07-14 10:32:23,953] | paramiko.transport - DEBUG - Got     : ssh-rsa: "\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x01#\x00\x00\x01\x01\x00\xc6_\xea\xc1yF\xd5\xdd\x146\x8c\x0eU\xeah[3\x16VP\xde2\xbe~\xed\x92\xef\xb81\x14\xd8\xb7\xa7\x08\xa3\x02\xa8\n\x9f\x7f{\x896\x1b\x89/,\x92\x86\xc7M\xa5\x7f0Cj\xe6\x93\xb0\x0eup%\xa2p\x9d3i\xaco\xf7\x14\xfb\xfe\x8f}a\xcbJ :\xdf\xdd1\x10\xe8;\xd3%\x98k')\xf8\x1c\xc1D\x97\\\xcc\xe6\xf5*6\xc2\xf1\xb8\xcf!\xeed\x1co\xc1\x03\xb4v\xc0,?\xda\xc7\xa3\xd9\xe8\xafy\xf5k\xf7\xe8\xa1\x9cr\xfa\x81\xcd\xee\xd3\xeao\xa7\x072\xce\x8b\xf9\x95\x03\xa1\xe2\xaf\t\xa2\xba\xa1O\xbc\xbf}}\x9e\xc0t\x9bC\x88\xbd\x18'\x00?-\xa0\x05\x83I\x1ah#I\xfc\xebc$\xfc\xa0\xad\xeeP\x80\x88Bh_\xeb`\xb6\xab\x8bC\x83\x987\xba\x05\xb14\xea\x90\xac>\x99&\x99\xb3\xf9\nC\xdf\xfd\xba<\xf8\xaf\xca\xaf\xfa\x15+\x1e,L\xa1\xf0\x1e\xa5`\xbd>\xf4\xa63)\xae7F\xd2\xc9\xf2\xf3A\xa4\x10u\xa9"
(<class 'paramiko.ssh_exception.SSHException'>, SSHException('Bad host key from server',), <traceback object at 0x7fb87f143ef0>)
Bad host key from server
[2016-07-14 10:32:24,037] | paramiko.transport - DEBUG - EOF in transport thread
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
MrDuk
  • 16,578
  • 18
  • 74
  • 133

4 Answers4

5

Workaround
This seems to be some sort of bug with the pysftp wrapper... I'm not sure. Reverting to native paramiko got me connected just fine, so I'm going with that for now. Current working code:

rsa_key = paramiko.RSAKey.from_private_key_file('~/.ssh/the_key', password='myPassword')
transport = paramiko.Transport((inventory[0],8055))
transport.connect(username='theUser', pkey=rsa_key)
sftp = paramiko.SFTPClient.from_transport(transport)
print sftp.listdir()
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
MrDuk
  • 16,578
  • 18
  • 74
  • 133
  • I do not think it's a bug in pysftp. It's rather that your `known_hosts` file contains a wrong host key. So pysftp fails, because the actual host key differs from the `known_hosts` file. Using `Transport` the way you do, you bypass the problem by completely avoiding the host key verification, what is a security flaw. Your code is susceptible to [MITM attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). For a correct solution, see [Verify host key with pysftp](https://stackoverflow.com/q/38939454/850848) or the answers by @Erick and @Bohdan here. – Martin Prikryl Oct 07 '21 at 05:25
4

For me, once I had the host public key loaded in .ssh\known_hosts, the cookbook example worked.

But, I don't recall being prompted to ever save the host public key to my .ssh\known_hosts file.

This link is what helped me understand the host public key was separate from my own public/private key pair. I'm using exavault and they provide the public/private key pair, but did not provide the host public key.

I used ssh-keyscan using git bash for windows:

$ ssh-keyscan example.com
# example.com SSH-2.0-OpenSSH_5.3
example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...

and copied the key to my known_hosts file. Then it worked with pysftp 0.2.9.

Erik Iverson
  • 309
  • 1
  • 11
1

I've had a similar problem and for me the issue was in that host ssh key was changed, and my known_hosts file was containing the old one, so to fix it i've run:

ssh-keygen -f /Users/myUserName/.ssh/known_hosts -R myhost.com

Obviously you'll need to use path yo your known_hosts file and your hostname or IP.

Bohdan
  • 23
  • 4
1

The problem is port based and it is a bug.

First Option

I fixed it in a fork which you can find over here. Essentially for ports other than 22 the query-language of host-lookup is done as such:

[host]:port

For instance, from the command-line:

$ ssh-keygen -H -F "[host]:port"

So that means use something like this in your requirements.txt if you want the patched version:

git+https://github.com/kristopolous/pysftp.git

Second Option

If you want to just move on with life and not use this patch then there's another way.

Grab the keys you want via ssh-keyscan like so

$ ssh-keyscan -p port host > magical-known-hosts

Now if you open that up you'll see something like this:

$ cat magical-known-hosts
[host]:port ecdsa-sha2-nistp256 AAAA...
[host]:port ssh-rsa AAAAA...
[host]:port ssh-ed25519 AAAA...

Well that's the syntax the library doesn't support so we swap that out.

$ sed -i 's/\[host\]:port/host/' magical-known-hosts

Now we are going to override an object in pysftp to use this file instead:

import pysftp
import paramiko

cnopts = pysftp.CnOpts()
cnopts.hostkeys = paramiko.hostkeys.HostKeys('/path/to/magical-known-hosts')

with pysftp.Connection(host, cnopts=cnopts, port=port) as sftp:
  ...

And there you go. No need to pull down code from some random person on stack overflow if you don't want to. Feel free to use either solution.

kristopolous
  • 1,768
  • 2
  • 16
  • 24
  • To anyone else reading this; this should be the accepted answer if you are using a non-standard port. I've read the change in the fork mentioned, and tried out the package. thanks @kristopolous – mp035 Feb 27 '23 at 01:29