← Back to team overview

duplicity-team team mailing list archive

[Merge] lp:~ed.so/duplicity/0.6-ssh_add_missinghostkey into lp:duplicity

 

edso has proposed merging lp:~ed.so/duplicity/0.6-ssh_add_missinghostkey into lp:duplicity.

Requested reviews:
  duplicity-team (duplicity-team)

For more details, see:
https://code.launchpad.net/~ed.so/duplicity/0.6-ssh_add_missinghostkey/+merge/97085

add missing_host_key prompt to new sshbackend similar to ssh procedure
-- 
https://code.launchpad.net/~ed.so/duplicity/0.6-ssh_add_missinghostkey/+merge/97085
Your team duplicity-team is requested to review the proposed merge of lp:~ed.so/duplicity/0.6-ssh_add_missinghostkey into lp:duplicity.
=== modified file 'Changelog.GNU'
--- Changelog.GNU	2012-03-08 14:47:34 +0000
+++ Changelog.GNU	2012-03-12 18:38:19 +0000
@@ -1,3 +1,7 @@
+2012-03-12  edso
+
+	add missing_host_key prompt to new sshbackend similar to ssh procedure
+
 2012-02-29  kenneth@xxxxxxxxxxx
 
 	Changes for 0.6.18.

=== modified file 'duplicity/backends/sshbackend.py'
--- duplicity/backends/sshbackend.py	2012-03-08 12:43:08 +0000
+++ duplicity/backends/sshbackend.py	2012-03-12 18:38:19 +0000
@@ -29,6 +29,7 @@
 import errno
 import sys
 import getpass
+from binascii import hexlify
 
 # debian squeeze's paramiko is a bit old, so we silence randompool depreciation warning
 # note also: passphrased private keys work with squeeze's paramiko only if done with DES, not AES
@@ -70,6 +71,7 @@
             self.remote_dir = '.'
 
         self.client = paramiko.SSHClient()
+        self.client.set_missing_host_key_policy(AgreedAddPolicy())
         # load known_hosts files
         # paramiko is very picky wrt format and bails out on any problem...
         try:
@@ -78,7 +80,13 @@
         except Exception, e:
             raise BackendException("could not load /etc/ssh/ssh_known_hosts, maybe corrupt?")
         try:
-            self.client.load_system_host_keys()
+            # use load_host_keys() to signal it's writable to paramiko
+            # load if file exists or add filename to create it if needed
+            file = os.path.expanduser('~/.ssh/known_hosts')
+            if os.path.isfile(file):
+                self.client.load_host_keys(file)
+            else:
+                self.client._host_keys_filename = file
         except Exception, e:
             raise BackendException("could not load ~/.ssh/known_hosts, maybe corrupt?")
 
@@ -319,6 +327,33 @@
         
         return sshconfig.lookup(host)
 
+class AgreedAddPolicy (paramiko.AutoAddPolicy):
+    """
+    Policy for showing a yes/no prompt and adding the hostname and new 
+    host key to the known host file accordingly.
+    
+    This class simply extends the AutoAddPolicy class with a yes/no prompt.
+    """
+    def missing_host_key(self, client, hostname, key):
+        fp = hexlify(key.get_fingerprint())
+        fingerprint = ':'.join(a+b for a,b in zip(fp[::2], fp[1::2]))
+        question = """The authenticity of host '%s' can't be established.
+%s key fingerprint is %s.
+Are you sure you want to continue connecting (yes/no)? """ % (hostname, key.get_name().upper(), fingerprint)
+        while True:
+            sys.stdout.write(question)
+            choice = raw_input().lower()
+            if choice in ['yes','y']:
+                super(AgreedAddPolicy, self).missing_host_key(client, hostname, key)
+                return
+            elif choice in ['no','n']:
+                raise AuthenticityException( hostname )
+            else:
+                question = "Please type 'yes' or 'no': "
+
+class AuthenticityException (paramiko.SSHException):
+    def __init__(self, hostname):
+        paramiko.SSHException.__init__(self, 'Host key verification for server %s failed.' % hostname)
 
 
 duplicity.backend.register_backend("sftp", SftpBackend)


Follow ups