def test_of_email_body_problematic_resources_with_reason( self, mock_get_account_alias): mock_get_account_alias.return_value = "test-account" unwanted_resources = [ Resource( 42, "ec2 instance", "12345", "date1", "us", reason= 'Please follow the principal of least privilege and do not use Action : *' ), Resource(42, "ec2 volume", "3312345", "date2", "us") ] problematic_resources = [] dry_run = False test_status_mail_plugin = StatusMailPlugin( unwanted_resources, problematic_resources, dry_run, region=self.test_region, sender=self.test_sender, recipients=self.test_recipients) body = test_status_mail_plugin.body expected_body = (EXPECTED_PART_HEADER + EXPECTED_PART_NO_DRY_RUN + EXPECTED_PART_UNWANTED_FILLED_WITH_REASON + EXPECTED_PART_FOOTER) self.maxDiff = None self.assertEqual(body, expected_body)
def fetch_unwanted_resources(self): return [ Resource(Mock(), "ec2 instance", "123456789", datetime.datetime.now(), "us"), Resource(Mock(), "ec2 volume", "33123456789", datetime.datetime.now(), "us") ]
def _fetch_unwanted_resources(self, region_name): client = boto3.client('acm', region_name=region_name) response = client.list_certificates(CertificateStatuses=['ISSUED']) certificate_arns = [ summary['CertificateArn'] for summary in response['CertificateSummaryList'] ] limit = datetime.datetime.now() + datetime.timedelta( days=MIN_VALID_DAYS) for certificate_arn in certificate_arns: response = client.describe_certificate( CertificateArn=certificate_arn) certificate = response['Certificate'] # Remove time zone information so we can compare with normal datetimes. not_after = datetime.datetime.replace(certificate['NotAfter'], tzinfo=None) if not_after > limit: continue resource_wrapper = Resource( resource="Certificate for " + certificate['DomainName'], resource_type=self.resource_type, resource_id=certificate_arn, creation_date=certificate.get('CreatedAt', certificate.get('ImportedAt')), region="global", reason="will expired soon") yield resource_wrapper
def test_fetch_unwanted_resources_returns_true_if_forbidden_action(self): iam_policy = 'iam.IamPolicy' policyVersionMock = MagicMock( document={ 'Statement': [{ 'Action': ['s3:test3', '*:*'], 'Resource': 'aws:s2222' }] }) self.iamResourceMock.PolicyVersion.return_value = policyVersionMock policy = { 'IsTruncated': False, 'Policies': [{ 'Arn': 'arn:aws:iam:123456789', 'DefaultVersionId': 'v1', 'CreateDate': '2012-06-12' }] } expected_unwanted_user = Resource( resource=policy['Policies'][0], resource_type=iam_policy, resource_id=policy['Policies'][0]['Arn'], creation_date=policy['Policies'][0]['CreateDate'], region='global', reason=self.reason) self.iamClientMock.list_policies.return_value = policy unwanted_resource = self.policy_handler.fetch_unwanted_resources() self.assertEqual(list(unwanted_resource)[0], expected_unwanted_user)
def fetch_unwanted_resources(self): client = self.get_client() response = client.list_buckets() buckets = [(bucket['Name'], bucket['CreationDate']) for bucket in response['Buckets']] region_names = [region.name for region in self.regions] for bucket_name, creation_date in buckets: try: response = client.get_bucket_location(Bucket=bucket_name) except Exception: # This happens when a bucket was deleted shortly after we # found it. E.g. during concurrent integration tests. self.logger.exception("Failed to get location for bucket %r:", bucket_name) continue region_name = self.map_location(response['LocationConstraint']) if region_name not in region_names: self.logger.debug("Bucket %s in region %s is OK.", bucket_name, region_name) continue self.logger.info("Reporting bucket %s in region %s as unwanted.", bucket_name, region_name) resource_wrapper = Resource(resource="Bucket " + bucket_name, resource_type=self.resource_type, resource_id=bucket_name, creation_date=creation_date, region=region_name) yield resource_wrapper
def test_skip_deletion_in_dry_run(self): resource = Resource(self.stack_mock, self.resource_type, self.stack_mock.stack_id, self.stack_mock.creation_time, self.negative_fake_region.name) self.cloudformation_handler.dry_run = True self.cloudformation_handler.delete(resource) self.assertFalse(self.cloudformation_mock.connect_to_region. return_value.delete_stack.called)
def setUp(self): self.test_recipients = ["*****@*****.**", "*****@*****.**"] self.unwanted_resources = [ Resource(42, "ec2 instance", "12345", "date1", "us"), Resource(42, "ec2 volume", "3312345", "date2", "us")] self.problematic_resources = [ Resource(23, "ec2 instance", "67890", "date1", "us"), Resource(23, "ec2 volume", "1112345", "date2", "us")] self.dry_run = True self.reason = 'Do not do it' self.test_region = "eu-west-1" self.test_sender = "*****@*****.**" self.test_status_mail_plugin = StatusMailPlugin(self.unwanted_resources, self.problematic_resources, self.dry_run, region=self.test_region, sender=self.test_sender, recipients=self.test_recipients)
def test_skip_deletion_if_already_deleted(self): self.rds_instance.dry_run = False self.instance_mock["DBInstanceStatus"] = rds2.DELETION_STATUS resource = Resource(self.instance_mock, self.resource_type, self.instance_mock["DBInstanceIdentifier"], self.instance_mock["InstanceCreateTime"], self.negative_fake_region.name) self.assertRaises(Warning, self.rds_instance.delete, resource)
def test_skip_deletion_in_dry_run(self): self.rds_instance.dry_run = True resource = Resource(self.instance_mock, self.resource_type, self.instance_mock["DBInstanceIdentifier"], self.instance_mock["InstanceCreateTime"], self.negative_fake_region.name) deleted_resource = self.rds_instance.delete(resource) self.assertEqual(None, deleted_resource)
def test_skip_deletion_if_autogenerated(self): self.rds_snapshot.dry_run = False self.snapshot_mock["SnapshotType"] = rds2.AUTOMATED_STATUS resource = Resource(self.snapshot_mock, self.resource_type, self.snapshot_mock["DBSnapshotIdentifier"], self.snapshot_mock["SnapshotCreateTime"], self.negative_fake_region.name) self.assertRaises(Warning, self.rds_snapshot.delete, resource)
def test_skip_deletion_in_dry_run(self): self.rds_snapshot.dry_run = True resource = Resource(self.snapshot_mock, self.resource_type, self.snapshot_mock["DBSnapshotIdentifier"], self.snapshot_mock["SnapshotCreateTime"], self.negative_fake_region.name) deleted_resource = self.rds_snapshot.delete(resource) self.assertEqual(None, deleted_resource)
def test_does_delete_if_not_dry_run(self): resource = Resource(self.stack_mock, self.resource_type, self.stack_mock.stack_id, self.stack_mock.creation_time, self.negative_fake_region.name) self.cloudformation_handler.dry_run = False self.cloudformation_handler.delete(resource) self.logger_mock.getLogger.return_value.info.assert_called_with( DELETION_STATEMENT % self.stack_mock.stack_name) self.assertTrue(self.cloudformation_mock.connect_to_region. return_value.delete_stack.called)
def test_skip_deletion_if_already_deleted(self): self.stack_mock.stack_status = "DELETE_COMPLETE" resource = Resource(self.stack_mock, self.resource_type, self.stack_mock.stack_id, self.stack_mock.creation_time, self.negative_fake_region.name) self.cloudformation_handler.dry_run = False self.assertRaises(Warning, self.cloudformation_handler.delete, resource) self.assertFalse(self.cloudformation_mock.connect_to_region. return_value.delete_stack.called)
def test_delete(self): resource = Resource(self.instance_mock, self.resource_type, self.instance_mock.id, self.instance_mock.launch_time, self.negative_fake_region.name) connection = self.ec2_mock.connect_to_region.return_value e = boto.exception.EC2ResponseError(412, 'boom') e.message = "test" connection.terminate_instances.side_effect = e self.assertRaises(Warning, self.ec2_handler.delete, resource)
def test_handle_service(self): handler = Mock() handler.fetch_unwanted_resources.return_value = [ Resource("foo", "test_type", "test_id", datetime.datetime.now(), "test_region") ] handler.to_string.return_value = "test handler" self.monocyte.handle_service(handler) self.logger_mock.getLogger.return_value.warning.assert_called_with( REGION_NOT_ALLOWED)
def fetch_unwanted_resources(self): for user in self.get_users(): if self.is_user_in_whitelist(user) or self.is_user_in_ignored_resources(user): self.logger.info('IGNORE user with {0}'.format(user['Arn'])) continue unwanted_resource = Resource(resource=user, resource_type=self.resource_type, resource_id=user['Arn'], creation_date=user['CreateDate'], region='global', reason=self.email_string()) yield unwanted_resource
def fetch_unwanted_resources(self): for policy in self.get_policies(): if self.is_arn_in_whitelist(policy): continue policy_document = self.get_policy_document(policy['Arn'], policy['DefaultVersionId']) actions = self.gather_actions(policy_document) if self.check_policy_action_for_forbidden_string(actions): unwanted_resource = Resource(resource=policy, resource_type=self.resource_type, resource_id=policy['Arn'], creation_date=policy['CreateDate'], region='global', reason=self.email_string()) yield unwanted_resource
def fetch_unwanted_resources(self): for region_name in self.region_names: connection = rds2.connect_to_region(region_name) resources = connection.describe_db_snapshots() or [] for resource in resources["DescribeDBSnapshotsResponse"]["DescribeDBSnapshotsResult"]["DBSnapshots"]: resource_wrapper = Resource(resource=resource, resource_type=self.resource_type, resource_id=resource["DBSnapshotIdentifier"], creation_date=resource["SnapshotCreateTime"], region=region_name) if resource['DBSnapshotIdentifier'] in self.ignored_resources: self.logger.info('IGNORE ' + self.to_string(resource_wrapper)) continue yield resource_wrapper
def test_fetch_unwanted_resources_returns_resource_wrapper_if_users_are_not_emtpy( self): iam_user = '******' expected_unwanted_user = Resource( resource=self.user, resource_type=iam_user, resource_id=self.user['Arn'], creation_date=self.user['CreateDate'], region='global', reason="Do not use user with static credentials.") unwanted_users = self.user_handler.fetch_unwanted_resources() self.assertEqual(list(unwanted_users)[0], expected_unwanted_user) self.assertEqual(len(list(unwanted_users)), 0)
def fetch_unwanted_resources(self): for region_name in self.region_names: connection = ec2.connect_to_region(region_name) resources = connection.get_all_volumes() or [] for resource in resources: resource_wrapper = Resource(resource=resource, resource_type=self.resource_type, resource_id=resource.id, creation_date=resource.create_time, region=region_name) if resource.id in self.ignored_resources: self.logger.info('IGNORE ' + self.to_string(resource_wrapper)) continue yield resource_wrapper
def fetch_unwanted_resources(self): for role in self.get_all_iam_roles_in_account(): if self.is_arn_in_whitelist(role): continue policies = self.get_all_inline_policies_for_role(role['RoleName']) for policy in policies: actions = self.gather_actions(policy.policy_document) if self.check_policy_action_for_forbidden_string( actions): unwanted_resource = Resource(resource=role, resource_type=self.resource_type, resource_id=role['Arn'], creation_date=role['CreateDate'], region='global', reason=self.email_string()) yield unwanted_resource
def test_fetch_unwanted_resources_return_true_if_action_and_resource_string_found( self): inline_policy = 'iam.InlinePolicy' role_mock = MagicMock() sample_role = { 'Arn': 'arn:aws:iam::123456789101:role/foo-bar-file', 'AssumeRolePolicyDocument': { 'Statement': [{ 'Action': 'sts:AssumeRole', 'Effect': 'Allow', 'Principal': { 'AWS': 'arn:aws:iam::9876543210:root' }, 'Sid': '' }], 'Version': '2012-10-17' }, 'CreateDate': '01.01.1989', 'Path': '/', 'RoleId': 'FOOAJ4DHXC5V55TMCIBAR', 'RoleName': 'foo-bar-file' } list_role_mock = {'Roles': [sample_role]} self.iamClientMock.list_roles.return_value = list_role_mock policy_mock = MagicMock( policy_document={ 'Statement': [{ 'Action': ['elasticloadbalancing:test3', '*:*'], 'Resource': ['arn:aws:s3:::test3'] }] }) role_mock.policies.all.return_value = [policy_mock] self.iamResourceMock.Role.return_value = role_mock expected_unwanted_role = Resource( resource=sample_role, resource_type=inline_policy, resource_id=sample_role['Arn'], creation_date=sample_role['CreateDate'], region='global', reason=self.reason) unwanted_resource = self.policy_handler.fetch_unwanted_resources() unwanted_resource_list = list(unwanted_resource) self.assertEqual(len(list(unwanted_resource_list)), 1) self.assertEqual(expected_unwanted_role, unwanted_resource_list[0])
def test_does_delete_if_not_dry_run(self): self.rds_snapshot.dry_run = False resource = Resource(self.snapshot_mock, self.resource_type, self.snapshot_mock["DBSnapshotIdentifier"], self.snapshot_mock["SnapshotCreateTime"], self.negative_fake_region.name) self.rds2_mock.connect_to_region.return_value.delete_db_snapshot.return_value = \ self._given_delete_db_snapshot_response() self.rds_snapshot.delete(resource) self.logger_mock.getLogger.return_value.info.assert_called_with( rds2.DELETION_STATEMENT % self.snapshot_mock["DBSnapshotIdentifier"]) self.assertEqual(self.snapshot_mock["DBSnapshotIdentifier"], resource.wrapped["DBSnapshotIdentifier"])
def fetch_unwanted_resources(self): for region_name in self.region_names: connection = dynamodb2.connect_to_region(region_name) names = connection.list_tables(limit=100) or {} for name in names.get("TableNames"): resource = connection.describe_table(name) resource_wrapper = Resource( resource=resource["Table"], resource_type=self.resource_type, resource_id=resource["Table"]["TableName"], creation_date=resource["Table"]["CreationDateTime"], region=region_name) if name in self.ignored_resources: self.logger.info('IGNORE ' + self.to_string(resource_wrapper)) continue yield resource_wrapper
def fetch_unwanted_resources(self): for region_name in self.region_names: connection = cloudformation.connect_to_region(region_name) unwanted_states = set(connection.valid_states) unwanted_states.remove("DELETE_COMPLETE") resources = connection.list_stacks( stack_status_filters=list(unwanted_states)) or [] for resource in resources: resource_wrapper = Resource( resource=resource, resource_type=self.resource_type, resource_id=resource.stack_id, creation_date=resource.creation_time, region=region_name) if resource.stack_name in self.ignored_resources: self.logger.info('IGNORE ' + self.to_string(resource_wrapper)) continue yield resource_wrapper