Вопрос-Ответ

Verify host key with pysftp

Проверка ключа хоста с помощью 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: 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'
Переведено автоматически
Ответ 1

В pysftp есть некоторые ошибки, касающиеся обработки ключа хоста, как описано ниже. Также кажется, что проект pysftp был заброшен. Рассмотрите возможность использования Paramiko напрямую вместо этого. pysftp - это просто оболочка поверх Paramiko, и она не добавляет ничего действительно существенного. Смотрите pysftp против Paramiko.

Для обработки ключей хоста в Paramiko см.:

Paramiko "Неизвестный сервер"


Если вы хотите продолжать использовать pysftp, не устанавливайте 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, этот подход будет выдавать предупреждение, что выглядит как ошибка:

Предупреждение "Не удалось загрузить ключи хоста" при подключении к 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
(Он автоматически добавит ключи хоста новых хостов в known_hosts, но для известных ключей хоста он не примет измененный ключ)


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

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


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

Ответ 2

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

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://pythonly.ru/a/38355117/1060738

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

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

Ответ 3

Попробуйте использовать версию 0.2.8 библиотеки pysftp.
$ 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

Ответ 4

Руководство по использованию различных способов 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, используя .метод загрузки.

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
python