diff --git a/prowler/providers/aws/services/organizations/organizations_scp_check_deny_regions/organizations_scp_check_deny_regions.py b/prowler/providers/aws/services/organizations/organizations_scp_check_deny_regions/organizations_scp_check_deny_regions.py index e1489f29..6962e979 100644 --- a/prowler/providers/aws/services/organizations/organizations_scp_check_deny_regions/organizations_scp_check_deny_regions.py +++ b/prowler/providers/aws/services/organizations/organizations_scp_check_deny_regions/organizations_scp_check_deny_regions.py @@ -29,6 +29,10 @@ class organizations_scp_check_deny_regions(Check): is_region_restricted_statement = False for policy in org.policies: + # We only check SCP policies here + if policy.type != "SERVICE_CONTROL_POLICY": + continue + # Statements are not always list statements = policy.content.get("Statement") if type(policy.content["Statement"]) is not list: diff --git a/prowler/providers/aws/services/organizations/organizations_service.py b/prowler/providers/aws/services/organizations/organizations_service.py index 333a8279..afc020e1 100644 --- a/prowler/providers/aws/services/organizations/organizations_service.py +++ b/prowler/providers/aws/services/organizations/organizations_service.py @@ -8,6 +8,13 @@ from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered from prowler.providers.aws.aws_provider import generate_regional_clients +available_organizations_policies = [ + "SERVICE_CONTROL_POLICY", + "TAG_POLICY", + "BACKUP_POLICY", + "AISERVICES_OPT_OUT_POLICY", +] + ################## Organizations class Organizations: @@ -36,13 +43,8 @@ class Organizations: organization_arn = organization_desc.get("Arn") organization_id = organization_desc.get("Id") organization_master_id = organization_desc.get("MasterAccountId") - organization_available_policy_types = organization_desc.get( - "AvailablePolicyTypes" - ) # Fetch policies for organization: - organization_policies = self.__list_policies__( - organization_available_policy_types - ) + organization_policies = self.__list_policies__() # Fetch delegated administrators for organization: organization_delegated_administrator = ( self.__list_delegated_administrators__() @@ -95,19 +97,17 @@ class Organizations: ) # I'm using list_policies instead of list_policies_for_target, because the last one only returns "Attached directly" policies but not "Inherited from..." policies. - def __list_policies__(self, enabled_policy_types): + def __list_policies__(self): logger.info("Organizations - List policies...") try: list_policies_paginator = self.client.get_paginator("list_policies") - for policy_type in enabled_policy_types: + for policy_type in available_organizations_policies: logger.info( "Organizations - List policies... - Type: %s", - policy_type.get("Type"), + policy_type, ) - for page in list_policies_paginator.paginate( - Filter=policy_type.get("Type") - ): + for page in list_policies_paginator.paginate(Filter=policy_type): for policy in page["Policies"]: policy_content = self.__describe_policy__(policy.get("Id")) policy_targets = self.__list_targets_for_policy__( diff --git a/prowler/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/__init__.py b/prowler/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prowler/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached.metadata.json b/prowler/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached.metadata.json new file mode 100644 index 00000000..77bbe13a --- /dev/null +++ b/prowler/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "aws", + "CheckID": "organizations_tags_policies_enabled_and_attached", + "CheckTitle": "Check if an AWS Organization has tags policies enabled and attached.", + "CheckType": [], + "ServiceName": "organizations", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:service::account-id:organization/organization-id", + "Severity": "medium", + "ResourceType": "Other", + "Description": "Check if an AWS Organization has tags policies enabled and attached.", + "Risk": "If an AWS Organization tags policies are not enabled and attached, it is not possible to enforce tags on AWS resources.", + "RelatedUrl": "", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable and attach AWS Organizations tags policies.", + "Url": "https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_tag-policies.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached.py b/prowler/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached.py new file mode 100644 index 00000000..f3dd44c9 --- /dev/null +++ b/prowler/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached.py @@ -0,0 +1,37 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.organizations.organizations_client import ( + organizations_client, +) + + +class organizations_tags_policies_enabled_and_attached(Check): + def execute(self): + findings = [] + + for org in organizations_client.organizations: + report = Check_Report_AWS(self.metadata()) + report.resource_id = org.id + report.resource_arn = org.arn + report.region = organizations_client.region + report.status = "FAIL" + report.status_extended = ( + "AWS Organizations is not in-use for this AWS Account" + ) + if org.status == "ACTIVE": + if org.policies is None: + # Access Denied to list_policies + continue + for policy in org.policies: + # We only check SCP policies here + if policy.type != "TAG_POLICY": + continue + + report.status_extended = f"AWS Organization {org.id} has tag policies enabled but not attached" + + if policy.targets: + report.status = "PASS" + report.status_extended = f"AWS Organization {org.id} has tag policies enabled and attached to an AWS account" + + findings.append(report) + + return findings diff --git a/tests/providers/aws/services/organizations/organizations_service_test.py b/tests/providers/aws/services/organizations/organizations_service_test.py index caf66ce6..54b33851 100644 --- a/tests/providers/aws/services/organizations/organizations_service_test.py +++ b/tests/providers/aws/services/organizations/organizations_service_test.py @@ -82,7 +82,6 @@ class Test_Organizations_Service: audit_info = self.set_mocked_audit_info() organizations = Organizations(audit_info) # Tests - assert len(organizations.policies) == 2 for policy in organizations.policies: if policy.arn == response["Policy"]["PolicySummary"]["Arn"]: assert policy.type == "SERVICE_CONTROL_POLICY" diff --git a/tests/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached_test.py b/tests/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached_test.py new file mode 100644 index 00000000..f6fdcba8 --- /dev/null +++ b/tests/providers/aws/services/organizations/organizations_tags_policies_enabled_and_attached/organizations_tags_policies_enabled_and_attached_test.py @@ -0,0 +1,143 @@ +from unittest import mock + +from prowler.providers.aws.services.organizations.organizations_service import ( + Organization, + Policy, +) + +AWS_REGION = "us-east-1" + +# Moto: NotImplementedError: The TAG_POLICY policy type has not been implemented +# Needs to Mock manually + + +class Test_organizations_tags_policies_enabled_and_attached: + def test_organization_no_organization(self): + organizations_client = mock.MagicMock + organizations_client.region = AWS_REGION + organizations_client.organizations = [ + Organization( + arn="", + id="AWS Organization", + status="NOT_AVAILABLE", + master_id="", + ) + ] + + with mock.patch( + "prowler.providers.aws.services.organizations.organizations_service.Organizations", + new=organizations_client, + ): + # Test Check + from prowler.providers.aws.services.organizations.organizations_tags_policies_enabled_and_attached.organizations_tags_policies_enabled_and_attached import ( + organizations_tags_policies_enabled_and_attached, + ) + + check = organizations_tags_policies_enabled_and_attached() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "AWS Organizations is not in-use for this AWS Account" + ) + assert result[0].resource_id == "AWS Organization" + assert result[0].resource_arn == "" + assert result[0].region == AWS_REGION + + def test_organization_with_tag_policies_not_attached(self): + organizations_client = mock.MagicMock + organizations_client.region = AWS_REGION + organizations_client.organizations = [ + Organization( + id="o-1234567890", + arn="arn:aws:organizations::1234567890:organization/o-1234567890", + status="ACTIVE", + master_id="1234567890", + policies=[ + Policy( + id="p-1234567890", + arn="arn:aws:organizations::1234567890:policy/o-1234567890/p-1234567890", + type="TAG_POLICY", + aws_managed=False, + content={"tags": {"Owner": {}}}, + targets=[], + ) + ], + delegated_administrators=None, + ) + ] + + with mock.patch( + "prowler.providers.aws.services.organizations.organizations_tags_policies_enabled_and_attached.organizations_tags_policies_enabled_and_attached.organizations_client", + new=organizations_client, + ): + # Test Check + from prowler.providers.aws.services.organizations.organizations_tags_policies_enabled_and_attached.organizations_tags_policies_enabled_and_attached import ( + organizations_tags_policies_enabled_and_attached, + ) + + check = organizations_tags_policies_enabled_and_attached() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "AWS Organization o-1234567890 has tag policies enabled but not attached" + ) + assert result[0].resource_id == "o-1234567890" + assert ( + result[0].resource_arn + == "arn:aws:organizations::1234567890:organization/o-1234567890" + ) + assert result[0].region == AWS_REGION + + def test_organization_with_tag_policies_attached(self): + organizations_client = mock.MagicMock + organizations_client.region = AWS_REGION + organizations_client.organizations = [ + Organization( + id="o-1234567890", + arn="arn:aws:organizations::1234567890:organization/o-1234567890", + status="ACTIVE", + master_id="1234567890", + policies=[ + Policy( + id="p-1234567890", + arn="arn:aws:organizations::1234567890:policy/o-1234567890/p-1234567890", + type="TAG_POLICY", + aws_managed=False, + content={"tags": {"Owner": {}}}, + targets=["1234567890"], + ) + ], + delegated_administrators=None, + ) + ] + + with mock.patch( + "prowler.providers.aws.services.organizations.organizations_tags_policies_enabled_and_attached.organizations_tags_policies_enabled_and_attached.organizations_client", + new=organizations_client, + ): + # Test Check + from prowler.providers.aws.services.organizations.organizations_tags_policies_enabled_and_attached.organizations_tags_policies_enabled_and_attached import ( + organizations_tags_policies_enabled_and_attached, + ) + + check = organizations_tags_policies_enabled_and_attached() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "AWS Organization o-1234567890 has tag policies enabled and attached to an AWS account" + ) + assert result[0].resource_id == "o-1234567890" + assert ( + result[0].resource_arn + == "arn:aws:organizations::1234567890:organization/o-1234567890" + ) + assert result[0].region == AWS_REGION