2017-10-18 105 views
2

此代碼會引發異常。如何驗證SSH指紋而不將其存儲在文件中?我相信下面的代碼是爲公鑰設計的。但是,使用SFTP服務器的客戶端驗證了指紋,並沒有爲我提供公鑰。Python - pysftp/paramiko - 使用其指紋驗證主機密鑰

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, "/") 

該代碼基於Verify host key with pysftp

回答

2

根據您的需求,您可以使用這兩種方法之一:

如果你需要確認只有一個特定的主機密鑰

使用ssh-keyscan(或類似)來檢索主機公鑰:

ssh-keyscan example.com > tmp.pub 

tmp.pub的樣子(known_hosts文件格式):

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

現在,就可以計算出公鑰的指紋與ssh-keygen

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

(使用-E md5僅適用於支持多個指紋算法和默認爲SHA256的OpenSSH的新版本)

你會得到類似於:

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

如果指紋匹配你有一個ES,你現在可以安全地假定tmp.pub是一個合法的公共密鑰,並在代碼中使用它:

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: 

(基於Verify host key with pysftp

如果您需要基於其指紋自動驗證主機密鑰

Eg因爲指紋來自外部配置。

我不確定pysftp的有限API是否允許。您可能不得不跳過pysftp並直接使用Paramiko library(pysftp在內部使用Paramiko)。

與Paramiko,你可以巧妙地實施MissingHostKeyPolicy interface

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

注意,在你的代碼有hexlify(key.get_fingerprint())可用的指紋。只需將該值與您擁有的指紋進行比較即可。如果匹配,就返回。否則引發異常, 像RejectPolicy一樣。


另一種解決方案(這甚至pysftp工作)是的,因爲它僅保留指紋的方式來實現PKey。並且只執行它的__cmp__ method比較指紋。然後可以將這樣的PKey的實例添加到cnopts.hostkeys.add

OP在his answer中發佈了此方法的實現。

2

根據Martin Prikryl的回答,下面是我的解決方案。

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 

用法:

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') 
+0

尼斯。我從我的回答中鏈接了這個。 –

相關問題