def clean_iam_access_keys(self, batch=False): """ Class method to remediate IAM User access keys which are not used """ main_account = Account(region=config.aws.region) ddb_table = main_account.resource("dynamodb").Table( self.config.iamUserInactiveKeys.ddb_table_name) retention_period = self.config.iamUserInactiveKeys.remediation_retention_period jira = JiraReporting(self.config) slack = SlackNotification(self.config) for account_id, account_name in self.config.aws.accounts.items(): logging.debug("* Account Name:" + account_name + " :::Account ID:::" + account_id) issues = IssueOperations.get_account_open_issues( ddb_table, account_id, IAMKeyInactiveIssue) for issue in issues: key_id = issue.issue_id username = issue.issue_details.username user_in_whitelist = self.config.iamUserInactiveKeys.in_whitelist( account_id, username) key_in_whitelist = self.config.iamUserInactiveKeys.in_whitelist( account_id, key_id) if user_in_whitelist or key_in_whitelist: logging.debug( f"Skipping '{key_id} / {username}' (in whitelist)") continue if issue.timestamps.reported is None: logging.debug( f"Skipping '{key_id} / {username}' (was not reported)") continue if issue.timestamps.remediated is not None: logging.debug( f"Skipping '{key_id} / {username}' (has been already remediated)" ) continue updated_date = issue.timestamp_as_datetime no_of_days_issue_created = (self.config.now - updated_date).days if no_of_days_issue_created >= retention_period: try: if not batch and \ not confirm(f"Do you want to remediate inactive access key '{key_id} / {username}'", False): continue account = Account( id=account_id, name=account_name, role_name=self.config.aws.role_name_reporting) if account.session is None: continue logging.debug( f"Remediating inactive access key '{key_id} / {username}'" ) remediation_succeed = True try: IAMOperations.disable_access_key( account.client("iam"), username, key_id) comment = ( f"Inactive access key '{key_id} / {username}' issue " f"in '{account_name} / {account_id}' account " f"was remediated by hammer") except Exception: remediation_succeed = False logging.exception( "Failed to disable '{key_id} / {username}' inactive access key" ) comment = ( f"Failed to remediate inactive access key '{key_id} / {username}' issue " f"in '{account_name} / {account_id}' account " f"due to some limitations. Please, check manually" ) jira.remediate_issue( ticket_id=issue.jira_details.ticket, comment=comment, reassign=remediation_succeed, ) slack.report_issue( msg=f"{comment}" f"{' (' + jira.ticket_url(issue.jira_details.ticket) + ')' if issue.jira_details.ticket else ''}", account_id=account_id, ) IssueOperations.set_status_remediated(ddb_table, issue) except Exception: logging.exception( f"Error occurred while disabling '{username} / {key_id}' " f"in '{account_name} / {account_id}'") else: logging.debug( f"Skipping '{key_id} / {username}' " f"({retention_period - no_of_days_issue_created} days before remediation)" )
def build_instances_table(self, iam_client, instances): instance_details = "" instance_profile_details = [] # security group has associated instances in_use = False # security group has associated instances with public ip in public subnet public = False # security group has associated instances with public ip in private subnet blind_public = False owners = [] bus = [] products = [] separator = "\n" table_limit_reached = False if len(instances) > 0: in_use = True instance_details += ( f"||Instance ID||State" f"||Private Ip Address||Public Ip Address" f"||Owner||Business unit||Product||Component" f"||Subnet||\n") for ec2_instance in instances: if len(ec2_instance.public_ips) > 0: if ec2_instance.public_subnet: public = True else: blind_public = True owner = ec2_instance.tags.get('owner') bu = ec2_instance.tags.get('bu') product = ec2_instance.tags.get('product') component = ec2_instance.tags.get('component') if self.config.jira.text_field_character_limit == 0 or \ len(instance_details) < (self.config.jira.text_field_character_limit * 0.5): instance_details += ( f"|{ec2_instance.id}|{ec2_instance.state}" f"|{list_converter(ec2_instance.private_ips)}" f"|{list_converter(ec2_instance.public_ips)}" f"|{empty_converter(owner)}" f"|{empty_converter(bu)}" f"|{empty_converter(product)}" f"|{empty_converter(component)}" f"|{'public' if ec2_instance.public_subnet else 'private'}|\n" ) instance_profile_id = ec2_instance.iam_profile_id if instance_profile_id is not None: try: public_role_policies = IAMOperations.get_instance_profile_policy_details(iam_client, instance_profile_id) except Exception: logging.exception("Failed to get instance profile policy details") public_role_policies = [] if len(public_role_policies) > 0: for public_role in public_role_policies: instance_profile_details.append( f"|{ec2_instance.id}|{public_role.role_name}" f"|{public_role.policy_name}" f"|{list_converter(public_role.actions, separator)}|\n" ) elif not table_limit_reached: table_limit_reached = True owners.append(owner) bus.append(bu) products.append(product) instance_details = f"*Ec2 Instances{' (limited subset)' if table_limit_reached else ''}*:\n{instance_details}" # remove empty and count number of occurrences for each owner/bu/product owners = Counter([x for x in owners if x]) bus = Counter([x for x in bus if x]) products = Counter([x for x in products if x]) # find owner/bu/product with max occurrences owner = max(owners, key=lambda owner: owners[owner]) if owners else None bu = max(bus, key=lambda bu: bus[bu]) if bus else None product = max(products, key=lambda product: products[product]) if products else None # logging.debug(f"bu={bu}") # logging.debug(f"product={product}") if len(instance_profile_details) > 0: instance_profile_details = ( f"\n*Instance Role Unsafe Policies:*\n" f"||Instance Id||Role Name||Policy Name||Unsafe actions||\n" ) + "".join(instance_profile_details) + "\n" return instance_details, instance_profile_details, in_use, public, blind_public, owner, bu, product