From 880523076d335155a3b21ee8875955c73daa2d87 Mon Sep 17 00:00:00 2001 From: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Date: Mon, 30 Sep 2024 18:04:32 +0200 Subject: [PATCH] feat(networkfirewall): add new check `networkfirewall_policy_rule_group_associated` (#5225) --- .../__init__.py | 0 ...policy_rule_group_associated.metadata.json | 32 +++ ...rkfirewall_policy_rule_group_associated.py | 25 ++ .../networkfirewall_service.py | 71 +++-- ...etworkfirewall_deletion_protection_test.py | 2 + .../networkfirewall_in_all_vpc_test.py | 2 + ...ewall_policy_rule_group_associated_test.py | 244 ++++++++++++++++++ 7 files changed, 349 insertions(+), 27 deletions(-) create mode 100644 prowler/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/__init__.py create mode 100644 prowler/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/networkfirewall_policy_rule_group_associated.metadata.json create mode 100644 prowler/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/networkfirewall_policy_rule_group_associated.py create mode 100644 tests/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/networkfirewall_policy_rule_group_associated_test.py diff --git a/prowler/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/__init__.py b/prowler/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/networkfirewall_policy_rule_group_associated.metadata.json b/prowler/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/networkfirewall_policy_rule_group_associated.metadata.json new file mode 100644 index 0000000000..34f204f637 --- /dev/null +++ b/prowler/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/networkfirewall_policy_rule_group_associated.metadata.json @@ -0,0 +1,32 @@ +{ + "Provider": "aws", + "CheckID": "networkfirewall_policy_rule_group_associated", + "CheckTitle": "Ensure Network Firewall Policies Have at Least One Rule Group Associated", + "CheckType": [ + "Software and Configuration Checks/Industry and Regulatory Standards/NIST 800-53" + ], + "ServiceName": "network-firewall", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:network-firewall::account-id:firewall-policy/policy-name", + "Severity": "medium", + "ResourceType": "AwsNetworkFirewallFirewallPolicy", + "Description": "This control checks whether a Network Firewall policy has any stateful or stateless rule groups associated. The control fails if stateless or stateful rule groups are not assigned.", + "Risk": "If a Network Firewall policy does not have at least one rule group associated, it may fail to properly filter and handle traffic, leaving the network vulnerable to unauthorized access and attacks.", + "RelatedUrl": "https://docs.aws.amazon.com/network-firewall/latest/developerguide/rule-groups.html", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/networkfirewall-controls.html#networkfirewall-3", + "Terraform": "" + }, + "Recommendation": { + "Text": "Regularly review your Network Firewall policies to ensure they have the necessary rule groups associated. Create and manage rule groups as needed to maintain effective traffic filtering and security.", + "Url": "https://docs.aws.amazon.com/network-firewall/latest/developerguide/firewall-policy-updating.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/networkfirewall_policy_rule_group_associated.py b/prowler/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/networkfirewall_policy_rule_group_associated.py new file mode 100644 index 0000000000..1efb722c41 --- /dev/null +++ b/prowler/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/networkfirewall_policy_rule_group_associated.py @@ -0,0 +1,25 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.networkfirewall.networkfirewall_client import ( + networkfirewall_client, +) + + +class networkfirewall_policy_rule_group_associated(Check): + def execute(self): + findings = [] + for firewall in networkfirewall_client.network_firewalls.values(): + report = Check_Report_AWS(self.metadata()) + report.region = firewall.region + report.resource_id = firewall.name + report.resource_arn = firewall.arn + report.resource_tags = firewall.tags + report.status = "PASS" + report.status_extended = f"Network Firewall {firewall.name} policy has at least one rule group associated." + + if not firewall.stateful_rule_groups and not firewall.stateless_rule_groups: + report.status = "FAIL" + report.status_extended = f"Network Firewall {firewall.name} policy does not have rule groups associated." + + findings.append(report) + + return findings diff --git a/prowler/providers/aws/services/networkfirewall/networkfirewall_service.py b/prowler/providers/aws/services/networkfirewall/networkfirewall_service.py index e012fec142..d140c367a2 100644 --- a/prowler/providers/aws/services/networkfirewall/networkfirewall_service.py +++ b/prowler/providers/aws/services/networkfirewall/networkfirewall_service.py @@ -5,14 +5,18 @@ from prowler.lib.scan_filters.scan_filters import is_resource_filtered from prowler.providers.aws.lib.service.service import AWSService -################## NetworkFirewall class NetworkFirewall(AWSService): def __init__(self, provider): # Call AWSService's __init__ super().__init__("network-firewall", provider) self.network_firewalls = {} self.__threading_call__(self._list_firewalls) - self._describe_firewall() + self.__threading_call__( + self._describe_firewall, self.network_firewalls.values() + ) + self.__threading_call__( + self._describe_firewall_policy, self.network_firewalls.values() + ) def _list_firewalls(self, regional_client): logger.info("Network Firewall - Listing Network Firewalls...") @@ -30,41 +34,51 @@ class NetworkFirewall(AWSService): self.network_firewalls[ network_firewall.get("FirewallArn", "") ] = Firewall( + arn=network_firewall.get("FirewallArn"), region=regional_client.region, name=network_firewall.get("FirewallName"), ) - except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - def _describe_firewall(self): + def _describe_firewall(self, network_firewall): logger.info("Network Firewall - Describe Network Firewalls...") try: - for arn, network_firewall in self.network_firewalls.items(): - regional_client = self.regional_clients[network_firewall.region] - try: - describe_firewall = regional_client.describe_firewall( - FirewallArn=arn, - )["Firewall"] - network_firewall.policy_arn = describe_firewall.get( - "FirewallPolicyArn" - ) - network_firewall.vpc_id = describe_firewall.get("VpcId") - network_firewall.tags = describe_firewall.get("Tags", []) - encryption_config = describe_firewall.get( - "EncryptionConfiguration", {} - ) - network_firewall.encryption_type = encryption_config.get("Type") - network_firewall.deletion_protection = describe_firewall.get( - "DeleteProtection", False - ) - except Exception as error: - logger.error( - f"Error describing firewall {network_firewall.arn} in region {network_firewall.region}: " - f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) + regional_client = self.regional_clients[network_firewall.region] + describe_firewall = regional_client.describe_firewall( + FirewallArn=network_firewall.arn, + )["Firewall"] + network_firewall.policy_arn = describe_firewall.get("FirewallPolicyArn") + network_firewall.vpc_id = describe_firewall.get("VpcId") + network_firewall.tags = describe_firewall.get("Tags", []) + encryption_config = describe_firewall.get("EncryptionConfiguration", {}) + network_firewall.encryption_type = encryption_config.get("Type") + network_firewall.deletion_protection = describe_firewall.get( + "DeleteProtection", False + ) + except Exception as error: + logger.error( + f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}" + ) + + def _describe_firewall_policy(self, network_firewall): + logger.info("Network Firewall - Describe Network Firewall Policies...") + try: + regional_client = self.regional_clients[network_firewall.region] + describe_firewall_policy = regional_client.describe_firewall_policy( + FirewallPolicyArn=network_firewall.policy_arn, + ) + firewall_policy = describe_firewall_policy.get("FirewallPolicy", {}) + network_firewall.stateless_rule_groups = [ + group.get("ResourceArn", "") + for group in firewall_policy.get("StatelessRuleGroupReferences", []) + ] + network_firewall.stateful_rule_groups = [ + group.get("ResourceArn", "") + for group in firewall_policy.get("StatefulRuleGroupReferences", []) + ] except Exception as error: logger.error( f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}" @@ -72,6 +86,7 @@ class NetworkFirewall(AWSService): class Firewall(BaseModel): + arn: str name: str region: str policy_arn: str = None @@ -79,3 +94,5 @@ class Firewall(BaseModel): tags: list = [] encryption_type: str = None deletion_protection: bool = False + stateless_rule_groups: list[str] = [] + stateful_rule_groups: list[str] = [] diff --git a/tests/providers/aws/services/networkfirewall/networkfirewall_deletion_protection/networkfirewall_deletion_protection_test.py b/tests/providers/aws/services/networkfirewall/networkfirewall_deletion_protection/networkfirewall_deletion_protection_test.py index 715ca1083d..1ca85fe987 100644 --- a/tests/providers/aws/services/networkfirewall/networkfirewall_deletion_protection/networkfirewall_deletion_protection_test.py +++ b/tests/providers/aws/services/networkfirewall/networkfirewall_deletion_protection/networkfirewall_deletion_protection_test.py @@ -49,6 +49,7 @@ class Test_networkfirewall_deletion_protection: networkfirewall_client.region = AWS_REGION_US_EAST_1 networkfirewall_client.network_firewalls = { FIREWALL_ARN: Firewall( + arn=FIREWALL_ARN, name=FIREWALL_NAME, region=AWS_REGION_US_EAST_1, policy_arn=POLICY_ARN, @@ -95,6 +96,7 @@ class Test_networkfirewall_deletion_protection: networkfirewall_client.region = AWS_REGION_US_EAST_1 networkfirewall_client.network_firewalls = { FIREWALL_ARN: Firewall( + arn=FIREWALL_ARN, name=FIREWALL_NAME, region=AWS_REGION_US_EAST_1, policy_arn=POLICY_ARN, diff --git a/tests/providers/aws/services/networkfirewall/networkfirewall_in_all_vpc/networkfirewall_in_all_vpc_test.py b/tests/providers/aws/services/networkfirewall/networkfirewall_in_all_vpc/networkfirewall_in_all_vpc_test.py index 1a94a16199..18193b4f17 100644 --- a/tests/providers/aws/services/networkfirewall/networkfirewall_in_all_vpc/networkfirewall_in_all_vpc_test.py +++ b/tests/providers/aws/services/networkfirewall/networkfirewall_in_all_vpc/networkfirewall_in_all_vpc_test.py @@ -58,6 +58,7 @@ class Test_networkfirewall_in_all_vpc: networkfirewall_client.region = AWS_REGION_US_EAST_1 networkfirewall_client.network_firewalls = { FIREWALL_ARN: Firewall( + arn=FIREWALL_ARN, name=FIREWALL_NAME, region=AWS_REGION_US_EAST_1, policy_arn=POLICY_ARN, @@ -285,6 +286,7 @@ class Test_networkfirewall_in_all_vpc: networkfirewall_client.region = AWS_REGION_US_EAST_1 networkfirewall_client.network_firewalls = { FIREWALL_ARN: Firewall( + arn=FIREWALL_ARN, name=FIREWALL_NAME, region=AWS_REGION_US_EAST_1, policy_arn=POLICY_ARN, diff --git a/tests/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/networkfirewall_policy_rule_group_associated_test.py b/tests/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/networkfirewall_policy_rule_group_associated_test.py new file mode 100644 index 0000000000..1340e4b938 --- /dev/null +++ b/tests/providers/aws/services/networkfirewall/networkfirewall_policy_rule_group_associated/networkfirewall_policy_rule_group_associated_test.py @@ -0,0 +1,244 @@ +from unittest import mock + +from prowler.providers.aws.services.networkfirewall.networkfirewall_service import ( + Firewall, +) +from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider + +FIREWALL_ARN = "arn:aws:network-firewall:us-east-1:123456789012:firewall/my-firewall" +FIREWALL_NAME = "my-firewall" +VPC_ID_PROTECTED = "vpc-12345678901234567" +VPC_ID_UNPROTECTED = "vpc-12345678901234568" +POLICY_ARN = "arn:aws:network-firewall:us-east-1:123456789012:firewall-policy/my-policy" + + +class Test_networkfirewall_policy_rule_group_associated: + def test_no_networkfirewall(self): + networkfirewall_client = mock.MagicMock + networkfirewall_client.provider = set_mocked_aws_provider( + [AWS_REGION_US_EAST_1] + ) + networkfirewall_client.region = AWS_REGION_US_EAST_1 + networkfirewall_client.network_firewalls = {} + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.networkfirewall.networkfirewall_policy_rule_group_associated.networkfirewall_policy_rule_group_associated.networkfirewall_client", + new=networkfirewall_client, + ): + # Test Check + from prowler.providers.aws.services.networkfirewall.networkfirewall_policy_rule_group_associated.networkfirewall_policy_rule_group_associated import ( + networkfirewall_policy_rule_group_associated, + ) + + check = networkfirewall_policy_rule_group_associated() + result = check.execute() + + assert len(result) == 0 + + def test_networkfirewall_policy_stateless_rule_group_associated(self): + networkfirewall_client = mock.MagicMock + networkfirewall_client.provider = set_mocked_aws_provider( + [AWS_REGION_US_EAST_1] + ) + networkfirewall_client.region = AWS_REGION_US_EAST_1 + networkfirewall_client.network_firewalls = { + FIREWALL_ARN: Firewall( + arn=FIREWALL_ARN, + name=FIREWALL_NAME, + region=AWS_REGION_US_EAST_1, + policy_arn=POLICY_ARN, + vpc_id=VPC_ID_PROTECTED, + tags=[], + encryption_type="CUSTOMER_KMS", + deletion_protection=False, + stateless_rule_groups=[ + "arn:aws:network-firewall:us-east-1:123456789012:stateful-rule-group/my-stateless-rule-group" + ], + ) + } + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.networkfirewall.networkfirewall_policy_rule_group_associated.networkfirewall_policy_rule_group_associated.networkfirewall_client", + new=networkfirewall_client, + ): + # Test Check + from prowler.providers.aws.services.networkfirewall.networkfirewall_policy_rule_group_associated.networkfirewall_policy_rule_group_associated import ( + networkfirewall_policy_rule_group_associated, + ) + + check = networkfirewall_policy_rule_group_associated() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Network Firewall {FIREWALL_NAME} policy has at least one rule group associated." + ) + assert result[0].region == AWS_REGION_US_EAST_1 + assert result[0].resource_id == FIREWALL_NAME + assert result[0].resource_tags == [] + assert result[0].resource_arn == FIREWALL_ARN + + def test_networkfirewall_policy_stateful_rule_group_associated(self): + networkfirewall_client = mock.MagicMock + networkfirewall_client.provider = set_mocked_aws_provider( + [AWS_REGION_US_EAST_1] + ) + networkfirewall_client.region = AWS_REGION_US_EAST_1 + networkfirewall_client.network_firewalls = { + FIREWALL_ARN: Firewall( + arn=FIREWALL_ARN, + name=FIREWALL_NAME, + region=AWS_REGION_US_EAST_1, + policy_arn=POLICY_ARN, + vpc_id=VPC_ID_PROTECTED, + tags=[], + encryption_type="CUSTOMER_KMS", + deletion_protection=False, + stateful_rule_groups=[ + "arn:aws:network-firewall:us-east-1:123456789012:stateful-rule-group/my-stateful-rule-group" + ], + ) + } + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.networkfirewall.networkfirewall_policy_rule_group_associated.networkfirewall_policy_rule_group_associated.networkfirewall_client", + new=networkfirewall_client, + ): + # Test Check + from prowler.providers.aws.services.networkfirewall.networkfirewall_policy_rule_group_associated.networkfirewall_policy_rule_group_associated import ( + networkfirewall_policy_rule_group_associated, + ) + + check = networkfirewall_policy_rule_group_associated() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Network Firewall {FIREWALL_NAME} policy has at least one rule group associated." + ) + assert result[0].region == AWS_REGION_US_EAST_1 + assert result[0].resource_id == FIREWALL_NAME + assert result[0].resource_tags == [] + assert result[0].resource_arn == FIREWALL_ARN + + def test_networkfirewall_policy_both_rule_groups_associated(self): + networkfirewall_client = mock.MagicMock + networkfirewall_client.provider = set_mocked_aws_provider( + [AWS_REGION_US_EAST_1] + ) + networkfirewall_client.region = AWS_REGION_US_EAST_1 + networkfirewall_client.network_firewalls = { + FIREWALL_ARN: Firewall( + arn=FIREWALL_ARN, + name=FIREWALL_NAME, + region=AWS_REGION_US_EAST_1, + policy_arn=POLICY_ARN, + vpc_id=VPC_ID_PROTECTED, + tags=[], + encryption_type="CUSTOMER_KMS", + deletion_protection=True, + stateless_rule_groups=[ + "arn:aws:network-firewall:us-east-1:123456789012:stateful-rule-group/my-stateless-rule-group" + ], + stateful_rule_groups=[ + "arn:aws:network-firewall:us-east-1:123456789012:stateful-rule-group/my-stateful-rule-group" + ], + ) + } + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.networkfirewall.networkfirewall_policy_rule_group_associated.networkfirewall_policy_rule_group_associated.networkfirewall_client", + new=networkfirewall_client, + ): + # Test Check + from prowler.providers.aws.services.networkfirewall.networkfirewall_policy_rule_group_associated.networkfirewall_policy_rule_group_associated import ( + networkfirewall_policy_rule_group_associated, + ) + + check = networkfirewall_policy_rule_group_associated() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Network Firewall {FIREWALL_NAME} policy has at least one rule group associated." + ) + assert result[0].region == AWS_REGION_US_EAST_1 + assert result[0].resource_id == FIREWALL_NAME + assert result[0].resource_tags == [] + assert result[0].resource_arn == FIREWALL_ARN + + def test_networkfirewall_policy_no_rule_groups_associated(self): + networkfirewall_client = mock.MagicMock + networkfirewall_client.provider = set_mocked_aws_provider( + [AWS_REGION_US_EAST_1] + ) + networkfirewall_client.region = AWS_REGION_US_EAST_1 + networkfirewall_client.network_firewalls = { + FIREWALL_ARN: Firewall( + arn=FIREWALL_ARN, + name=FIREWALL_NAME, + region=AWS_REGION_US_EAST_1, + policy_arn=POLICY_ARN, + vpc_id=VPC_ID_PROTECTED, + tags=[], + encryption_type="CUSTOMER_KMS", + deletion_protection=True, + ) + } + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.networkfirewall.networkfirewall_policy_rule_group_associated.networkfirewall_policy_rule_group_associated.networkfirewall_client", + new=networkfirewall_client, + ): + # Test Check + from prowler.providers.aws.services.networkfirewall.networkfirewall_policy_rule_group_associated.networkfirewall_policy_rule_group_associated import ( + networkfirewall_policy_rule_group_associated, + ) + + check = networkfirewall_policy_rule_group_associated() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Network Firewall {FIREWALL_NAME} policy does not have rule groups associated." + ) + assert result[0].region == AWS_REGION_US_EAST_1 + assert result[0].resource_id == FIREWALL_NAME + assert result[0].resource_tags == [] + assert result[0].resource_arn == FIREWALL_ARN