Перевірте ключ хосту за допомогою pysftp


77

Я пишу програму за допомогою 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-пакети \ pysftp__init __. Py: 61: UserWarning:
Не вдалося завантажити HostKeys з C: \ Users \ JohnCalvin.ssh \ known_hosts.
Вам потрібно буде явно завантажити HostKeys (cnopts.hostkeys.load (ім'я файлу)) або відключити перевірку HostKey (cnopts.hostkeys = Немає). warnings.warn (wmsg, UserWarning) Traceback (останній дзвінок останній): Файл "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-пакети \ pysftp__init __. Py ", рядок 132, init self._tconnect ['hostkey'] = self._cnopts.get_hostkey (host) Файл "E: \ Program Files (x86) \ Anaconda3 \ lib \ site-пакети \ pysftp__init __. py", рядок 71, в get_hostkey підняти SSHException (" Не знайдено хост-клавішу для хосту% s. "% Host) paramiko.ssh_exception.SSHException: Не знайдено хост-клавішу для хосту 138.99.99.129. Виняток проігноровано у:> Traceback (останній виклик останній): Файл "E: \ Program Files (x86) \ Anaconda3 \ lib \ site-пакети \ pysftp__init __. Py", рядок 1013, у файлі del self.close () "E : \ Program Files (x86) \ Anaconda3 \ lib \ site-пакети \ pysftp__init __. Py ", рядок 784, у тісноті, якщо self._sftp_live: AttributeError: 'Connection' об'єкт не має атрибута '_sftp_live'


2
Ви можете знайти відповідь на свою проблему в документації, яка прямо згадує цю проблему тут . pysftp
patryk.beza

Відповіді:


98

Не встановлюйте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
(Він автоматично додасть ключі хостів нових хостів known_hosts, але для відомих ключів хосту він не прийме змінений ключ)


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

Дивіться мою статтю Де я можу отримати відбиток ключа хосту SSH для авторизації сервера?
Це для мого клієнта WinSCP SFTP, але більшість інформації там дійсні загалом.


Якщо вам потрібно перевірити ключ хосту, використовуючи лише його відбиток пальця, див. Python - pysftp / paramiko - Перевірка ключа хоста за допомогою його відбитка пальця .


що слід використовувати для hostnameпараметра, cnopts.hostkeys.add()коли він відображається known_hostsяк |1|xyzxyzxyz=|abcabcabc=?
marengaz

1
@marengaz Використовуйте ім'я хосту, яке ви використовуєте в pysftp.Connection.
Мартін

83

Одним із варіантів є вимкнення вимоги до ключа хосту:

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ви втратите захист від атак "Людина посередині". Використовуйте відповідь @ martin-prikryl, щоб уникнути цього.


2
Так, таким чином я врешті вирішив свою проблему. Але роблячи це таким чином, лише ігнорує ключ хосту. Я хотів би мати можливість зберегти захист (якщо це можливо), прочитавши фактичний ключ.
Габріель Теодулос

6
Коротка примітка. CnOpts () не зможе, якщо у вас є порожній файл known_hosts у шляху.
Tommy Strand


Я встановив клавіші хост-клавіш на None, але це не працює всередині контейнера докера, викликає попередження та не встановлює з'єднання.
роніт

13

Спробуйте використовувати версію 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


1
Не могли б ви трохи детальніше розповісти? Я перейшов за посиланням і насправді не знайшов жодного пристойного пояснення, чому зниження рівня може допомогти. Хоча ваше твердження "тут усі подробиці" свідчить про те, що їх повинно бути.
Мартін Прикрил,

Чудово !! Я встановив 0.2.9. ця публікація врятувала мені день!
Рам Двіведі,

5

Якщо ви намагаєтесь підключитися за допомогою pysftp до "звичайного" FTP, вам потрібно встановити клавішу хосту на None.

import pysftp

cnopts = pysftp.CnOpts()
cnopts.hostkeys = None 
with pysftp.Connection(host='****',username='****',password='***',port=22,cnopts=cnopts) as sftp:
    print('DO SOMETHING')

3

Готуйте книгу, щоб використовувати різні способи pysftp.CnOpts () та параметри клавіш хосту.

Джерело: 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

1

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


Нечесний коментар. Ви читали питання? Як узгодити різницю між pysftp та PuTTY?
Ганнес Раутенбах,

0

Привіт У нас була однакова проблема, якщо я вас добре розумію. Тож перевірте, яку версію pysftp ви використовуєте. Якщо це найновіший варіант, який має зниження до 0,2,9 до 0,2,8. Заціни. https://github.com/Yenthe666/auto_backup/issues/47


2
Не пропонуйте людям обійти захисну функцію, не пояснюючи наслідків! Ви втрачаєте захист від атак MITM .
Мартін Прикрил,

0

Я реалізував 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

-1

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


1
Це, мабуть, було б більш доречним як коментар і зауважили відповідь просто FYI
henrycjc
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.