Exemple #1
0
def create_ssh_key_pair(name):
    """Create an SSH private/public key pair in ~/.ssh/

    If an SSH key pair exists with "name" then the private key path is
    returned *without* creating anything new.

    Args:
        name (str): Filename of private key file

    Returns:
        str: Private ssh key path

    Raises:
        UserException: If ssh-keygen command fails
    """
    log = logger.getlogger()
    ssh_dir = os.path.join(Path.home(), ".ssh")
    private_key_path = os.path.join(ssh_dir, name)
    if not os.path.isdir(ssh_dir):
        os.mkdir(ssh_dir, mode=0o700)
    if os.path.isfile(private_key_path):
        log.info(f'SSH key \'{private_key_path}\' already exists, continuing')
    else:
        print(bold(f'Creating SSH key \'{private_key_path}\''))
        cmd = ('ssh-keygen -t rsa -b 4096 '
               '-C "Generated by Power-Up Software Installer" '
               f'-f {private_key_path} -N ""')
        resp, err, rc = sub_proc_exec(cmd, shell=True)
        if str(rc) != "0":
            msg = 'ssh-keygen failed:\n{}'.format(resp)
            log.debug(msg)
            raise UserException(msg)
    return private_key_path
Exemple #2
0
 def sync(self):
     self.log.info(f'Syncing {self.repo_name}')
     self.log.info(
         'This can take many minutes or hours for large repositories\n')
     cmd = (f'reposync -a {self.arch} -r {self.repo_id} -p'
            f'{os.path.dirname(self.repo_dir)} -l -m')
     rc = sub_proc_display(cmd)
     if rc != 0:
         self.log.error(bold(f'\nFailed {self.repo_name} repo sync. {rc}'))
         raise UserException
     else:
         self.log.info(f'{self.repo_name} sync finished successfully')
Exemple #3
0
def copy_ssh_key_pair_to_hosts(private_key_path,
                               software_hosts_file_path,
                               global_pass=None):
    """Copy an SSH public key into software hosts authorized_keys files

    TODO: detailed description

    Args:
        private_key_path (str) : Filename of private key file
        software_hosts_file_path (str): Path to software inventory file
        global_pass (str, optional): Global client default SSH password

    Returns:
        bool: True iff rc of all commands are "0"
    """
    hosts_list = _validate_inventory_count(software_hosts_file_path, 0)
    all_zero_returns = True

    hostvars = get_ansible_hostvars(software_hosts_file_path)

    for host in hosts_list:
        print(bold(f'Copy SSH Public Key to {host}'))
        cmd = f'ssh-copy-id -i {private_key_path} '
        if "ansible_port" in hostvars[host]:
            cmd += f'-p {hostvars[host]["ansible_port"]} '
        if "ansible_ssh_common_args" in hostvars[host]:
            cmd += f'{hostvars[host]["ansible_ssh_common_args"]} '
        cmd += f'{hostvars[host]["ansible_user"]}@{host}'

        if 'ansible_ssh_pass' not in hostvars[host]:
            cmd = f'SSHPASS=\'{global_pass}\' sshpass -e ' + cmd

        resp, err, rc = sub_proc_exec(cmd, shell=True)
        if rc != 0:
            all_zero_returns = False
            print(err)

    return all_zero_returns
Exemple #4
0
def get_ansible_inventory():
    log = logger.getlogger()
    inventory_choice = None
    dynamic_inventory_path = get_dynamic_inventory_path()
    software_hosts_file_path = (
        os.path.join(get_playbooks_path(), 'software_hosts'))

    heading1("Software hosts inventory setup\n")

    dynamic_inventory = None

    # If dynamic inventory contains clients prompt user to use it
    if (dynamic_inventory is not None and
            len(set(_get_hosts_list(dynamic_inventory)) -
                set(['deployer', 'localhost'])) > 0):
        print("Ansible Dynamic Inventory found:")
        print("--------------------------------")
        print(_get_groups_hosts_string(dynamic_inventory))
        print("--------------------------------")
        validate_software_inventory(dynamic_inventory)
        if click.confirm('Do you want to use this inventory?'):
            print("Using Ansible Dynamic Inventory")
            inventory_choice = dynamic_inventory_path
        else:
            print("NOT using Ansible Dynamic Inventory")

    # If dynamic inventory has no hosts or user declines to use it
    if inventory_choice is None:
        while True:
            # Check if software inventory file exists
            if os.path.isfile(software_hosts_file_path):
                print("Software inventory file found at '{}':"
                      .format(software_hosts_file_path))
            # If no software inventory file exists create one using template
            else:
                rlinput("Press enter to create client node inventory")
                _create_new_software_inventory(software_hosts_file_path)

            # If still no software inventory file exists prompt user to
            # exit (else start over to create one).
            if not os.path.isfile(software_hosts_file_path):
                print("No inventory file found at '{}'"
                      .format(software_hosts_file_path))
                if click.confirm('Do you want to exit the program?'):
                    sys.exit(1)
                else:
                    continue

            # Menu items can modified to show validation results
            continue_msg = 'Continue with current inventory'
            edit_msg = 'Edit inventory file'
            exit_msg = 'Exit program'
            ssh_config_msg = 'Configure Client Nodes for SSH Key Access'
            menu_items = []

            # Validate software inventory
            inv_count = len(_validate_inventory_count(software_hosts_file_path,
                                                      0))
            print(f'Validating software inventory ({inv_count} nodes)...')
            if validate_software_inventory(software_hosts_file_path):
                print(bold("Validation passed!"))
            else:
                print(bold("Unable to complete validation"))
                continue_msg = ("Continue with inventory as-is - "
                                "WARNING: Validation incomplete")
                menu_items.append(ssh_config_msg)

            # Prompt user
            menu_items += [continue_msg, edit_msg, exit_msg]
            choice, item = get_selection(menu_items)
            print(f'Choice: {choice} Item: {item}')
            if item == ssh_config_msg:
                configure_ssh_keys(software_hosts_file_path)
            elif item == continue_msg:
                print("Using '{}' as inventory"
                      .format(software_hosts_file_path))
                inventory_choice = software_hosts_file_path
                break
            elif item == edit_msg:
                click.edit(filename=software_hosts_file_path)
            elif item == exit_msg:
                sys.exit(1)

    if inventory_choice is None:
        log.error("Software inventory file is required to continue!")
        sys.exit(1)
    log.debug("User software inventory choice: {}".format(inventory_choice))

    return inventory_choice
Exemple #5
0
def copy_ssh_key_pair_to_user_dir(private_key_path):
    """Copy an SSH private/public key pair into the user's ~/.ssh dir

    This function is useful when a key pair is created as root user
    (e.g. using 'sudo') but should also be available to the user for
    direct 'ssh' calls.

    If the private key is already in the user's ~/.ssh directory
    nothing is done.

    Args:
        private_key_path (str) : Filename of private key file

    Returns:
        str: Path to user copy of private key
    """
    public_key_path = private_key_path + '.pub'

    user_name, user_home_dir = get_user_and_home()
    user_ssh_dir = os.path.join(user_home_dir, ".ssh")

    if user_ssh_dir not in private_key_path:
        user_private_key_path = os.path.join(
            user_ssh_dir, os.path.basename(private_key_path))
        user_public_key_path = user_private_key_path + '.pub'
        user_uid = pwd.getpwnam(user_name).pw_uid
        user_gid = grp.getgrnam(user_name).gr_gid

        if not os.path.isdir(user_ssh_dir):
            os.mkdir(user_ssh_dir, mode=0o700)
            os.chown(user_ssh_dir, user_uid, user_gid)

        # Never overwrite an existing private key file!
        already_copied = False
        while os.path.isfile(user_private_key_path):
            # If key pair already exists no need to do anything
            if (filecmp.cmp(private_key_path, user_private_key_path) and
                    filecmp.cmp(public_key_path, user_public_key_path)):
                already_copied = True
                break
            else:
                user_private_key_path += "_powerup"
                user_public_key_path = user_private_key_path + '.pub'

        if already_copied:
            print(f'\'{private_key_path}\' already copied to '
                  f'\'{user_private_key_path}\'')
        else:
            print(bold(f'Copying \'{private_key_path}\' to '
                       f'\'{user_private_key_path}\' for unprivileged use'))
            copyfile(private_key_path, user_private_key_path)
            copyfile(public_key_path, user_public_key_path)

            os.chown(user_private_key_path, user_uid, user_gid)
            os.chmod(user_private_key_path, 0o600)

            os.chown(user_public_key_path, user_uid, user_gid)
            os.chmod(user_public_key_path, 0o644)

    else:
        user_private_key_path = private_key_path

    return user_private_key_path
Exemple #6
0
def configure_ssh_keys(software_hosts_file_path):
    """Configure SSH keys for Ansible software hosts

    Scan for SSH key pairs in home directory, and if called using
    'sudo' also in "login" user's home directory. Allow user to create
    a new SSH key pair if 'default_ssh_key_name' doesn't already exist.
    If multiple choices are available user will be prompted to choose.
    Selected key pair is copied into "login" user's home '.ssh'
    directory if necessary. Selected key pair is then copied to all
    hosts listed in 'software_hosts' file via 'ssh-copy-id', and
    finally assigned to the 'ansible_ssh_private_key_file' var in
    the 'software_hosts' '[all:vars]' section.

    Args:
        software_hosts_file_path (str): Path to software inventory file
    """
    log = logger.getlogger()
    default_ssh_key_name = "powerup"

    ssh_key_options = get_existing_ssh_key_pairs(no_root_keys=True)

    user_name, user_home_dir = get_user_and_home()
    if os.path.join(user_home_dir, ".ssh",
                    default_ssh_key_name) not in ssh_key_options:
        ssh_key_options.insert(0, 'Create New "powerup" Key Pair')

    if len(ssh_key_options) == 1:
        item = ssh_key_options[0]
    elif len(ssh_key_options) > 1:
        print(bold("\nSelect an SSH key to use:"))
        choice, item = get_selection(ssh_key_options)

    if item == 'Create New "powerup" Key Pair':
        ssh_key = create_ssh_key_pair(default_ssh_key_name)
    else:
        ssh_key = item

    ssh_key = copy_ssh_key_pair_to_user_dir(ssh_key)

    add_software_hosts_global_var(
        software_hosts_file_path,
        "ansible_ssh_common_args='-o StrictHostKeyChecking=no'")

    hostvars = get_ansible_hostvars(software_hosts_file_path)

    run = True
    while run:
        global_user = None
        global_pass = None
        header_printed = False
        header_msg = bold('\nGlobal client SSH login credentials required')
        for host in _validate_inventory_count(software_hosts_file_path, 0):
            if global_user is None and 'ansible_user' not in hostvars[host]:
                print(header_msg)
                header_printed = True
                global_user = rlinput('username: '******'ansible_user={global_user}')
            if (global_pass is None and
                    'ansible_ssh_pass' not in hostvars[host]):
                if not header_printed:
                    print(header_msg)
                global_pass = getpass('password: '******'Retry', 'Continue', 'Exit'])
            if choice == "1":
                pass
            elif choice == "2":
                run = False
            elif choice == "3":
                log.debug('User chooses to exit.')
                sys.exit('Exiting')
        else:
            print()
            log.info("SSH key successfully copied to all hosts\n")
            run = False

    add_software_hosts_global_var(software_hosts_file_path,
                                  f'ansible_ssh_private_key_file={ssh_key}')