def GetSshKeyFilenames(key_type, suffix=""): """Get filenames of the SSH key pair of the given type. @type key_type: string @param key_type: type of SSH key, must be element of C{constants.SSHK_ALL} @type suffix: string @param suffix: optional suffix for the key filenames @rtype: tuple of (string, string) @returns: a tuple containing the name of the private key file and the public key file. """ if key_type not in constants.SSHK_ALL: raise errors.SshUpdateError("Unsupported key type '%s'. Supported key types" " are: %s." % (key_type, constants.SSHK_ALL)) (_, root_keyfiles) = \ GetAllUserFiles(constants.SSH_LOGIN_USER, mkdir=False, dircheck=False) if not key_type in root_keyfiles.keys(): raise errors.SshUpdateError("No keyfile for key type '%s' available." % key_type) key_filenames = root_keyfiles[key_type] if suffix: key_filenames = [_ComputeKeyFilePathWithSuffix(key_filename, suffix) for key_filename in key_filenames] return key_filenames
def ReadLocalSshPubKeys(key_types, suffix=""): """Reads the local root user SSH key. @type key_types: list of string @param key_types: types of SSH keys. Must be subset of constants.SSHK_ALL. If 'None' or [], all available keys are returned. @type suffix: string @param suffix: optional suffix to be attached to key names when reading them. Used for temporary key files. @rtype: list of string @return: list of public keys """ fetch_key_types = [] if key_types: fetch_key_types += key_types else: fetch_key_types = constants.SSHK_ALL (_, root_keyfiles) = \ GetAllUserFiles(constants.SSH_LOGIN_USER, mkdir=False, dircheck=False) result_keys = [] for (public_key_type, (_, public_key_file)) in root_keyfiles.items(): if public_key_type not in fetch_key_types: continue public_key_dir = os.path.dirname(public_key_file) public_key_filename = "" if suffix: public_key_filename = \ os.path.splitext(os.path.basename(public_key_file))[0] \ + suffix + ".pub" else: public_key_filename = public_key_file public_key_path = os.path.join(public_key_dir, public_key_filename) if not os.path.exists(public_key_path): raise errors.SshUpdateError("Cannot find SSH public key of type '%s'." % public_key_type) else: key = utils.ReadFile(public_key_path) result_keys.append(key) return result_keys
def ReplaceSshKeys(src_key_type, dest_key_type, src_key_suffix="", dest_key_suffix=""): """Replaces an SSH key pair by another SSH key pair. Note that both parts, the private and the public key, are replaced. @type src_key_type: string @param src_key_type: key type of key pair that is replacing the other key pair @type dest_key_type: string @param dest_key_type: key type of the key pair that is being replaced by the source key pair @type src_key_suffix: string @param src_key_suffix: optional suffix of the key files of the source key pair @type dest_key_suffix: string @param dest_key_suffix: optional suffix of the keey files of the destination key pair """ (src_priv_filename, src_pub_filename) = GetSshKeyFilenames(src_key_type, suffix=src_key_suffix) (dest_priv_filename, dest_pub_filename) = GetSshKeyFilenames(dest_key_type, suffix=dest_key_suffix) if not (os.path.exists(src_priv_filename) and os.path.exists(src_pub_filename)): raise errors.SshUpdateError( "At least one of the source key files is missing: %s", ", ".join([src_priv_filename, src_pub_filename])) for dest_file in [dest_priv_filename, dest_pub_filename]: if os.path.exists(dest_file): utils.CreateBackup(dest_file) utils.RemoveFile(dest_file) shutil.move(src_priv_filename, dest_priv_filename) shutil.move(src_pub_filename, dest_pub_filename)
def _ManipulatePubKeyFile(target_identifier, target_key, key_file=pathutils.SSH_PUB_KEYS, error_fn=errors.ProgrammerError, process_line_fn=None, process_else_fn=None): """Manipulates the list of public SSH keys of the cluster. This is a general function to manipulate the public key file. It needs two auxiliary functions C{process_line_fn} and C{process_else_fn} to work. Generally, the public key file is processed as follows: 1) The function processes each line of the original ganeti public key file, applies the C{process_line_fn} function on it, which returns a possibly manipulated line and an indicator whether the line in question was found. If a line is returned, it is added to a list of lines for later writing to the file. 2) If all lines are processed and the 'found' variable is False, the seconds auxiliary function C{process_else_fn} is called to possibly add more lines to the list of lines. 3) Finally, the list of lines is assembled to a string and written atomically to the public key file, thereby overriding it. If the public key file does not exist, we create it. This is necessary for a smooth transition after an upgrade. @type target_identifier: str @param target_identifier: identifier of the node whose key is added; in most cases this is the node's UUID, but in some it is the node's host name @type target_key: str @param target_key: string containing a public SSH key (a complete line possibly including more parameters than just the key) @type key_file: str @param key_file: filename of the file of public node keys (optional parameter for testing) @type error_fn: function @param error_fn: Function that returns an exception, used to customize exception types depending on the calling context @type process_line_fn: function @param process_line_fn: function to process one line of the public key file @type process_else_fn: function @param process_else_fn: function to be called if no line of the key file matches the target uuid """ assert process_else_fn is not None assert process_line_fn is not None old_lines = [] f_orig = None if os.path.exists(key_file): try: f_orig = open(key_file, "r") old_lines = f_orig.readlines() finally: f_orig.close() else: try: f_orig = open(key_file, "w") f_orig.close() except IOError as e: raise errors.SshUpdateError("Cannot create public key file: %s" % e) found = False new_lines = [] for line in old_lines: (uuid, key) = _ParseKeyLine(line, error_fn) if not uuid: continue (new_found, new_line) = process_line_fn(target_identifier, target_key, uuid, key, found) if new_found: found = True if new_line is not None: new_lines.append(new_line) if not found: new_line = process_else_fn(target_identifier, target_key) if new_line is not None: new_lines.append(new_line) new_file_content = "".join(new_lines) utils.WriteFile(key_file, data=new_file_content)