feat(networkfirewall): add new check networkfirewall_policy_rule_group_associated (#5225)

This commit is contained in:
Hugo Pereira Brito
2024-09-30 18:04:32 +02:00
committed by GitHub
parent 3d2f1a3aa7
commit 880523076d
7 changed files with 349 additions and 27 deletions
@@ -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": ""
}
@@ -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
@@ -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] = []
@@ -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,
@@ -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,
@@ -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