Consider ECDSA in SSH setup
authorHelga Velroyen <helgav@google.com>
Wed, 1 Jul 2015 12:24:11 +0000 (14:24 +0200)
committerHelga Velroyen <helgav@google.com>
Fri, 3 Jul 2015 10:55:24 +0000 (12:55 +0200)
So far, Ganeti did only care about DSA and RSA host
keys. With the rising popularity of ECDSA, we should
support this key type as well, as it is already
enabled by default in many common distributions.

This fixes Issue 1098.

Signed-off-by: Helga Velroyen <helgav@google.com>
Reviewed-by: Petr Pudlak <pudlak@google.com>

lib/ssh.py
src/Ganeti/Constants.hs
test/py/ganeti.ssh_unittest.py
test/py/ganeti.tools.prepare_node_join_unittest.py

index 599b7cc..06003a5 100644 (file)
@@ -79,6 +79,8 @@ def GetUserFiles(user, mkdir=False, dircheck=True, kind=constants.SSHK_DSA,
     suffix = "dsa"
   elif kind == constants.SSHK_RSA:
     suffix = "rsa"
+  elif kind == constants.SSHK_ECDSA:
+    suffix = "ecdsa"
   else:
     raise errors.ProgrammerError("Unknown SSH key kind '%s'" % kind)
 
index 97093df..106e4d4 100644 (file)
@@ -4399,11 +4399,14 @@ cryptoOptionSerialNo = "serial_no"
 sshkDsa :: String
 sshkDsa = "dsa"
 
+sshkEcdsa :: String
+sshkEcdsa = "ecdsa"
+
 sshkRsa :: String
 sshkRsa = "rsa"
 
 sshkAll :: FrozenSet String
-sshkAll = ConstantUtils.mkSet [sshkRsa, sshkDsa]
+sshkAll = ConstantUtils.mkSet [sshkRsa, sshkDsa, sshkEcdsa]
 
 -- * SSH authorized key types
 
@@ -4438,6 +4441,12 @@ sshHostDsaPriv = sshConfigDir ++ "/ssh_host_dsa_key"
 sshHostDsaPub :: String
 sshHostDsaPub = sshHostDsaPriv ++ ".pub"
 
+sshHostEcdsaPriv :: String
+sshHostEcdsaPriv = sshConfigDir ++ "/ssh_host_ecdsa_key"
+
+sshHostEcdsaPub :: String
+sshHostEcdsaPub = sshHostEcdsaPriv ++ ".pub"
+
 sshHostRsaPriv :: String
 sshHostRsaPriv = sshConfigDir ++ "/ssh_host_rsa_key"
 
@@ -4448,6 +4457,7 @@ sshDaemonKeyfiles :: Map String (String, String)
 sshDaemonKeyfiles =
   Map.fromList [ (sshkRsa, (sshHostRsaPriv, sshHostRsaPub))
                , (sshkDsa, (sshHostDsaPriv, sshHostDsaPub))
+               , (sshkEcdsa, (sshHostEcdsaPriv, sshHostEcdsaPub))
                ]
 
 -- * Node daemon setup
index b520a97..a121d42 100755 (executable)
@@ -147,6 +147,9 @@ class TestGetUserFiles(unittest.TestCase):
         constants.SSHK_DSA:
           (os.path.join(self.tmpdir, ".ssh", "id_dsa"),
            os.path.join(self.tmpdir, ".ssh", "id_dsa.pub")),
+        constants.SSHK_ECDSA:
+          (os.path.join(self.tmpdir, ".ssh", "id_ecdsa"),
+           os.path.join(self.tmpdir, ".ssh", "id_ecdsa.pub")),
       }))
     self.assertEqual(os.listdir(self.tmpdir), [])
 
index e0c60a4..09f8750 100755 (executable)
@@ -143,6 +143,9 @@ class TestUpdateSshDaemon(unittest.TestCase):
       constants.SSHK_DSA:
         (utils.PathJoin(self.tmpdir, "dsa.private"),
          utils.PathJoin(self.tmpdir, "dsa.public")),
+      constants.SSHK_ECDSA:
+        (utils.PathJoin(self.tmpdir, "ecdsa.private"),
+         utils.PathJoin(self.tmpdir, "ecdsa.public")),
       }
 
   def tearDown(self):
@@ -180,6 +183,13 @@ class TestUpdateSshDaemon(unittest.TestCase):
         ],
       })
 
+  def testDryRunEcdsa(self):
+    self._TestDryRun({
+      constants.SSHS_SSH_HOST_KEY: [
+        (constants.SSHK_ECDSA, "ecdsapriv", "ecdsapub"),
+        ],
+      })
+
   def _RunCmd(self, fail, cmd, interactive=NotImplemented):
     self.assertTrue(interactive)
     self.assertEqual(cmd, [pathutils.DAEMON_UTIL, "reload-ssh-keys"])
@@ -195,6 +205,7 @@ class TestUpdateSshDaemon(unittest.TestCase):
     data = {
       constants.SSHS_SSH_HOST_KEY: [
         (constants.SSHK_DSA, "dsapriv", "dsapub"),
+        (constants.SSHK_ECDSA, "ecdsapriv", "ecdsapub"),
         (constants.SSHK_RSA, "rsapriv", "rsapub"),
         ],
       }
@@ -209,6 +220,7 @@ class TestUpdateSshDaemon(unittest.TestCase):
     self.assertEqual(sorted(os.listdir(self.tmpdir)), sorted([
       "rsa.public", "rsa.private",
       "dsa.public", "dsa.private",
+      "ecdsa.public", "ecdsa.private",
       ]))
     self.assertEqual(utils.ReadFile(utils.PathJoin(self.tmpdir, "rsa.public")),
                      "rsapub")
@@ -218,6 +230,10 @@ class TestUpdateSshDaemon(unittest.TestCase):
                      "dsapub")
     self.assertEqual(utils.ReadFile(utils.PathJoin(self.tmpdir, "dsa.private")),
                      "dsapriv")
+    self.assertEqual(utils.ReadFile(utils.PathJoin(
+        self.tmpdir, "ecdsa.public")), "ecdsapub")
+    self.assertEqual(utils.ReadFile(utils.PathJoin(
+        self.tmpdir, "ecdsa.private")), "ecdsapriv")
 
   def testSuccess(self):
     self._TestUpdate(False)