Verify host key with pysftp
Asked Answered
M

9

107

I am writing a program using pysftp, and it wants to verify the SSH host Key against C:\Users\JohnCalvin\.ssh\known_hosts.

Using PuTTY, the terminal program is saving it to the Registry [HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys].

How do I reconcile the difference between pysftp and PuTTY?

My code is:

import pysftp as sftp

def push_file_to_server():
    s = sftp.Connection(host='138.99.99.129', username='root', password='*********')
    local_path = "testme.txt"
    remote_path = "/home/testme.txt"

    s.put(local_path, remote_path)
    s.close()

push_file_to_server()

The error response I am receiving is:

E:\Program Files (x86)\Anaconda3\lib\site-packages\pysftp\__init__.py:61:
UserWarning: Failed to load HostKeys from C:\Users\JohnCalvin\.ssh\known_hosts.  You will need to explicitly load HostKeys (cnopts.hostkeys.load(filename)) or disableHostKey checking (cnopts.hostkeys = None).
 warnings.warn(wmsg, UserWarning)
Traceback (most recent call last): 
  File "E:\OneDrive\Python\GIT\DigitalCloud\pysftp_tutorial.py", line 14, in <module>
    push_file_to_server()
  File "E:\OneDrive\Python\GIT\DigitalCloud\pysftp_tutorial.py", line 7, in push_file_to_server
    s = sftp.Connection(host='138.99.99.129', username='root', password='********') 
  File "E:\Program Files (x86)\Anaconda3\lib\site-packages\pysftp\__init__.py", line 132, in __init__
    self._tconnect['hostkey'] = self._cnopts.get_hostkey(host)   
  File "E:\Program Files (x86)\Anaconda3\lib\site-packages\pysftp\__init__.py", line 71, in get_hostkey
    raise SSHException("No hostkey for host %s found." % host) paramiko.ssh_exception.SSHException: No hostkey for host 138.99.99.129 found.
Exception ignored in: <bound method Connection.__del__ of <pysftp.Connection object at 0x00000222FF3A6BE0>>
Traceback (most recent call last):
  File "E:\Program Files (x86)\Anaconda3\lib\site-packages\pysftp\__init__.py", line 1013, in __del__
    self.close()  
  File "E:\Program Files (x86)\Anaconda3\lib\site-packages\pysftp\__init__.py", line 784, in close
    if self._sftp_live:
AttributeError: 'Connection' object has no attribute '_sftp_live'
Moreen answered 14/8, 2016 at 5:30 Comment(1)
You can find the answer for your problem in pysftp documentation which mentions this issue explicitly here.Hagiography
A
151

The pysftp has some bugs regarding host key handling, as described below. It also seems that the pysftp project was abandoned. Consider using Paramiko directly instead. The pysftp is just a wrapper on top of Paramiko and it does not add anything really significant. See pysftp vs. Paramiko.

For handling of host keys in Paramiko, see:
Paramiko "Unknown Server"


If you want to keep using pysftp, do not set cnopts.hostkeys = None (as the second most upvoted answer shows), unless you do not care about security. You lose a protection against Man-in-the-middle attacks by doing so.

Use CnOpts.hostkeys (returns HostKeys) to manage trusted host keys.

cnopts = pysftp.CnOpts(knownhosts='known_hosts')

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

where the known_hosts contains a server public key(s) in a format like:

example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...

If you do not want to use an external file, you can also use

from base64 import decodebytes
# ...

keydata = b"""AAAAB3NzaC1yc2EAAAADAQAB..."""
key = paramiko.RSAKey(data=decodebytes(keydata))
cnopts = pysftp.CnOpts()
cnopts.hostkeys.add('example.com', 'ssh-rsa', key)

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

Though as of pysftp 0.2.9, this approach will issue a warning, what seems like a bug:
"Failed to load HostKeys" warning while connecting to SFTP server with pysftp


An easy way to retrieve the host key in the needed format is using OpenSSH ssh-keyscan:

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

(due to a bug in pysftp, this does not work, if the server uses non-standard port – the entry starts with [example.com]:port + beware of redirecting ssh-keyscan to a file in PowerShell)

You can also make the application do the same automatically:
Use Paramiko AutoAddPolicy with pysftp
(It will automatically add host keys of new hosts to known_hosts, but for known host keys, it will not accept a changed key)


Though for an absolute security, you should not retrieve the host key remotely, as you cannot be sure, if you are not being attacked already.

See my article Where do I get SSH host key fingerprint to authorize the server?
It's for my WinSCP SFTP client, but most information there is valid in general.


If you need to verify the host key using its fingerprint only, see Python - pysftp / paramiko - Verify host key using its fingerprint.

Arbela answered 13/4, 2017 at 10:14 Comment(2)
what should i use for the hostname param in cnopts.hostkeys.add() when it appears in known_hosts like |1|xyzxyzxyz=|abcabcabc=?Pampero
@Pampero Use tha hostname, which you use in pysftp.Connection.Arbela
W
107

One option is to disable the host key requirement:

import pysftp
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None   
with pysftp.Connection(host, username, password, cnopts=cnopts) as sftp:
    sftp.put(local_path, remote_path)

You can find more info about that here: https://mcmap.net/q/205152/-python-pysftp-error

Important note:

By setting cnopts.hostkeys=None you'll lose the protection against Man-in-the-middle attacks by doing so. Use @martin-prikryl answer to avoid that.

Winker answered 18/8, 2016 at 13:14 Comment(3)
Yes, this is how I eventually resolved my problem. But doing it this way only ignores the Host Key. I would like to be able to retain the security (if possible) by reading the actual key.Moreen
A quick note. CnOpts() will fail if you have an empty known_hosts file in path.Cree
See my answer for a solution.Arbela
K
21

Try to use the 0.2.8 version of pysftp library. $ pip uninstall pysftp && pip install pysftp==0.2.8

And try with this:

try:
    ftp = pysftp.Connection(host, username=user, password=password)
 except:
    print("Couldn't connect to ftp")
    return False

Why this? Basically is a bug with the 0.2.9 of pysftp here all details https://github.com/Yenthe666/auto_backup/issues/47

Kadner answered 23/7, 2018 at 15:28 Comment(1)
Could you elaborate a bit? I went through the link and didn't actually find any decent explanation why downgrading would help. While your statement "here all details" suggests that there should be some.Arbela
A
11

Cook book to use different ways of pysftp.CnOpts() and hostkeys options.

Source : https://pysftp.readthedocs.io/en/release_0.2.9/cookbook.html

Host Key checking is enabled by default. It will use ~/.ssh/known_hosts by default. If you wish to disable host key checking (NOT ADVISED) you will need to modify the default CnOpts and set the .hostkeys to None.

import pysftp
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None
with pysftp.Connection('host', username='me', password='pass', cnopts=cnopts):
    # do stuff here

To use a completely different known_hosts file, you can override CnOpts looking for ~/.ssh/known_hosts by specifying the file when instantiating.

import pysftp
cnopts = pysftp.CnOpts(knownhosts='path/to/your/knownhostsfile')

with pysftp.Connection('host', username='me', password='pass', cnopts=cnopts):
    # do stuff here

If you wish to use ~/.ssh/known_hosts but add additional known host keys you can merge with update additional known_host format files by using .load method.

import pysftp
cnopts = pysftp.CnOpts()
cnopts.hostkeys.load('path/to/your/extra_knownhosts')
with pysftp.Connection('host', username='me', password='pass', cnopts=cnopts):
    # do stuff here
Almemar answered 27/4, 2020 at 20:35 Comment(0)
Y
5

If You try to connect by pysftp to "normal" FTP You have to set hostkey to None.

import pysftp

cnopts = pysftp.CnOpts()
cnopts.hostkeys = None 
with pysftp.Connection(host='****',username='****',password='***',port=22,cnopts=cnopts) as sftp:
    print('DO SOMETHING')
Yetta answered 29/1, 2020 at 9:4 Comment(0)
P
2

Connect to the server first with a Windows ssh client that uses the known_hosts file. PuTTy stores the data in the windows registry,however OpenSSH uses the known_hosts file, and will add entries in there after you connect. Default location for the file is %USERPROFILE%.ssh. I hope this helps

Precondition answered 2/6, 2017 at 4:28 Comment(0)
T
1

I've implemented auto_add_key in my pysftp github fork.

auto_add_key will add the key to known_hosts if auto_add_key=True
Once a key is present for a host in known_hosts this key will be checked.

Please reffer Martin Prikryl -> answer about security concerns.

Though for an absolute security, you should not retrieve the host key remotely, as you cannot be sure, if you are not being attacked already.

import pysftp as sftp

def push_file_to_server():
    s = sftp.Connection(host='138.99.99.129', username='root', password='pass', auto_add_key=True)
    local_path = "testme.txt"
    remote_path = "/home/testme.txt"

    s.put(local_path, remote_path)
    s.close()

push_file_to_server()

Note: Why using context manager

import pysftp
with pysftp.Connection(host, username="whatever", password="whatever", auto_add_key=True) as sftp:
    #do your stuff here
#connection closed
Tridactyl answered 28/8, 2019 at 11:23 Comment(0)
S
0

Hi We sort of had the same problem if I understand you well. So check what pysftp version you're using. If it's the latest one which is 0.2.9 downgrade to 0.2.8. Check this out. https://github.com/Yenthe666/auto_backup/issues/47

Supervisory answered 11/1, 2017 at 14:56 Comment(1)
Do not suggest people to bypass a security feature, without explaining the consequences! You are losing a protection against MITM attacks.Arbela
A
-1

FWIR, if authentication is only username & pw, add remote server ip address to known_hosts like ssh-keyscan -H 192.168.1.162 >> ~/.ssh/known_hosts for ref https://www.techrepublic.com/article/how-to-easily-add-an-ssh-fingerprint-to-your-knownhosts-file-in-linux/

Acetone answered 21/1, 2020 at 23:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.