You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by to...@apache.org on 2020/04/04 20:45:05 UTC

[libcloud] 03/21: Add support for Ed25519 key types when using paramiko >= 2.2.0.

This is an automated email from the ASF dual-hosted git repository.

tomaz pushed a commit to branch 2.8.x
in repository https://gitbox.apache.org/repos/asf/libcloud.git

commit b3f36cd27a47dcce885a972f55b52c4e2eddf624
Author: Tomaz Muraus <to...@tomaz.me>
AuthorDate: Tue Mar 31 23:06:18 2020 +0200

    Add support for Ed25519 key types when using paramiko >= 2.2.0.
---
 libcloud/compute/ssh.py                  | 19 +++++++++---
 libcloud/test/compute/test_ssh_client.py | 53 ++++++++++++++++++++++++++++++++
 2 files changed, 68 insertions(+), 4 deletions(-)

diff --git a/libcloud/compute/ssh.py b/libcloud/compute/ssh.py
index 78718fe..fcc5d01 100644
--- a/libcloud/compute/ssh.py
+++ b/libcloud/compute/ssh.py
@@ -517,10 +517,20 @@ class ParamikoSSHClient(BaseSSHClient):
 
         # NOTE: Paramiko only supports key in PKCS#1 PEM format.
         """
+        key_types = [
+            (paramiko.RSAKey, 'RSA'),
+            (paramiko.DSSKey, 'DSA'),
+            (paramiko.ECDSAKey, 'EC')
+        ]
 
-        for cls, key_type in [(paramiko.RSAKey, 'RSA'),
-                              (paramiko.DSSKey, 'DSA'),
-                              (paramiko.ECDSAKey, 'EC')]:
+        paramiko_version = getattr(paramiko, '__version__', '0.0.0')
+        paramiko_version = tuple([int(c) for c in paramiko_version.split('.')])
+
+        if paramiko_version >= (2, 2, 0):
+            # Ed25519 is only supported in paramiko >= 2.2.0
+            key_types.append((paramiko.ed25519key.Ed25519Key, 'Ed25519'))
+
+        for cls, key_type in key_types:
             # Work around for paramiko not recognizing keys which start with
             # "----BEGIN PRIVATE KEY-----"
             # Since key is already in PEM format, we just try changing the
@@ -546,7 +556,8 @@ class ParamikoSSHClient(BaseSSHClient):
             else:
                 return key
 
-        msg = ('Invalid or unsupported key type (only RSA, DSS and ECDSA keys'
+        msg = ('Invalid or unsupported key type (only RSA, DSS, ECDSA and'
+               ' Ed25519 keys'
                ' in PEM format are supported). For more information on '
                ' supported key file types, see %s' % (SUPPORTED_KEY_TYPES_URL))
         raise paramiko.ssh_exception.SSHException(msg)
diff --git a/libcloud/test/compute/test_ssh_client.py b/libcloud/test/compute/test_ssh_client.py
index dc59df5..1a323df 100644
--- a/libcloud/test/compute/test_ssh_client.py
+++ b/libcloud/test/compute/test_ssh_client.py
@@ -183,6 +183,59 @@ class ParamikoSSHClientTests(LibcloudTestCase):
         assertRaisesRegex(self, paramiko.ssh_exception.PasswordRequiredException,
                           expected_msg, mock.connect)
 
+        conn_params = {'hostname': 'dummy.host.org',
+                       'username': 'ubuntu',
+                       'key_files': path}
+
+        mock = ParamikoSSHClient(**conn_params)
+
+        expected_msg = 'private key file is encrypted'
+        assertRaisesRegex(self, paramiko.ssh_exception.PasswordRequiredException,
+                          expected_msg, mock.connect)
+
+    @patch('paramiko.SSHClient', Mock)
+    def test_password_protected_key_valid_password_provided(self):
+        path = os.path.join(os.path.dirname(__file__),
+                            'fixtures', 'misc',
+                            'test_rsa_2048b_pass_foobar.key')
+
+        # Supplied as key_material
+        with open(path, 'r') as fp:
+            private_key = fp.read()
+
+        conn_params = {'hostname': 'dummy.host.org',
+                       'username': 'ubuntu',
+                       'key_material': private_key,
+                       'key_password': 'foobar'}
+
+        mock = ParamikoSSHClient(**conn_params)
+        self.assertTrue(mock.connect())
+
+        conn_params = {'hostname': 'dummy.host.org',
+                       'username': 'ubuntu',
+                       'key_files': path,
+                       'key_password': 'foobar'}
+
+        mock = ParamikoSSHClient(**conn_params)
+        self.assertTrue(mock.connect())
+
+    @patch('paramiko.SSHClient', Mock)
+    def test_ed25519_key_type(self):
+        path = os.path.join(os.path.dirname(__file__),
+                            'fixtures', 'misc',
+                            'test_ed25519.key')
+
+        # Supplied as key_material
+        with open(path, 'r') as fp:
+            private_key = fp.read()
+
+        conn_params = {'hostname': 'dummy.host.org',
+                       'username': 'ubuntu',
+                       'key_material': private_key}
+
+        mock = ParamikoSSHClient(**conn_params)
+        self.assertTrue(mock.connect())
+
     def test_key_material_valid_pem_keys_invalid_header_auto_conversion(self):
         # Test a scenario where valid PEM keys with invalid headers which is
         # not recognized by paramiko are automatically converted in a format