Ejemplo n.º 1
0
    def search_ruleset(self, environment_name, no_prompt = False):
        """

        :param environment_name:
        :return:
        """
        ruleset_found = False
        if environment_name != 'default':
            ruleset_file_name = 'ruleset-%s.json' % environment_name
            ruleset_file_path = os.path.join(os.getcwd(), ruleset_file_name)
            if os.path.exists(ruleset_file_path):
                if no_prompt or prompt_4_yes_no("A ruleset whose name matches your environment name was found in %s. Would you like to use it instead of the default one" % ruleset_file_name):
                    ruleset_found = True
                    self.filename = ruleset_file_path
        if not ruleset_found:
            self.filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/rulesets/default.json')
Ejemplo n.º 2
0
    def search_ruleset(self, environment_name, no_prompt=False):
        """

        :param environment_name:
        :return:
        """
        ruleset_found = False
        if environment_name != 'default':
            ruleset_file_name = 'ruleset-%s.json' % environment_name
            ruleset_file_path = os.path.join(self.rules_data_path, 'rulesets/%s' % ruleset_file_name)
            if os.path.exists(ruleset_file_path):
                if no_prompt or prompt_4_yes_no(
                        "A ruleset whose name matches your environment name was found in %s. Would you like to use it instead of the default one" % ruleset_file_name):
                    ruleset_found = True
                    self.filename = ruleset_file_path
        if not ruleset_found:
            self.filename = os.path.join(self.rules_data_path, 'rulesets/default.json')
def main():

    # Parse arguments
    parser = OpinelArgumentParser()
    parser.add_argument('debug')
    parser.add_argument('profile')
    parser.add_argument('csv-credentials')
    parser.add_argument('mfa-serial')
    parser.add_argument('mfa-code')
    parser.parser.add_argument('--role-arn',
                                dest='role_arn',
                                default=None,
                                help='ARN of the assumed role.')
    parser.parser.add_argument('--external-id',
                                dest='external_id',
                                default=None,
                                help='External ID to use when assuming the role.')
    args = parser.parse_args()

    # Configure the debug level
    configPrintException(args.debug)

    # Check version of opinel
    if not check_requirements(os.path.realpath(__file__)):
        return 42

    # Arguments
    profile_name = args.profile[0]

    if args.csv_credentials:
        # Read credentials from a CSV file
        credentials = {}
        credentials['AccessKeyId'], credentials['SecretAccessKey'], credentials['SerialNumber'] = read_creds_from_csv(args.csv_credentials)
        if not credentials['AccessKeyId'] or not credentials['SecretAccessKey']:
            printError('Failed to read credentials from %s' % args.csv_credentials)
            return 42
        use_found_credentials = True
    else:
        # Check for migration from existing profile to no-mfa profile
        use_found_credentials = False
        credentials = read_creds_from_aws_credentials_file(profile_name)
        if 'AccessKeyId' in credentials and credentials['AccessKeyId'] != None and credentials['SecretAccessKey'] != None and credentials['SerialNumber'] == None and credentials['SessionToken'] == None:
            if prompt_4_yes_no('Found long-lived credentials for the profile \'%s\'. Do you want to use those when configuring MFA' % profile_name):
               use_found_credentials = True
               iam_client = connect_service('iam', credentials)
               try:
                   printInfo('Trying to read the MFA serial number associated with this IAM user...')
                   user_name = iam_client.get_user()['User']['UserName']
                   mfa_devices = iam_client.list_mfa_devices(UserName = user_name)['MFADevices']
                   credentials['SerialNumber'] = mfa_devices[0]['SerialNumber']
               except Exception as e:
                   printException(e)
                   pass

    if not use_found_credentials:
       # Get values
        credentials['AccessKeyId'] = prompt_4_value('AWS Access Key ID: ', no_confirm = True)
        credentials['SecretAccessKey'] = prompt_4_value('AWS Secret Access Key: ', no_confirm = True)
    if 'SerialNumber' not in credentials or not credentials['SerialNumber']:
        credentials['SerialNumber'] = prompt_4_mfa_serial()

    # Check for overwrite
    while True:
        c = read_creds_from_aws_credentials_file(profile_name)
        if 'AccessKeyId' in c and c['AccessKeyId']:
            if not prompt_4_yes_no('The profile \'%s\' already exists. Do you want to overwrite the existing values' % profile_name):
                if not prompt_4_yes_no('Do you want to create a new profile with these credentials'):
                    printError('Configuration aborted.')
                    return
                profile_name = prompt_4_value('Profile name: ')
            else:
                break
        else:
            break

    # Write values to credentials file
    write_creds_to_aws_credentials_file(profile_name, credentials)

    # Delete CSV file?
    if args.csv_credentials and prompt_4_yes_no('Do you want to delete the CSV file that contains your long-lived credentials?'):
        os.remove(args.csv_credentials)
def main():

    # Parse arguments
    parser = OpinelArgumentParser()
    parser.add_argument('debug')
    parser.add_argument('profile')
    parser.add_argument('force')
    parser.add_argument('dry-run')
    parser.add_argument('regions')
    parser.add_argument('partition-name')
    parser.parser.add_argument('--interactive',
                        dest='interactive',
                        default=False,
                        action='store_true',
                        help='Interactive prompt to manually enter CIDRs.')
    parser.parser.add_argument('--csv-ip-ranges',
                        dest='csv_ip_ranges',
                        default=[],
                        nargs='+',
                        help='CSV file(s) containing CIDRs information.')
    parser.parser.add_argument('--skip-first-line',
                        dest='skip_first_line',
                        default=False,
                        action='store_true',
                        help='Skip first line when parsing CSV file.')
    parser.parser.add_argument('--attributes',
                        dest='attributes',
                        default=[],
                        nargs='+',
                        help='Name of the attributes to enter for each CIDR.')
    parser.parser.add_argument('--mappings',
                        dest='mappings',
                        default=[],
                        nargs='+',
                        help='Column number matching attributes when headers differ.')
    args = parser.parse_args()

    # Configure the debug level
    configPrintException(args.debug)

    # Check version of opinel
    if not check_requirements(os.path.realpath(__file__)):
        return 42

    # Initialize the list of regions to work with
    regions = build_region_list('ec2', args.regions, args.partition_name)

    # For each profile/environment...
    for profile_name in args.profile:

        # Interactive mode
        if args.interactive:

            # Initalize prefixes
            attributes = args.attributes
            filename = 'ip-ranges-%s.json' % profile_name
            if os.path.isfile(filename):
                printInfo('Loading existing IP ranges from %s' % filename)
                prefixes = read_ip_ranges(filename)
                # Initialize attributes from existing values
                if attributes == []:
                    for prefix in prefixes:
                        for key in prefix:
                            if key not in attributes:
                                attributes.append(key)
            else:
                prefixes = []

            # IP prefix does not need to be specified as an attribute
            attributes = [a for a in attributes if a != 'ip_prefix']

            # Prompt for new entries
            while prompt_4_yes_no('Add a new IP prefix to the ip ranges'):
                ip_prefix = prompt_4_value('Enter the new IP prefix:')
                obj = {}
                for a in attributes:
                    obj[a] = prompt_4_value('Enter the \'%s\' value:' % a)
                prefixes.append(new_prefix(ip_prefix, obj))

        # Support loading from CSV file
        elif len(args.csv_ip_ranges) > 0:

            # Initalize prefixes
            prefixes = []

            # Load CSV file contents
            for filename in args.csv_ip_ranges:
                with open(filename, 'rt') as f:
                    csv_contents = f.readlines()

                # Initialize mappings
                attributes = args.attributes
                mappings = {}
                if attributes == []:
                    # Follow structure of first line
                    headers = csv_contents.pop(0).strip().split(',')
                    for index, attribute in enumerate(headers):
                        mappings[attribute] = index
                elif attributes and args.mappings == []:
                    # Follow structure of first line but only map a subset of fields
                    headers = csv_contents.pop(0).strip().split(',')
                    attributes.append('ip_prefix')
                    for attribute in set(attributes):
                        mappings[attribute] = headers.index(attribute)
                else:
                    # Indices of columns are provided as an argument
                    for index, attribute in enumerate(attributes):
                        mappings[attribute] = int(args.mappings[index])
                    if args.skip_first_line:
                        csv_contents.pop(0)

                # For each line...
                for line in csv_contents:
                    ip_prefix = {}
                    values = line.strip().split(',')
                    if len(values) < len(mappings):
                        continue
                    for attribute in mappings:
                        ip_prefix[attribute] = values[mappings[attribute]]
                    if 'ip_prefix' in mappings and 'mask' in mappings:
                        ip = ip_prefix.pop('ip_prefix')
                        mask = ip_prefix.pop('mask')
                        ip_prefix['ip_prefix'] = '%s/%s' % (ip, mask.replace('/',''))
                    prefixes.append(ip_prefix)

        # AWS mode
        else:

            # Initialize IP addresses
            printInfo('Fetching public IP information for the \'%s\' environment...' % profile_name)
            ip_addresses = {}

            # Search for AWS credentials
            credentials = read_creds(profile_name)
            if not credentials['AccessKeyId']:
                return 42

            # For each region...
            for region in regions:

                # Connect to EC2
                ec2_client = connect_service('ec2', credentials, region)
                if not ec2_client:
                    continue

                # Get public IP addresses associated with EC2 instances
                printInfo('...in %s: EC2 instances' % region)
                reservations = handle_truncated_response(ec2_client.describe_instances, {}, ['Reservations'])
                for reservation in reservations['Reservations']:
                    for i in reservation['Instances']:
                        if 'PublicIpAddress' in i:
                            ip_addresses[i['PublicIpAddress']] = new_ip_info(region, i['InstanceId'], False)
                            get_name(i, ip_addresses[i['PublicIpAddress']], 'InstanceId')
                        if 'NetworkInterfaces' in i:
                            for eni in i['NetworkInterfaces']:
                                if 'Association' in eni:
                                    ip_addresses[eni['Association']['PublicIp']] = new_ip_info(region, i['InstanceId'], False) # At that point, we don't know whether it's an EIP or not...
                                    get_name(i, ip_addresses[eni['Association']['PublicIp']], 'InstanceId')

                # Get all EIPs (to handle unassigned cases)
                printInfo('...in %s: Elastic IP addresses' % region)
                eips = handle_truncated_response(ec2_client.describe_addresses, {}, ['Addresses'])
                for eip in eips['Addresses']:
                    instance_id = eip['InstanceId'] if 'InstanceId' in eip else None
                    # EC2-Classic non associated EIPs have an empty string for instance ID (instead of lacking the attribute in VPC)
                    if instance_id == '':
                        instance_id = None
                    ip_addresses[eip['PublicIp']] = new_ip_info(region, instance_id, True)
                    ip_addresses[eip['PublicIp']]['name'] = instance_id

                # Format
                prefixes = []
                for ip in ip_addresses:
                    prefixes.append(new_prefix(ip, ip_addresses[ip]))

        # Generate an ip-ranges-<profile>.json file
        save_ip_ranges(profile_name, prefixes, args.force_write, args.debug)
def main():

    # Parse arguments
    parser = OpinelArgumentParser()
    parser.add_argument('debug')
    parser.add_argument('profile')
    parser.add_argument('managed',
                        dest='is_managed',
                        default=False,
                        action='store_true',
                        help='Create a managed policy.')
    parser.add_argument(
        'type',
        default=[None],
        nargs='+',
        choices=['group', 'managed', 'role', 'user'],
        help='Type of target that the policy will apply or be attached to.')
    parser.add_argument(
        'targets',
        default=[],
        nargs='+',
        help=
        'Name of the IAM entity the policy will be added to (required for inline policies).'
    )
    parser.add_argument(
        'templates',
        default=[],
        nargs='+',
        help='Path to the template IAM policies that will be created.')
    parser.add_argument('save',
                        dest='save_locally',
                        default=False,
                        action='store_true',
                        help='Generates the policies and store them locally.')
    args = parser.parse_args()

    # Configure the debug level
    configPrintException(args.debug)

    # Check version of opinel
    if not check_requirements(os.path.realpath(__file__)):
        return 42

    # Arguments
    profile_name = args.profile[0]
    target_type = args.type[0]
    if len(args.templates) == 0:
        printError(
            'Error: you must specify the path the template IAM policies.')
        return 42
    if not args.is_managed and target_type == None:
        printError(
            'Error: you must either create a managed policy or specify the type of IAM entity the policy will be attached to.'
        )
        return 42
    if not args.is_managed and target_type == None and len(args.targets) < 1:
        printError(
            'Error: you must provide the name of at least one IAM %s you will attach this inline policy to.'
            % target_type)
        return 42

    # Read creds
    credentials = read_creds(args.profile[0])
    if not credentials['AccessKeyId']:
        return 42

    # Connect to IAM APIs
    iam_client = connect_service('iam', credentials)
    if not iam_client:
        return 42

    # Get AWS account ID
    aws_account_id = get_aws_account_id(credentials)

    # Create the policies
    for template in args.templates:
        if not os.path.isfile(template):
            printError('Error: file \'%s\' does not exist.' % template)
            continue
        with open(template, 'rt') as f:
            policy = f.read()
        policy = re_aws_account_id.sub(aws_account_id, policy)
        policy_name = os.path.basename(template).split('.')[0]
        if not args.is_managed:
            callback = getattr(iam_client, 'put_' + target_type + '_policy')
            params = {}
            params['PolicyName'] = policy_name
            params['PolicyDocument'] = policy
            for target in args.targets:
                params[target_type.title() + 'Name'] = target
                try:
                    printInfo(
                        'Creating policy \'%s\' for the \'%s\' IAM %s...' %
                        (policy_name, target, target_type))
                    callback(**params)
                except Exception as e:
                    printException(e)
                    pass
        else:
            params = {}
            params['PolicyDocument'] = policy
            params['PolicyName'] = policy_name
            description = ''
            # Search for a description file
            descriptions_dir = os.path.join(os.path.dirname(template),
                                            'descriptions')
            if os.path.exists(descriptions_dir):
                description_file = os.path.join(
                    descriptions_dir,
                    os.path.basename(template).replace('.json', '.txt'))
                if os.path.isfile(description_file):
                    with open(description_file, 'rt') as f:
                        params['Description'] = f.read()
            elif prompt_4_yes_no(
                    'Do you want to add a description to the \'%s\' policy' %
                    policy_name):
                params['Description'] = prompt_4_value(
                    'Enter the policy description:')
            params['Description'] = params['Description'].strip()
            printInfo('Creating policy \'%s\'...' % (policy_name))
            new_policy = iam_client.create_policy(**params)
            if len(args.targets):
                callback = getattr(iam_client,
                                   'attach_' + target_type + '_policy')
                for target in args.targets:
                    printInfo('Attaching policy to the \'%s\' IAM %s...' %
                              (target, target_type))
                    params = {}
                    params['PolicyArn'] = new_policy['Policy']['Arn']
                    params[target_type.title() + 'Name'] = target
                    callback(**params)

        if args.save_locally:
            with open('%s-%s.json' % (policy_name, profile_name), 'wt') as f:
                f.write(policy)
                f.close()
Ejemplo n.º 6
0
def main():

    # Parse arguments
    parser = OpinelArgumentParser()
    parser.add_argument('debug')
    parser.add_argument('profile')
    parser.add_argument('regions',
                        help='Regions where the stack(s) will be created.')
    parser.add_argument('partition-name')
    parser.parser.add_argument('--template',
                               dest='template',
                               default=None,
                               required=True,
                               help='Path to the CloudFormation template.')
    parser.parser.add_argument('--parameters',
                               dest='parameters',
                               default=None,
                               nargs='+',
                               help='Optional parameters for the stack.')
    args = parser.parse_args()

    # Configure the debug level
    configPrintException(args.debug)

    # Check version of opinel
    if not check_requirements(os.path.realpath(__file__)):
        return 42

    # Get profile name
    profile_name = args.profile[0]

    # Search for AWS credentials
    credentials = read_creds(profile_name)
    if __name__ == '__main__':
        if not credentials['AccessKeyId']:
            return 42

    # Validate the regions
    regions = build_region_list('cloudformation', args.regions,
                                args.partition_name)
    if len(args.regions) == 0 and not prompt_4_yes_no(
            'You didn\'t specify a region for this stack, do you want to create it in all regions ?'
    ):
        return 42

    for region in regions:
        try:
            # Create stack
            api_client = connect_service('cloudformation', credentials, region)
            params = {}
            params['api_client'] = api_client
            if not args.template.startswith('/'):
                params['template_path'] = os.path.join(
                    (os.path.dirname(os.path.realpath(__file__))),
                    args.template)
            else:
                params['template_path'] = args.template
            if args.parameters:
                params['template_parameters'] = args.parameters
            params['stack_name'] = make_awsrecipes_stack_name(
                params['template_path'])
            create_or_update_stack(**params)
        except Exception as e:
            printException(e)
Ejemplo n.º 7
0
def main():

    # Parse arguments
    parser = OpinelArgumentParser()
    parser.add_argument('debug')
    parser.add_argument('profile')
    parser.add_argument('user-name', help='Name of user(s) to be created.')
    parser.add_argument('group-name',
                        help='Name of group(s) the user(s) will belong to.')
    parser.add_argument(
        'force-common-group',
        default=False,
        action='store_true',
        help='Automatically add user(s) to the common group(s)')
    parser.add_argument('no-mfa',
                        default=False,
                        action='store_true',
                        help='Do not configure and enable MFA.')
    parser.add_argument('no-password',
                        default=False,
                        action='store_true',
                        help='Do not create a password and login')
    parser.add_argument('no-access-key',
                        default=False,
                        action='store_true',
                        help='Do not generate an access key')
    parser.add_argument('always-trust',
                        default=False,
                        action='store_true',
                        help='A not generate an access key')
    parser.add_argument('allow-plaintext',
                        default=False,
                        action='store_true',
                        help='')
    parser.add_argument('no-prompt-before-plaintext',
                        dest='prompt_before_plaintext',
                        default=True,
                        action='store_false',
                        help='')
    args = parser.parse_args()

    # Configure the debug level
    configPrintException(args.debug)

    # Check version of opinel
    if not check_requirements(os.path.realpath(__file__)):
        return 42

    # Arguments
    profile_name = args.profile[0]
    if not len(args.user_name):
        printError("Error, you need to provide at least one user name")
        return 42

    # Search for AWS credentials
    credentials = read_creds(profile_name)
    if not credentials['AccessKeyId']:
        return 42

    # Connect to IAM
    iam_client = connect_service('iam', credentials)
    if not iam_client:
        return 42

    # Initialize and compile the list of regular expression for category groups
    #if 'category_groups' in default_args and 'category_regex' in default_args:
    #    category_regex = init_iam_group_category_regex(default_args['category_groups'], default_args['category_regex'])

    # Iterate over users
    for user in args.user_name:

        # Search for the GPG key
        abort = False
        gpg_key = get_gpg_key(user)
        if not gpg_key:
            printInfo('No PGP key found for user matching %s' % user)
            if args.allow_plaintext:
                if args.prompt_before_plaintext and not prompt_4_yes_no(
                        'Save unencrypted value'):
                    abort = True
            else:
                abort = True
        if abort:
            printError(
                'Will not create user %s as credentials cannot be saved. Use --allow-plaintext to enable storage of unencrypted credentials.'
            )
            continue

        # Prepare the output folder
        try:
            user_dir = 'users/%s' % user
            os.makedirs(user_dir)
        except Exception as e:
            printError(
                'Error, failed to create a temporary folder for user %s.' %
                user)
            continue

        # Determine the groups Groups
        groups = args.group_name
        if args.force_common_group:
            groups += args.common_groups
        # Add user to a category group


#        if 'category_groups' in default_args and len(default_args['category_groups']) > 0:
#            add_user_to_category_group(iam_client, args.group_name, default_args['category_groups'], category_regex, user)

# Create the user
        user_data = create_user(iam_client, user, groups, not args.no_password,
                                not args.no_mfa, not args.no_access_key)
        if 'errors' in user_data and len(user_data['errors']) > 0:
            printError('Error doing the following actions:\n%s' %
                       '\n'.join(' - %s' % action
                                 for action in user_data['errors']))

        # Save data
        if 'password' in user_data:
            gpg_and_write('%s/password.txt' % user_dir, user_data['password'],
                          gpg_key, args.always_trust)
        if 'AccessKeyId' in user_data:
            credentials = '[%s]\naws_access_key_id = %s\naws_secret_access_key = %s\n' % (
                profile_name, user_data['AccessKeyId'],
                user_data['SecretAccessKey'])
            # TODO: mfa
            gpg_and_write('%s/credentials' % user_dir, credentials, gpg_key,
                          args.always_trust)

        # Create a zip archive
        f = zipfile.ZipFile('users/%s.zip' % user, 'w')
        for root, dirs, files in os.walk(user_dir):
            for file in files:
                f.write(os.path.join(root, file))
        f.close()
        shutil.rmtree(user_dir)
Ejemplo n.º 8
0
def main():

    # Parse arguments
    parser = OpinelArgumentParser()
    parser.add_argument('debug')
    parser.add_argument('profile')
    parser.parser.add_argument('--role-name',
                                dest='role_name',
                                default=[],
                                nargs='+',
                                required=True,
                                help='Name of the role to be assumed in each child account.')
    parser.parser.add_argument('--ou',
                                dest='org_unit',
                                default=[],
                                nargs='+',
                                help='')
    parser.parser.add_argument('--profile-prefix',
                                dest='profile_prefix',
                                default=None,
                                help='')
    args = parser.parse_args()

    # Configure the debug level
    configPrintException(args.debug)

    # Check version of opinel
    if not check_requirements(os.path.realpath(__file__)):
        return 42

    # Arguments
    source_profile = AWSProfiles.get(args.profile)[0]
    credentials = read_creds(args.profile[0])
    if not credentials['AccessKeyId']:
        return 42

    # Get all accounts to setup
    api_client = connect_service('organizations', credentials)
    if len(args.org_unit) == 0:
        if prompt_4_yes_no('Do you want to specify a particular organizational unit'):
            ous = get_organizational_units(api_client)
            choice = prompt_4_value('Which OU do you want to configure IAM for', choices = [ou['Name'] for ou in ous], display_indices = True, no_confirm = True, return_index = True)
            account_list = list_accounts_for_parent(api_client, ous[choice]) 
        else:
            account_list = get_organization_accounts(api_client)

    # Setup the accounts
    organization_profiles = {'ready': [], 'notready': []}
    
    for account in account_list:
        printInfo('Validating role name in %s...' % account['Name'], newLine = False)
        profile_name = account['Name'].lower().replace(' ', '_')
        if args.profile_prefix:
            profile_name = '%s-%s' % (args.profile_prefix, profile_name)
        profile = AWSProfile(filename = aws_config_file, name = profile_name, account_id = account['Id'])
        profile.set_attribute('source_profile', source_profile.name)
        success = False
        for role_name in args.role_name:
            try:
                role_arn = 'arn:aws:iam::%s:role/%s' % (account['Id'], role_name)
                role_credentials = assume_role(role_name, credentials, role_arn, 'aws-recipes', silent = True )
                profile.set_attribute('role_arn', 'arn:aws:iam::%s:role/%s' % (account['Id'], role_name))
                profile.set_attribute('source_profile', source_profile.name)
                organization_profiles['ready'].append(profile)
                printInfo(' success')
                success = True
                break
            except Exception as e:
                pass
        if not success:
            printInfo(' failure')
            organization_profiles['notready'].append(profile)

    for profile in organization_profiles['ready']:
        profile.write()

    for profile in organization_profiles['notready']:
        printError('Failed to determine a valid role in %s (%s)' % (profile.name, profile.account_id))