Use DSA SSH keys only
authorHelga Velroyen <helgav@google.com>
Fri, 9 Jan 2015 09:52:28 +0000 (10:52 +0100)
committerHelga Velroyen <helgav@google.com>
Tue, 13 Jan 2015 10:28:02 +0000 (11:28 +0100)
Ganeti always generates DSA SSH keys and thus, these should
be the only once added to the public key file and the
authorized_keys files. This fixes Issue 998.

Signed-off-by: Helga Velroyen <helgav@google.com>
Reviewed-by: Klaus Aehlig <aehlig@google.com>

lib/backend.py
lib/client/gnt_cluster.py
lib/client/gnt_node.py
lib/ssh.py
test/py/ganeti.backend_unittest.py

index 1ad0b25..d9fd478 100644 (file)
@@ -1013,22 +1013,17 @@ def _VerifySshSetup(node_status_list, my_name,
 
     (_, key_files) = \
       ssh.GetAllUserFiles(constants.SSH_LOGIN_USER, mkdir=False, dircheck=False)
+    (_, dsa_pub_key_filename) = key_files[constants.SSHK_DSA]
+
     my_keys = pub_keys[my_uuid]
-    num_keys = 0
-    for (key_type, (_, pub_key_file)) in key_files.items():
-      try:
-        pub_key = utils.ReadFile(pub_key_file)
-        if pub_key.strip() not in my_keys:
-          result.append("The %s key of node %s does not match this node's keys"
-                        " in the pub key file." % (key_type, my_name))
-        num_keys += 1
-      except IOError:
-        # There might not be keys of every type.
-        pass
-    if num_keys != len(my_keys):
-      result.append("The number of keys for node %s in the public key file"
-                    " (%s) does not match the number of keys on the node"
-                    " (%s)." % (my_name, len(my_keys), len(key_files)))
+
+    dsa_pub_key = utils.ReadFile(dsa_pub_key_filename)
+    if dsa_pub_key.strip() not in my_keys:
+      result.append("The dsa key of node %s does not match this node's key"
+                    " in the pub key file." % (my_name))
+    if len(my_keys) != 1:
+      result.append("There is more than one key for node %s in the public key"
+                    " file." % my_name)
   else:
     if len(pub_keys.keys()) > 0:
       result.append("The public key file of node '%s' is not empty, although"
@@ -1918,19 +1913,20 @@ def RenewSshKeys(node_uuids, node_names, ssh_port_map,
                         noded_cert_file=noded_cert_file,
                         run_cmd_fn=run_cmd_fn)
 
-    fetched_keys = ssh.ReadRemoteSshPubKeys(root_keyfiles, node_name,
-                                            cluster_name,
-                                            ssh_port_map[node_name],
-                                            False, # ask_key
-                                            False) # key_check
-    if not fetched_keys:
+    try:
+      (_, dsa_pub_keyfile) = root_keyfiles[constants.SSHK_DSA]
+      pub_key = ssh.ReadRemoteSshPubKeys(dsa_pub_keyfile,
+                                         node_name, cluster_name,
+                                         ssh_port_map[node_name],
+                                         False, # ask_key
+                                         False) # key_check
+    except:
       raise errors.SshUpdateError("Could not fetch key of node %s"
                                   " (UUID %s)" % (node_name, node_uuid))
 
     if potential_master_candidate:
       ssh.RemovePublicKey(node_uuid, key_file=pub_key_file)
-      for pub_key in fetched_keys.values():
-        ssh.AddPublicKey(node_uuid, pub_key, key_file=pub_key_file)
+      ssh.AddPublicKey(node_uuid, pub_key, key_file=pub_key_file)
 
     AddNodeSshKey(node_uuid, node_name,
                   potential_master_candidates,
index ecd1095..40b7690 100644 (file)
@@ -1124,28 +1124,22 @@ def _BuildGanetiPubKeys(options, pub_key_file=pathutils.SSH_PUB_KEYS, cl=None,
   nonmaster_nodes = [name for name in online_nodes
                      if name != master_node]
 
-  (_, root_keyfiles) = \
-    ssh.GetAllUserFiles(constants.SSH_LOGIN_USER, mkdir=False, dircheck=False,
-                        _homedir_fn=homedir_fn)
+  _, pub_key_filename, _ = \
+    ssh.GetUserFiles(constants.SSH_LOGIN_USER, mkdir=False, dircheck=False,
+                     kind=constants.SSHK_DSA, _homedir_fn=homedir_fn)
 
   # get the key file of the master node
-  for (_, (_, public_key_file)) in root_keyfiles.items():
-    try:
-      pub_key = utils.ReadFile(public_key_file)
-      ssh.AddPublicKey(node_uuid_map[master_node], pub_key,
-                       key_file=pub_key_file)
-    except IOError:
-      # Not all types of keys might be existing
-      pass
+  pub_key = utils.ReadFile(pub_key_filename)
+  ssh.AddPublicKey(node_uuid_map[master_node], pub_key,
+                   key_file=pub_key_file)
 
   # get the key files of all non-master nodes
   for node in nonmaster_nodes:
-    fetched_keys = ssh.ReadRemoteSshPubKeys(root_keyfiles, node, cluster_name,
-                                            ssh_port_map[node],
-                                            options.ssh_key_check,
-                                            options.ssh_key_check)
-    for pub_key in fetched_keys.values():
-      ssh.AddPublicKey(node_uuid_map[node], pub_key, key_file=pub_key_file)
+    pub_key = ssh.ReadRemoteSshPubKeys(pub_key_filename, node, cluster_name,
+                                       ssh_port_map[node],
+                                       options.ssh_key_check,
+                                       options.ssh_key_check)
+    ssh.AddPublicKey(node_uuid_map[node], pub_key, key_file=pub_key_file)
 
 
 def RenewCrypto(opts, args):
index bb88fb5..cbc3cf6 100644 (file)
@@ -167,7 +167,7 @@ def _TryReadFile(path):
 
 
 def _ReadSshKeys(keyfiles, _tostderr_fn=ToStderr):
-  """Reads SSH keys according to C{keyfiles}.
+  """Reads the DSA SSH keys according to C{keyfiles}.
 
   @type keyfiles: dict
   @param keyfiles: Dictionary with keys of L{constants.SSHK_ALL} and two-values
@@ -186,8 +186,8 @@ def _ReadSshKeys(keyfiles, _tostderr_fn=ToStderr):
     if public_key and private_key:
       result.append((kind, private_key, public_key))
     elif public_key or private_key:
-      _tostderr_fn("Couldn't find a complete set of keys for kind '%s'; files"
-                   " '%s' and '%s'", kind, private_file, public_file)
+      _tostderr_fn("Couldn't find a complete set of keys for kind '%s';"
+                   " files '%s' and '%s'", kind, private_file, public_file)
 
   return result
 
@@ -222,7 +222,10 @@ def _SetupSSH(options, cluster_name, node, ssh_port, cl):
   (_, root_keyfiles) = \
     ssh.GetAllUserFiles(constants.SSH_LOGIN_USER, mkdir=False, dircheck=False)
 
-  root_keys = _ReadSshKeys(root_keyfiles)
+  dsa_root_keyfiles = dict((kind, value) for (kind, value)
+                           in root_keyfiles.items()
+                           if kind == constants.SSHK_DSA)
+  root_keys = _ReadSshKeys(dsa_root_keyfiles)
 
   (_, cert_pem) = \
     utils.ExtractX509Certificate(utils.ReadFile(pathutils.NODED_CERT_FILE))
@@ -241,14 +244,14 @@ def _SetupSSH(options, cluster_name, node, ssh_port, cl):
                          use_cluster_key=False, ask_key=options.ssh_key_check,
                          strict_host_check=options.ssh_key_check)
 
-  fetched_keys = ssh.ReadRemoteSshPubKeys(root_keyfiles, node, cluster_name,
-                                          ssh_port, options.ssh_key_check,
-                                          options.ssh_key_check)
-  for pub_key in fetched_keys.values():
-    # Unfortunately, we have to add the key with the node name rather than
-    # the node's UUID here, because at this point, we do not have a UUID yet.
-    # The entry will be corrected in noded later.
-    ssh.AddPublicKey(node, pub_key)
+  (_, dsa_pub_keyfile) = root_keyfiles[constants.SSHK_DSA]
+  pub_key = ssh.ReadRemoteSshPubKeys(dsa_pub_keyfile, node, cluster_name,
+                                     ssh_port, options.ssh_key_check,
+                                     options.ssh_key_check)
+  # Unfortunately, we have to add the key with the node name rather than
+  # the node's UUID here, because at this point, we do not have a UUID yet.
+  # The entry will be corrected in noded later.
+  ssh.AddPublicKey(node, pub_key)
 
 
 @UsesRPC
index 4baaf92..4514b5e 100644 (file)
@@ -684,7 +684,7 @@ def InitSSHSetup(error_fn=errors.OpPrereqError, _homedir_fn=None,
 
   """
   priv_key, _, auth_keys = GetUserFiles(constants.SSH_LOGIN_USER,
-                                              _homedir_fn=_homedir_fn)
+                                        _homedir_fn=_homedir_fn)
 
   new_priv_key_name = priv_key + _suffix
   new_pub_key_name = priv_key + _suffix + ".pub"
@@ -1087,40 +1087,27 @@ def GetSshPortMap(nodes, cfg):
   return node_port_map
 
 
-def ReadRemoteSshPubKeys(keyfiles, node, cluster_name, port, ask_key,
+def ReadRemoteSshPubKeys(pub_key_file, node, cluster_name, port, ask_key,
                          strict_host_check):
-  """Fetches the public SSH keys from a node via SSH.
+  """Fetches the public DSA SSH key from a node via SSH.
 
-  @type keyfiles: dict from string to (string, string) tuples
-  @param keyfiles: a dictionary mapping the type of key (e.g. rsa, dsa) to a
-    tuple consisting of the file name of the private and public key
+  @type pub_key_file: string
+  @param pub_key_file: a tuple consisting of the file name of the public DSA key
 
   """
   ssh_runner = SshRunner(cluster_name)
 
-  failed_results = {}
-  fetched_keys = {}
-  for (kind, (_, public_key_file)) in keyfiles.items():
-    cmd = ["cat", public_key_file]
-    ssh_cmd = ssh_runner.BuildCmd(node, constants.SSH_LOGIN_USER,
-                                  utils.ShellQuoteArgs(cmd),
-                                  batch=False, ask_key=ask_key, quiet=False,
-                                  strict_host_check=strict_host_check,
-                                  use_cluster_key=False,
-                                  port=port)
-
-    result = utils.RunCmd(ssh_cmd)
-    if result.failed:
-      failed_results[kind] = (result.cmd, result.fail_reason)
-    else:
-      fetched_keys[kind] = result.stdout
+  cmd = ["cat", pub_key_file]
+  ssh_cmd = ssh_runner.BuildCmd(node, constants.SSH_LOGIN_USER,
+                                utils.ShellQuoteArgs(cmd),
+                                batch=False, ask_key=ask_key, quiet=False,
+                                strict_host_check=strict_host_check,
+                                use_cluster_key=False,
+                                port=port)
 
-  if len(fetched_keys.keys()) < 1:
-    error_msg = "Could not fetch any public SSH key."
-    for (kind, (cmd, fail_reason)) in failed_results.items():
-      error_msg += "Could not fetch the public '%s' SSH key from node '%s':" \
-                   " ran command '%s', failure reason: '%s'. " % \
-                   (kind, node, cmd, fail_reason)
-    raise errors.OpPrereqError(error_msg)
-
-  return fetched_keys
+  result = utils.RunCmd(ssh_cmd)
+  if result.failed:
+    raise errors.OpPrereqError("Could not fetch a public DSA SSH key from node"
+                               " '%s': ran command '%s', failure reason: '%s'."
+                               % (node, cmd, result.fail_reason))
+  return result.stdout
index d8811f6..0709065 100755 (executable)
@@ -1311,7 +1311,7 @@ class TestVerifySshSetup(testutils.GanetiTestCase):
   _NODE1_NAME = "name1"
   _NODE2_NAME = "name2"
   _NODE3_NAME = "name3"
-  _NODE1_KEYS = ["key11", "key12"]
+  _NODE1_KEYS = ["key11"]
   _NODE2_KEYS = ["key21"]
   _NODE3_KEYS = ["key31"]
 
@@ -1329,7 +1329,6 @@ class TestVerifySshSetup(testutils.GanetiTestCase):
 
   _AUTH_RESULT = {
     _NODE1_KEYS[0]: True,
-    _NODE1_KEYS[1]: True,
     _NODE2_KEYS[0]: False,
     _NODE3_KEYS[0]: False,
   }
@@ -1390,7 +1389,7 @@ class TestVerifySshSetup(testutils.GanetiTestCase):
 
   def testMissingMasterCandidate(self):
     auth_result = copy.deepcopy(self._AUTH_RESULT)
-    auth_result["key12"] = False
+    auth_result["key11"] = False
     self._has_authorized_mock.side_effect = \
       lambda _, key : auth_result[key]
     self._query_mock.return_value = self._PUB_KEY_RESULT