python ssh paramiko pysftp

Python-pysftp/paramiko-Verifica la clave de host usando su huella digital



ssh (2)

Este código arroja una excepción. ¿Cómo puedo verificar una huella digital SSH sin almacenarla en un archivo? Creo que el código a continuación está diseñado para una clave pública. Pero el cliente con el servidor SFTP validó la huella digital y no me consiguió la clave pública.

import os import shutil import pysftp import paramiko connection_info = { ''server'': "example.com", ''user'': "user", ''passwd'': "password", ''target_dir'': "out/prod", ''hostkey'': "ssh-rsa 2048 d8:4e:f1:f1:f1:f1:f1:f1:21:31:41:14:13:12:11:aa", } def move_files_from_server_to_local(server, localpath): target_dir = server[''target_dir''] keydata = "d8:4e:f1:f1:f1:f1:f1:f1:21:31:41:14:13:12:11:aa" key = paramiko.RSAKey(data=decodebytes(keydata)) options = pysftp.CnOpts() options.hostkeys.add(''example.com'', ''ssh-rsa'', key) with pysftp.Connection( server[''server''], username=server[''user''], password=server[''passwd''], cnopts=options) as conn: conn.get_d(target_dir, localpath) delete_files_from_dir(conn, target_dir) move_files_from_server_to_local(connection_info, "/")

El código se basa en Verificar clave de host con pysftp .


Basado en la respuesta de Martin Prikryl, a continuación está mi solución.

def trim_fingerprint(fingerprint): if fingerprint.startswith(''ssh-rsa 2048 ''): return fingerprint[len(''ssh-rsa 2048 ''):] return fingerprint def clean_fingerprint(fingerprint): return trim_fingerprint(fingerprint).replace('':'', '''') class FingerprintKey: def __init__(self, fingerprint): self.fingerprint = clean_fingerprint(fingerprint) def compare(self, other): if callable(getattr(other, "get_fingerprint", None)): return other.get_fingerprint() == self.fingerprint elif clean_fingerprint(other) == self.get_fingerprint(): return True elif md5(other).digest().encode(''hex'') == self.fingerprint: return True else: return False def __cmp__(self, other): return self.compare(other) def __contains__(self, other): return self.compare(other) def __eq__(self, other): return self.compare(other) def __ne__(self, other): return not self.compare(other) def get_fingerprint(self): return self.fingerprint def get_name(self): return u''ssh-rsa'' def asbytes(self): # Note: This returns itself. # That way when comparisons are done to asbytes return value, # this class can handle the comparison. return self

uso:

options = pysftp.CnOpts() options.hostkeys.clear() options.hostkeys.add(''www.example.com'', u''ssh-rsa'', FingerprintKey("ssh-rsa 2048 d8:4e:f1:f1:f1:f1:f1:f1:21:31:41:14:13:12:11:aa")) with pysftp.Connection( ''www.example.com'', username=''user'', password=''password'', cnopts=options) as conn: conn.get_d(''remote/filedir'', ''c:/local/output'')


Según sus necesidades, puede usar cualquiera de estos dos métodos:

En caso de que necesite verificar solo una clave de host específica

Use ssh-keyscan (o similar) para recuperar la clave pública del host:

ssh-keyscan example.com > tmp.pub

El tmp.pub se verá como (formato de archivo known_hosts ):

example.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0hVqZOvZ7yWgie9OHdTORJVI5fJJoH1yEGamAd5G3werH0z7e9ybtq1mGUeRkJtea7bzru0ISR0EZ9HIONoGYrDmI7S+BiwpDBUKjva4mAsvzzvsy6Ogy/apkxm6Kbcml8u4wjxaOw3NKzKqeBvR3pc+nQVA+SJUZq8D2XBRd4EDUFXeLzwqwen9G7gSLGB1hJkSuRtGRfOHbLUuCKNR8RV82i3JvlSnAwb3MwN0m3WGdlJA8J+5YAg4e6JgSKrsCObZK7W1R6iuyuH1zA+dtAHyDyYVHB4FnYZPL0hgz2PSb9c+iDEiFcT/lT4/dQ+kRW6DYn66lS8peS8zCJ9CSQ==

Ahora puede calcular una huella digital de esa clave pública con ssh-keygen :

ssh-keygen -l -f tmp.pub -E md5

(utilice -E md5 solo con las versiones más nuevas de OpenSSH que admitan múltiples algoritmos de huellas dactilares y de forma predeterminada con SHA256)

Obtendrás algo como:

2048 MD5:c4:26:18:cf:a0:15:9a:5f:f3:bf:96:d8:3b:19:ef:7b example.com (RSA)

Si la huella dactilar coincide con la que tiene, ahora puede asumir con seguridad que el tmp.pub es una clave pública legítima y usarlo en el código:

keydata = b"""AAAAB3NzaC1yc2EAAAABIwAAAQEA0hV...""" 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:

(basado en Verificar clave de host con pysftp )

En caso de que necesite automatizar la verificación de una clave de host en función de su huella digital

Por ejemplo, porque la huella digital proviene de una configuración externa.

No estoy seguro si una API limitada de pysftp lo permite. Probablemente tendrías que saltear pysftp y usar la biblioteca Paramiko directamente (pysftp usa Paramiko internamente).

Con Paramiko, puede implementar MissingHostKeyPolicy interfaz MissingHostKeyPolicy .

Comience con cómo se implementa AutoAddPolicy :

class AutoAddPolicy (MissingHostKeyPolicy): """ Policy for automatically adding the hostname and new host key to the local `.HostKeys` object, and saving it. This is used by `.SSHClient`. """ def missing_host_key(self, client, hostname, key): client._host_keys.add(hostname, key.get_name(), key) if client._host_keys_filename is not None: client.save_host_keys(client._host_keys_filename) client._log(DEBUG, ''Adding %s host key for %s: %s'' % (key.get_name(), hostname, hexlify(key.get_fingerprint())))

Tenga en cuenta que en el código tiene la huella digital disponible en hexlify(key.get_fingerprint()) . Simplemente compare ese valor con la huella digital que tiene. Si coincide, solo regrese. De lo contrario, plantee una excepción, como lo hace RejectPolicy .

Otra solución (que funcionaría incluso con pysftp) es implementar PKey de forma que solo contenga la huella dactilar. E implemente su método __cmp__ para comparar solo la huella dactilar. Tal instancia de PKey se puede agregar a cnopts.hostkeys.add .

OP publicó una implementación de este enfoque en su respuesta .