Проверьте ключ хоста с помощью pysftp

Я пишу программу, использующую pysftp, и она хочет проверить ключ хоста SSH на соответствие C:\Users\JohnCalvin\.ssh\known_hosts.

Используя PuTTY, программа терминала сохраняет его в реестр [HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys].

Как согласовать разницу между pysftp и PuTTY?

Мой код:

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()

Я получаю следующий ответ об ошибке:

E: \ Program Files (x86) \ Anaconda3 \ lib \ site-packages \ pysftp__init __. Py: 61: UserWarning:
Не удалось загрузить HostKeys из C: \ Users \ JohnCalvin.ssh \ known_hosts.
Вам понадобится для явной загрузки HostKeys (cnopts.hostkeys.load (filename)) или отключения проверки HostKey (cnopts.hostkeys = None). warnings.warn (wmsg, UserWarning) Отслеживание (последний вызов последним): файл «E: \ OneDrive \ Python \ GIT \ DigitalCloud \ pysftp_tutorial.py», строка 14, в файле push_file_to_server () «E: \ OneDrive \ Python \ GIT \ DigitalCloud \ pysftp_tutorial.py ", строка 7, в push_file_to_server s = sftp.Connection (host = '138.99.99.129', username = 'root', password = '********') Файл" E : \ Program Files (x86) \ Anaconda3 \ lib \ site-packages \ pysftp__init __. Py ", строка 132, в init self._tconnect ['hostkey'] = self._cnopts.get_hostkey (host) Файл «E: \ Program Files (x86) \ Anaconda3 \ lib \ site-packages \ pysftp__init __. Py», строка 71, в get_hostkey raise SSHException («Нет hostkey для хоста% s не найдено.»% Host) paramiko.ssh_exception.SSHException : Не найден ключ хоста для хоста 138.99.99.129. Исключение игнорируется в:> Traceback (последний вызов последним): файл "E: \ Program Files (x86) \ Anaconda3 \ lib \ site-packages \ pysftp__init __. Py", строка 1013, в del self .close () Файл "E: \ Program Files (x86) \ Anaconda3 \ lib \ site-packages \ pysftp__init __. py", строка 784, закрывается, если self._sftp_live: AttributeError: объект 'Connection' не имеет атрибута '_sftp_live'


person Gabriel Theodoulos    schedule 14.08.2016    source источник
comment
Вы можете найти ответ на свою проблему в pysftp документация, в которой эта проблема явно упоминается здесь.   -  person patryk.beza    schedule 25.08.2017


Ответы (9)


Не устанавливайте cnopts.hostkeys = None (как показывает второй по количеству голосов ответ), если вы не заботитесь о безопасности. Вы теряете защиту от атак типа "человек посередине" из-за так и сделаю.


Используйте CnOpts.hostkeys (возвращает HostKeys) для управления ключами доверенного хоста.

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

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

где known_hosts содержит открытый ключ (и) сервера] в таком формате:

example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...

Если вы не хотите использовать внешний файл, вы также можете использовать

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:

Хотя, начиная с pysftp 0.2.9, этот подход будет выдавать предупреждение, что похоже на ошибку:
Не удалось загрузить предупреждение HostKeys при подключении к SFTP-серверу с помощью pysftp


Простой способ получить ключ хоста в нужном формате - использовать OpenSSH ssh-keyscan:

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

(из-за ошибки в pysftp этот не работает, если сервер использует нестандартный порт - запись начинается с [example.com]:port + остерегайтесь перенаправления ssh-keyscan в файл в PowerShell)

Вы также можете заставить приложение делать то же самое автоматически:
Используйте Paramiko AutoAddPolicy с pysftp
(It автоматически добавит ключи новых хостов в known_hosts, но для известных ключей хоста не примет измененный ключ)


Хотя для абсолютной безопасности вам не следует извлекать ключ хоста удаленно, поскольку вы не можете быть уверены, если вы еще не подверглись атаке.

См. Мою статью Где я могу получить отпечаток ключа хоста SSH для авторизации сервера?
Это для моего SFTP-клиента WinSCP, но большая часть информации там действительна в целом.


Если вам нужно проверить ключ хоста, используя только его отпечаток пальца, см. Python - pysftp / paramiko - Проверить ключ хоста с помощью его отпечатка пальца .

person Martin Prikryl    schedule 13.04.2017
comment
что я должен использовать для параметра hostname в cnopts.hostkeys.add(), когда он появляется в known_hosts как |1|xyzxyzxyz=|abcabcabc=? - person marengaz; 10.09.2019
comment
@marengaz Используйте имя хоста, которое вы используете в pysftp.Connection. - person Martin Prikryl; 10.09.2019

Один из вариантов - отключить требование ключа хоста:

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

Дополнительную информацию об этом можно найти здесь: https://stackoverflow.com/a/38355117/1060738

Важное примечание:

Установив cnopts.hostkeys=None, вы потеряете защиту от атак Man-in-the-middle. Используйте ответ @ martin-prikryl, чтобы этого избежать.

person Noam Peled    schedule 18.08.2016
comment
Да, вот как я в итоге решил свою проблему. Но при этом игнорируется только ключ хоста. Я хотел бы иметь возможность сохранить безопасность (если возможно), прочитав фактический ключ. - person Gabriel Theodoulos; 21.08.2016
comment
Небольшая заметка. CnOpts () завершится ошибкой, если в пути есть пустой файл known_hosts. - person Tommy Strand; 08.09.2016
comment
См. мой ответ о решении. - person Martin Prikryl; 13.04.2017
comment
Я установил hostkeys на None, но это не работает внутри контейнера докеров, вызывает предупреждение и не устанавливает соединение. - person ronit; 08.06.2020

Попробуйте использовать библиотеку pysftp версии 0.2.8. $ pip uninstall pysftp && pip install pysftp==0.2.8

И попробуйте вот так:

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

Почему это? В основном это ошибка с 0.2.9 pysftp здесь все подробности https://github.com/Yenthe666/auto_backup/issues/47

person wilo087    schedule 23.07.2018
comment
Не могли бы вы немного уточнить? Я перешел по ссылке и на самом деле не нашел никакого приличного объяснения, почему возврат к более ранней версии может помочь. Хотя ваше утверждение здесь все подробности предполагает, что они должны быть. - person Martin Prikryl; 23.07.2018
comment
Чудесно!! Я установил 0.2.9. этот пост спас мне день! - person Ram Dwivedi; 04.10.2018

Если вы попытаетесь подключиться с помощью pysftp к "обычному" FTP, вы должны установить для hostkey значение None.

import pysftp

cnopts = pysftp.CnOpts()
cnopts.hostkeys = None 
with pysftp.Connection(host='****',username='****',password='***',port=22,cnopts=cnopts) as sftp:
    print('DO SOMETHING')
person Paweł Miłosz    schedule 29.01.2020

Поваренная книга по использованию различных вариантов pysftp.CnOpts () и hostkeys.

Источник: https://pysftp.readthedocs.io/en/release_0.2.9/cookbook.html

Проверка ключа хоста включена по умолчанию. По умолчанию он будет использовать ~ / .ssh / known_hosts. Если вы хотите отключить проверку ключей хоста (НЕ РЕКОМЕНДУЕТСЯ), вам нужно будет изменить CnOpts по умолчанию и установить для .hostkeys значение None.

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

Чтобы использовать совершенно другой файл known_hosts, вы можете переопределить CnOpts, ищущие ~ / .ssh / known_hosts, указав файл при создании экземпляра.

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

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

Если вы хотите использовать ~ / .ssh / known_hosts, но добавить дополнительные ключи известного хоста, вы можете объединить их с обновленными дополнительными файлами в формате known_host, используя метод .load.

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
person Yugendhar Anveshra    schedule 27.04.2020

Сначала подключитесь к серверу с помощью ssh-клиента Windows, который использует файл known_hosts. PuTTy хранит данные в реестре Windows, однако OpenSSH использует файл known_hosts и добавит туда записи после подключения. Расположение файла по умолчанию -% USERPROFILE% .ssh. надеюсь, это поможет

person Hannes Rautenbach    schedule 02.06.2017
comment
Несправедливый комментарий. Вы прочитали вопрос? Как согласовать разницу между pysftp и PuTTY? - person Hannes Rautenbach; 07.06.2017

Привет, у нас была такая же проблема, если я вас хорошо понимаю. Так что проверьте, какую версию pysftp вы используете. Если это последняя версия, которая составляет 0,2,9, понизьте ее до 0,2,8. Проверь это. https://github.com/Yenthe666/auto_backup/issues/47

person Omagano Uushona    schedule 11.01.2017
comment
Не предлагайте людям обходить функцию безопасности, не объясняя последствий! Вы теряете защиту от атак MITM. - person Martin Prikryl; 13.04.2017

Я реализовал auto_add_key в своей вилке pysftp github.

auto_add_key добавит ключ к known_hosts, если auto_add_key=True
Когда ключ присутствует для хоста в known_hosts, этот ключ будет проверен.

Обратитесь к Мартину Прикрылу -> ответ о проблемах безопасности.

Хотя для абсолютной безопасности вам не следует извлекать ключ хоста удаленно, поскольку вы не можете быть уверены, если вы еще не подверглись атаке.

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()

Примечание: Зачем использовать диспетчер контекста

import pysftp
with pysftp.Connection(host, username="whatever", password="whatever", auto_add_key=True) as sftp:
    #do your stuff here
#connection closed
person Fabian    schedule 28.08.2019

FWIR, если аутентификация - это только имя пользователя и pw, добавьте IP-адрес удаленного сервера в известные_хосты, например ssh-keyscan -H 192.168.1.162 >> ~/.ssh/known_hosts для ref https://www.techrepublic.com/article/how-to-easily-add-an-ssh-fingerprint-to-your-knownhosts-file-in-linux/

person Andrew Garcia    schedule 21.01.2020
comment
Это, вероятно, было бы более подходящим в качестве комментария и отметьте ответ просто FYI - person henrycjc; 22.01.2020