From 13059e056817393b10cd552e83e42862cc81987a Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Wed, 9 Aug 2023 10:08:30 +0200 Subject: [PATCH] fix(ec2-securitygroups): Handle IPv6 public (#2690) --- .../aws/services/ec2/lib/security_groups.py | 9 +-- ...internet_to_tcp_port_postgres_5432_test.py | 65 +++++++++++++++++++ .../services/ec2/lib/security_groups_test.py | 46 ++++++++++++- 3 files changed, 111 insertions(+), 9 deletions(-) diff --git a/prowler/providers/aws/services/ec2/lib/security_groups.py b/prowler/providers/aws/services/ec2/lib/security_groups.py index e131506f..787e8cb6 100644 --- a/prowler/providers/aws/services/ec2/lib/security_groups.py +++ b/prowler/providers/aws/services/ec2/lib/security_groups.py @@ -33,7 +33,7 @@ def check_security_group( @param ports: List of ports to check. (Default: []) - @param any_address: If True, only 0.0.0.0/0 will be public and do not search for public addresses. (Default: False) + @param any_address: If True, only 0.0.0.0/0 or "::/0" will be public and do not search for public addresses. (Default: False) """ # Check for all traffic ingress rules regardless of the protocol if ingress_rule["IpProtocol"] == "-1": @@ -76,7 +76,7 @@ def check_security_group( # IPv6 for ip_ingress_rule in ingress_rule["Ipv6Ranges"]: - if _is_cidr_public(ip_ingress_rule["CidrIpv6"]): + if _is_cidr_public(ip_ingress_rule["CidrIpv6"], any_address): # If there are input ports to check if ports: for port in ports: @@ -98,13 +98,10 @@ def _is_cidr_public(cidr: str, any_address: bool = False) -> bool: @param cidr: CIDR 10.22.33.44/8 - @param any_address: If True, only 0.0.0.0/0 will be public and do not search for public addresses. (Default: False) + @param any_address: If True, only 0.0.0.0/0 or "::/0" will be public and do not search for public addresses. (Default: False) """ public_IPv4 = "0.0.0.0/0" public_IPv6 = "::/0" - # Workaround until this issue is fixed - # PR https://github.com/python/cpython/pull/97733 - # Issue https://github.com/python/cpython/issues/82836 if cidr in (public_IPv4, public_IPv6): return True if not any_address: diff --git a/tests/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432_test.py b/tests/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432_test.py index 2312b83a..d49fa047 100644 --- a/tests/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432_test.py +++ b/tests/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432_test.py @@ -196,3 +196,68 @@ class Test_ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_54 ) assert sg.resource_details == default_sg_name assert sg.resource_tags == [] + + @mock_ec2 + def test_ec2_compliant_default_sg_ipv4_and_ipv6(self): + # Create EC2 Mocked Resources + ec2_client = client("ec2", region_name=AWS_REGION) + ec2_client.create_vpc(CidrBlock="10.0.0.0/16") + default_sg = ec2_client.describe_security_groups(GroupNames=["default"])[ + "SecurityGroups" + ][0] + default_sg_id = default_sg["GroupId"] + default_sg_name = default_sg["GroupName"] + ec2_client.authorize_security_group_ingress( + GroupId=default_sg_id, + IpPermissions=[ + { + "IpProtocol": "tcp", + "FromPort": 5432, + "ToPort": 5432, + "IpRanges": [{"CidrIp": "10.0.0.0/16"}], + "Ipv6Ranges": [ + { + "CidrIpv6": "cafe:cafe:cafe:cafe::/64", + }, + ], + }, + ], + ) + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + current_audit_info = self.set_mocked_audit_info() + + with mock.patch( + "prowler.providers.aws.lib.audit_info.audit_info.current_audit_info", + new=current_audit_info, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432.ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432 import ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432, + ) + + check = ( + ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432() + ) + result = check.execute() + + # One default sg per region + assert len(result) == 3 + # Search changed sg + for sg in result: + if sg.resource_id == default_sg_id: + assert sg.status == "PASS" + assert search( + "does not have Postgres port 5432 open to the Internet", + sg.status_extended, + ) + assert ( + sg.resource_arn + == f"arn:{current_audit_info.audited_partition}:ec2:{AWS_REGION}:{current_audit_info.audited_account}:security-group/{default_sg_id}" + ) + assert sg.resource_details == default_sg_name + assert sg.resource_tags == [] diff --git a/tests/providers/aws/services/ec2/lib/security_groups_test.py b/tests/providers/aws/services/ec2/lib/security_groups_test.py index 3d78c51e..7f4a521c 100644 --- a/tests/providers/aws/services/ec2/lib/security_groups_test.py +++ b/tests/providers/aws/services/ec2/lib/security_groups_test.py @@ -4,18 +4,58 @@ from prowler.providers.aws.services.ec2.lib.security_groups import _is_cidr_publ class Test_security_groups: - def test__is_cidr_public_Public_IP(self): + def test__is_cidr_public_Public_IPv4_all_IPs_any_address_false(self): cidr = "0.0.0.0/0" assert _is_cidr_public(cidr) - def test__is_cidr_public_Private_IP(self): + def test__is_cidr_public_Public_IPv4__all_IPs_any_address_true(self): + cidr = "0.0.0.0/0" + assert _is_cidr_public(cidr, any_address=True) + + def test__is_cidr_public_Public_IPv4_any_address_false(self): + cidr = "84.28.12.2/32" + assert _is_cidr_public(cidr) + + def test__is_cidr_public_Public_IPv4_any_address_true(self): + cidr = "84.28.12.2/32" + assert not _is_cidr_public(cidr, any_address=True) + + def test__is_cidr_public_Private_IPv4(self): + cidr = "10.0.0.0/8" + assert not _is_cidr_public(cidr, any_address=True) + + def test__is_cidr_public_Private_IPv4_any_address_true(self): cidr = "10.0.0.0/8" assert not _is_cidr_public(cidr) - def test__is_cidr_public_Bad_Private_IP(self): + def test__is_cidr_public_Bad_Private_IPv4(self): cidr = "10.0.0.0/0" with pytest.raises(ValueError) as ex: _is_cidr_public(cidr) assert ex.type == ValueError assert ex.match(f"{cidr} has host bits set") + + def test__is_cidr_public_Public_IPv6_all_IPs_any_address_false(self): + cidr = "::/0" + assert _is_cidr_public(cidr) + + def test__is_cidr_public_Public_IPv6_all_IPs_any_adress_true(self): + cidr = "::/0" + assert _is_cidr_public(cidr, any_address=True) + + def test__is_cidr_public_Public_IPv6(self): + cidr = "cafe:cafe:cafe:cafe::/64" + assert _is_cidr_public(cidr) + + def test__is_cidr_public_Public_IPv6_any_adress_true(self): + cidr = "cafe:cafe:cafe:cafe::/64" + assert not _is_cidr_public(cidr, any_address=True) + + def test__is_cidr_public_Private_IPv6(self): + cidr = "fc00::/7" + assert not _is_cidr_public(cidr) + + def test__is_cidr_public_Private_IPv6_any_adress_true(self): + cidr = "fc00::/7" + assert not _is_cidr_public(cidr, any_address=True)