From e75022763ce230b319ec03c1cd15331673276a4c Mon Sep 17 00:00:00 2001 From: Gabriel Soltz Date: Tue, 11 Apr 2023 14:15:39 +0200 Subject: [PATCH] feat(checks): New iam_securityaudit_role_created (#2182) --- .../__init__.py | 0 ...m_securityaudit_role_created.metadata.json | 34 ++++++ .../iam_securityaudit_role_created.py | 19 +++ .../providers/aws/services/iam/iam_service.py | 24 ++-- .../iam_support_role_created.py | 4 +- .../iam_securityaudit_role_created_test.py | 110 ++++++++++++++++++ .../aws/services/iam/iam_service_test.py | 53 ++++++++- 7 files changed, 229 insertions(+), 15 deletions(-) create mode 100644 prowler/providers/aws/services/iam/iam_securityaudit_role_created/__init__.py create mode 100644 prowler/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created.metadata.json create mode 100644 prowler/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created.py create mode 100644 tests/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created_test.py diff --git a/prowler/providers/aws/services/iam/iam_securityaudit_role_created/__init__.py b/prowler/providers/aws/services/iam/iam_securityaudit_role_created/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prowler/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created.metadata.json b/prowler/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created.metadata.json new file mode 100644 index 00000000..d03c1e91 --- /dev/null +++ b/prowler/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created.metadata.json @@ -0,0 +1,34 @@ +{ + "Provider": "aws", + "CheckID": "iam_securityaudit_role_created", + "CheckTitle": "Ensure a Security Audit role has been created to conduct security audits", + "CheckType": [ + "Software and Configuration Checks", + "Industry and Regulatory Standards", + "CIS AWS Foundations Benchmark" + ], + "ServiceName": "iam", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id", + "Severity": "low", + "ResourceType": "AwsIamRole", + "Description": "Ensure a Security Audit role has been created to conduct security audits", + "Risk": "Creating an IAM role with a security audit policy provides a clear separation of duties between the security team and other teams within the organization. This helps to ensure that security-related activities are performed by authorized individuals with the appropriate expertise and access permissions.", + "RelatedUrl": "", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Create an IAM role for conduct security audits with AWS.", + "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_security-auditor" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created.py b/prowler/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created.py new file mode 100644 index 00000000..1bc337d3 --- /dev/null +++ b/prowler/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created.py @@ -0,0 +1,19 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.iam.iam_client import iam_client + + +class iam_securityaudit_role_created(Check): + def execute(self) -> Check_Report_AWS: + findings = [] + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_id = "SecurityAudit" + report.resource_arn = "arn:aws:iam::aws:policy/SecurityAudit" + if iam_client.entities_role_attached_to_securityaudit_policy: + report.status = "PASS" + report.status_extended = f"SecurityAudit policy attached to role {iam_client.entities_role_attached_to_securityaudit_policy[0]['RoleName']}" + else: + report.status = "FAIL" + report.status_extended = "SecurityAudit policy is not attached to any role" + findings.append(report) + return findings diff --git a/prowler/providers/aws/services/iam/iam_service.py b/prowler/providers/aws/services/iam/iam_service.py index 7deef976..53b6214e 100644 --- a/prowler/providers/aws/services/iam/iam_service.py +++ b/prowler/providers/aws/services/iam/iam_service.py @@ -53,8 +53,15 @@ class IAM: self.__list_inline_user_policies__() self.__list_mfa_devices__() self.password_policy = self.__get_password_policy__() - self.entities_attached_to_support_roles = ( - self.__get_entities_attached_to_support_roles__() + support_policy_arn = ( + "arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy" + ) + self.entities_role_attached_to_support_policy = ( + self.__list_entities_role_for_policy__(support_policy_arn) + ) + securityaudit_policy_arn = "arn:aws:iam::aws:policy/SecurityAudit" + self.entities_role_attached_to_securityaudit_policy = ( + self.__list_entities_role_for_policy__(securityaudit_policy_arn) ) self.policies = self.__list_policies__() self.__list_policies_version__(self.policies) @@ -337,21 +344,18 @@ class IAM: f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - def __get_entities_attached_to_support_roles__(self): + def __list_entities_role_for_policy__(self, policy_arn): try: - support_roles = [] - support_entry_policy_arn = ( - "arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy" - ) - support_roles = self.client.list_entities_for_policy( - PolicyArn=support_entry_policy_arn, EntityFilter="Role" + roles = [] + roles = self.client.list_entities_for_policy( + PolicyArn=policy_arn, EntityFilter="Role" )["PolicyRoles"] except Exception as error: logger.error( f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) finally: - return support_roles + return roles def __list_policies__(self): try: diff --git a/prowler/providers/aws/services/iam/iam_support_role_created/iam_support_role_created.py b/prowler/providers/aws/services/iam/iam_support_role_created/iam_support_role_created.py index 75c1b8fe..ec69b65a 100644 --- a/prowler/providers/aws/services/iam/iam_support_role_created/iam_support_role_created.py +++ b/prowler/providers/aws/services/iam/iam_support_role_created/iam_support_role_created.py @@ -11,9 +11,9 @@ class iam_support_role_created(Check): report.resource_arn = ( "arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy" ) - if iam_client.entities_attached_to_support_roles: + if iam_client.entities_role_attached_to_support_policy: report.status = "PASS" - report.status_extended = f"Support policy attached to role {iam_client.entities_attached_to_support_roles[0]['RoleName']}" + report.status_extended = f"Support policy attached to role {iam_client.entities_role_attached_to_support_policy[0]['RoleName']}" else: report.status = "FAIL" report.status_extended = "Support policy is not attached to any role" diff --git a/tests/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created_test.py b/tests/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created_test.py new file mode 100644 index 00000000..b88753bd --- /dev/null +++ b/tests/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created_test.py @@ -0,0 +1,110 @@ +from json import dumps +from re import search +from unittest import mock + +from boto3 import client, session +from moto import mock_iam + +from prowler.providers.aws.lib.audit_info.audit_info import AWS_Audit_Info +from prowler.providers.aws.services.iam.iam_service import IAM + + +class Test_iam_securityaudit_role_created: + + # Mocked Audit Info + def set_mocked_audit_info(self): + audit_info = AWS_Audit_Info( + session_config=None, + original_session=None, + audit_session=session.Session( + profile_name=None, + botocore_session=None, + ), + audited_account=None, + audited_user_id=None, + audited_partition="aws", + audited_identity_arn=None, + profile=None, + profile_region="us-east-1", + credentials=None, + assumed_role_info=None, + audited_regions=None, + organizations_metadata=None, + audit_resources=None, + ) + return audit_info + + @mock_iam + def test_securityaudit_role_created(self): + audit_info = self.set_mocked_audit_info() + iam = client("iam") + role_name = "test_securityaudit_role_created" + assume_role_policy_document = { + "Version": "2012-10-17", + "Statement": { + "Sid": "test", + "Effect": "Allow", + "Principal": {"AWS": "*"}, + "Action": "sts:AssumeRole", + }, + } + iam.create_role( + RoleName=role_name, + AssumeRolePolicyDocument=dumps(assume_role_policy_document), + ) + iam.attach_role_policy( + RoleName=role_name, + PolicyArn="arn:aws:iam::aws:policy/SecurityAudit", + ) + + with mock.patch( + "prowler.providers.aws.lib.audit_info.audit_info.current_audit_info", + new=audit_info, + ): + with mock.patch( + "prowler.providers.aws.services.iam.iam_securityaudit_role_created.iam_securityaudit_role_created.iam_client", + new=IAM(audit_info), + ): + # Test Check + from prowler.providers.aws.services.iam.iam_securityaudit_role_created.iam_securityaudit_role_created import ( + iam_securityaudit_role_created, + ) + + check = iam_securityaudit_role_created() + result = check.execute() + assert result[0].status == "PASS" + assert search( + f"SecurityAudit policy attached to role {role_name}", + result[0].status_extended, + ) + assert result[0].resource_id == "SecurityAudit" + assert result[0].resource_arn == "arn:aws:iam::aws:policy/SecurityAudit" + assert result[0].region == "us-east-1" + + @mock_iam + def test_no_securityaudit_role_created(self): + audit_info = self.set_mocked_audit_info() + + with mock.patch( + "prowler.providers.aws.lib.audit_info.audit_info.current_audit_info", + new=audit_info, + ): + with mock.patch( + "prowler.providers.aws.services.iam.iam_securityaudit_role_created.iam_securityaudit_role_created.iam_client", + new=IAM(audit_info), + ): + # Test Check + from prowler.providers.aws.services.iam.iam_securityaudit_role_created.iam_securityaudit_role_created import ( + iam_securityaudit_role_created, + ) + + check = iam_securityaudit_role_created() + result = check.execute() + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "SecurityAudit policy is not attached to any role" + ) + assert result[0].resource_id == "SecurityAudit" + assert result[0].resource_arn == "arn:aws:iam::aws:policy/SecurityAudit" + assert result[0].region == "us-east-1" diff --git a/tests/providers/aws/services/iam/iam_service_test.py b/tests/providers/aws/services/iam/iam_service_test.py index 9d512570..9352a19b 100644 --- a/tests/providers/aws/services/iam/iam_service_test.py +++ b/tests/providers/aws/services/iam/iam_service_test.py @@ -528,7 +528,7 @@ class Test_IAM_Service: audit_info = self.set_mocked_audit_info() iam = IAM(audit_info) - assert len(iam.entities_attached_to_support_roles) == 0 + assert len(iam.entities_role_attached_to_support_policy) == 0 @mock_iam def test__get_entities_attached_to_support_roles__(self): @@ -559,8 +559,55 @@ class Test_IAM_Service: audit_info = self.set_mocked_audit_info() iam = IAM(audit_info) - assert len(iam.entities_attached_to_support_roles) == 1 - assert iam.entities_attached_to_support_roles[0]["RoleName"] == role_name + assert len(iam.entities_role_attached_to_support_policy) == 1 + assert iam.entities_role_attached_to_support_policy[0]["RoleName"] == role_name + + @mock_iam + def test__get_entities_attached_to_securityaudit_roles__no_roles(self): + iam_client = client("iam") + _ = iam_client.list_entities_for_policy( + PolicyArn="arn:aws:iam::aws:policy/SecurityAudit", + EntityFilter="Role", + )["PolicyRoles"] + + audit_info = self.set_mocked_audit_info() + iam = IAM(audit_info) + assert len(iam.entities_role_attached_to_securityaudit_policy) == 0 + + @mock_iam + def test__get_entities_attached_to_securityaudit_roles__(self): + iam_client = client("iam") + role_name = "test_securityaudit" + assume_role_policy_document = { + "Version": "2012-10-17", + "Statement": { + "Sid": "test", + "Effect": "Allow", + "Principal": {"AWS": "*"}, + "Action": "sts:AssumeRole", + }, + } + iam_client.create_role( + RoleName=role_name, + AssumeRolePolicyDocument=dumps(assume_role_policy_document), + ) + iam_client.attach_role_policy( + RoleName=role_name, + PolicyArn="arn:aws:iam::aws:policy/SecurityAudit", + ) + + iam_client.list_entities_for_policy( + PolicyArn="arn:aws:iam::aws:policy/SecurityAudit", + EntityFilter="Role", + )["PolicyRoles"] + + audit_info = self.set_mocked_audit_info() + iam = IAM(audit_info) + assert len(iam.entities_role_attached_to_securityaudit_policy) == 1 + assert ( + iam.entities_role_attached_to_securityaudit_policy[0]["RoleName"] + == role_name + ) @mock_iam def test___list_policies__(self):