From 38912d12f6dacca6e2e05dc1c95946dbb8d48415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Wed, 7 Aug 2024 12:42:35 +0200 Subject: [PATCH 01/20] feat(elbv2): Add SGs attribute --- .../aws/services/elbv2/elbv2_service.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/prowler/providers/aws/services/elbv2/elbv2_service.py b/prowler/providers/aws/services/elbv2/elbv2_service.py index dd3626dd3a7..ddce9785bc3 100644 --- a/prowler/providers/aws/services/elbv2/elbv2_service.py +++ b/prowler/providers/aws/services/elbv2/elbv2_service.py @@ -27,6 +27,8 @@ def __init__(self, provider): ], ) self.__threading_call__(self._describe_tags, self.loadbalancersv2.items()) + self.target_groups = {} + self.__threading_call__(self._describe_target_groups) def _describe_load_balancers(self, regional_client): logger.info("ELBv2 - Describing load balancers...") @@ -174,6 +176,35 @@ def _describe_tags(self, load_balancer): f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) + def _describe_target_groups(self, regional_client): + logger.info("ELBv2 - Describing target groups...") + try: + describe_target_groups_paginator = regional_client.get_paginator( + "describe_target_groups" + ) + for page in describe_target_groups_paginator.paginate(): + for target_group in page["TargetGroups"]: + targets = { + (tg["Target"]["Id"], tg["Target"].get("Port", 0)) + for tg in regional_client.describe_target_health( + TargetGroupArn=target_group["TargetGroupArn"] + )["TargetHealthDescriptions"] + } + + self.target_groups[target_group["TargetGroupArn"]] = TargetGroup( + name=target_group.get("TargetGroupName", ""), + protocol=target_group.get("Protocol", ""), + port=target_group.get("Port", 0), + vpc_id=target_group.get("VpcId", ""), + target_type=target_group.get("TargetType", ""), + targets=dict(targets), + load_balancer_arns=target_group.get("LoadBalancerArns", []), + ) + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + class ListenerRule(BaseModel): arn: str @@ -201,3 +232,14 @@ class LoadBalancerv2(BaseModel): listeners: Dict[str, Listenerv2] = {} scheme: Optional[str] tags: Optional[list] = [] + + +class TargetGroup(BaseModel): + name: str + protocol: str + port: int + vpc_id: str + target_type: str + # Key: ID, Value: Port (only available for IP targets) + targets: Dict[str, int] = [] + load_balancer_arns: list[str] From cd0b0c49d1f889d389bde9f3f8670957fa516557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Wed, 7 Aug 2024 12:44:19 +0200 Subject: [PATCH 02/20] test(elbv2): Add new tests for target groups attributes --- .../aws/services/elbv2/elbv2_service_test.py | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tests/providers/aws/services/elbv2/elbv2_service_test.py b/tests/providers/aws/services/elbv2/elbv2_service_test.py index cb7bd517106..b19fd664ffc 100644 --- a/tests/providers/aws/services/elbv2/elbv2_service_test.py +++ b/tests/providers/aws/services/elbv2/elbv2_service_test.py @@ -327,3 +327,74 @@ def test_describe_tags(self): elbv2.loadbalancersv2[lb["LoadBalancerArn"]].tags[1]["Key"] == "Environment" ) assert elbv2.loadbalancersv2[lb["LoadBalancerArn"]].tags[1]["Value"] == "dev" + + @mock_aws + def test_describe_target_groups(self): + conn = client("elbv2", region_name=AWS_REGION_EU_WEST_1) + ec2 = resource("ec2", region_name=AWS_REGION_EU_WEST_1) + + security_group = ec2.create_security_group( + GroupName="a-security-group", Description="First One" + ) + vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default") + subnet1 = ec2.create_subnet( + VpcId=vpc.id, + CidrBlock="172.28.7.192/26", + AvailabilityZone=AWS_REGION_EU_WEST_1_AZA, + ) + subnet2 = ec2.create_subnet( + VpcId=vpc.id, + CidrBlock="172.28.7.0/26", + AvailabilityZone=AWS_REGION_EU_WEST_1_AZB, + ) + + lb = conn.create_load_balancer( + Name="my-lb", + Subnets=[subnet1.id, subnet2.id], + SecurityGroups=[security_group.id], + Scheme="internal", + )["LoadBalancers"][0] + + target_group = conn.create_target_group( + Name="my-target-group", + Protocol="HTTP", + Port=80, + VpcId=vpc.id, + HealthCheckProtocol="HTTP", + HealthCheckPort="80", + HealthCheckPath="/", + )["TargetGroups"][0] + + conn.create_listener( + LoadBalancerArn=lb["LoadBalancerArn"], + Protocol="HTTP", + Port=80, + DefaultActions=[ + {"Type": "forward", "TargetGroupArn": target_group["TargetGroupArn"]} + ], + ) + + # ELBv2 client for this test class + aws_provider = set_mocked_aws_provider( + [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] + ) + + elbv2 = ELBv2(aws_provider) + + assert len(elbv2.target_groups) == 1 + assert target_group["TargetGroupArn"] in elbv2.target_groups + assert ( + elbv2.target_groups[target_group["TargetGroupArn"]].name + == "my-target-group" + ) + assert elbv2.target_groups[target_group["TargetGroupArn"]].protocol == "HTTP" + assert elbv2.target_groups[target_group["TargetGroupArn"]].port == 80 + assert elbv2.target_groups[target_group["TargetGroupArn"]].vpc_id == vpc.id + assert elbv2.target_groups[ + target_group["TargetGroupArn"] + ].load_balancer_arns == [lb["LoadBalancerArn"]] + assert ( + elbv2.target_groups[target_group["TargetGroupArn"]].target_type + == "instance" + ) + assert elbv2.target_groups[target_group["TargetGroupArn"]].targets == {} From 6c420fd50ae6d13001be7e6bab100f45259e3435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Wed, 7 Aug 2024 12:45:21 +0200 Subject: [PATCH 03/20] chore(awslabmda): Remove unnecesary class header --- prowler/providers/aws/services/awslambda/awslambda_service.py | 1 - 1 file changed, 1 deletion(-) diff --git a/prowler/providers/aws/services/awslambda/awslambda_service.py b/prowler/providers/aws/services/awslambda/awslambda_service.py index 82f335227eb..287bbb74a59 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_service.py +++ b/prowler/providers/aws/services/awslambda/awslambda_service.py @@ -14,7 +14,6 @@ from prowler.providers.aws.lib.service.service import AWSService -################## Lambda class Lambda(AWSService): def __init__(self, provider): # Call AWSService's __init__ From d1fdbe87e937fbfa560cbd864276c9f34f8a5825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Wed, 7 Aug 2024 12:46:27 +0200 Subject: [PATCH 04/20] chore(awslabmda): Change to have in count that is really public Internet exposed --- ...tion_not_publicly_accessible.metadata.json | 4 +- ...lambda_function_not_publicly_accessible.py | 73 ++++++++++++++----- 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json index cdc5176105c..e52f7f1bdd5 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json @@ -1,14 +1,14 @@ { "Provider": "aws", "CheckID": "awslambda_function_not_publicly_accessible", - "CheckTitle": "Check if Lambda functions have resource-based policy set as Public.", + "CheckTitle": "Check if Lambda functions is publicly accessible through Internet", "CheckType": [], "ServiceName": "lambda", "SubServiceName": "", "ResourceIdTemplate": "arn:partition:lambda:region:account-id:function/function-name", "Severity": "critical", "ResourceType": "AwsLambdaFunction", - "Description": "Check if Lambda functions have resource-based policy set as Public.", + "Description": "Check if Lambda functions is publicly accessible through Internet via ALB and function's permission.", "Risk": "Publicly accessible services could expose sensitive data to bad actors.", "RelatedUrl": "https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html", "Remediation": { diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py index a39b8576443..978e78b0c48 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py @@ -1,5 +1,6 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.awslambda.awslambda_client import awslambda_client +from prowler.providers.aws.services.elbv2.elbv2_client import elbv2_client class awslambda_function_not_publicly_accessible(Check): @@ -13,30 +14,68 @@ def execute(self): report.resource_tags = function.tags report.status = "PASS" - report.status_extended = f"Lambda function {function.name} has a policy resource-based policy not public." + report.status_extended = ( + f"Lambda function {function.name} is not publicly accessible." + ) - public_access = False - if function.policy: + # 1. Check if the function is associated with a public load balancer + target_group_redirects_alb_to_function = "" + + for tg_arn, target_group in elbv2_client.target_groups.items(): + if ( + target_group.target_type == "lambda" + ) and function.arn in target_group.targets: + for lb_arn in target_group.load_balancer_arns: + lb = elbv2_client.loadbalancersv2.get( + lb_arn, + None, # Bug: If prowler is filtering by ARN, this will fail because loadbalancersv2 are filtered + ) + if lb and lb.scheme == "internet-facing": + target_group_redirects_alb_to_function = tg_arn + break + + public_policy = False + + # 2. Check if the function policy allows public access + if target_group_redirects_alb_to_function and function.policy: for statement in function.policy["Statement"]: # Only check allow statements - if statement["Effect"] == "Allow": - if ( + if ( + statement["Effect"] == "Allow" + and ( "*" in statement["Principal"] - or ( - "AWS" in statement["Principal"] - and "*" in statement["Principal"]["AWS"] - ) - or ( - "CanonicalUser" in statement["Principal"] - and "*" in statement["Principal"]["CanonicalUser"] - ) - ): - public_access = True + or "*" in statement["Principal"].get("AWS", "") + or "*" in statement["Principal"].get("CanonicalUser", "") + or "elasticloadbalancing.amazonaws.com" + in statement["Principal"].get("Service", "") + ) + and ( + "*" in statement["Action"] + or "lambda:InvokeFunction" in statement["Action"] + ) + and ( + "*" in statement["Resource"] + or function.arn in statement["Resource"] + ) + ): + if statement.get("Condition", {}): + if ( + statement["Condition"] + .get("ArnLike", {}) + .get("AWS:SourceArn", "") + == target_group_redirects_alb_to_function + ): + public_policy = True + break + else: + public_policy = True break - if public_access: + if public_policy: report.status = "FAIL" - report.status_extended = f"Lambda function {function.name} has a policy resource-based policy with public access." + report.status_extended = ( + f"Lambda function {function.name} is publicly accessible." + ) findings.append(report) From 4e5a5f7f342faf151aa2b185d982f6e35ce57d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Wed, 7 Aug 2024 12:48:01 +0200 Subject: [PATCH 05/20] test(awslambda): Adapt new tests for case of ALBs --- ...a_function_not_publicly_accessible_test.py | 427 ++++++++++++++++-- 1 file changed, 383 insertions(+), 44 deletions(-) diff --git a/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py b/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py index 1b9a1460327..b5930d61fad 100644 --- a/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py +++ b/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py @@ -4,12 +4,7 @@ from boto3 import client from moto import mock_aws -from prowler.providers.aws.services.awslambda.awslambda_service import Function -from tests.providers.aws.utils import ( - AWS_ACCOUNT_NUMBER, - AWS_REGION_EU_WEST_1, - set_mocked_aws_provider, -) +from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider class Test_awslambda_function_not_publicly_accessible: @@ -37,7 +32,7 @@ def test_no_functions(self): assert len(result) == 0 @mock_aws - def test_function_public(self): + def test_function_public_but_not_internet_exposed(self): # Create the mock IAM role iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1) role_name = "test-role" @@ -79,7 +74,6 @@ def test_function_public(self): StatementId="public-access", Action="lambda:InvokeFunction", Principal="*", - SourceArn=function_arn, ) aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) @@ -105,15 +99,52 @@ def test_function_public(self): assert result[0].region == AWS_REGION_EU_WEST_1 assert result[0].resource_id == function_name assert result[0].resource_arn == function_arn - assert result[0].status == "FAIL" + assert result[0].status == "PASS" assert ( result[0].status_extended - == f"Lambda function {function_name} has a policy resource-based policy with public access." + == f"Lambda function {function_name} is not publicly accessible." ) assert result[0].resource_tags == [{"tag1": "value1", "tag2": "value2"}] @mock_aws - def test_function_not_public(self): + def test_function_public_with_alb(self): + # Create the mock VPC + ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) + vpc = ec2_client.create_vpc(CidrBlock="10.0.0.0/16") + vpc_id = vpc["Vpc"]["VpcId"] + + # Create subnets + subnet_a = ec2_client.create_subnet( + VpcId=vpc_id, + CidrBlock="10.0.1.0/24", + AvailabilityZone=f"{AWS_REGION_EU_WEST_1}a", + ) + subnet_b = ec2_client.create_subnet( + VpcId=vpc_id, + CidrBlock="10.0.2.0/24", + AvailabilityZone=f"{AWS_REGION_EU_WEST_1}b", + ) + + # Create an Internet Gateway + igw = ec2_client.create_internet_gateway() + igw_id = igw["InternetGateway"]["InternetGatewayId"] + ec2_client.attach_internet_gateway(InternetGatewayId=igw_id, VpcId=vpc_id) + + # Create a Route Table and associate it with subnets + route_table = ec2_client.create_route_table(VpcId=vpc_id) + route_table_id = route_table["RouteTable"]["RouteTableId"] + ec2_client.create_route( + RouteTableId=route_table_id, + DestinationCidrBlock="0.0.0.0/0", + GatewayId=igw_id, + ) + ec2_client.associate_route_table( + RouteTableId=route_table_id, SubnetId=subnet_a["Subnet"]["SubnetId"] + ) + ec2_client.associate_route_table( + RouteTableId=route_table_id, SubnetId=subnet_b["Subnet"]["SubnetId"] + ) + # Create the mock IAM role iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1) role_name = "test-role" @@ -138,7 +169,7 @@ def test_function_not_public(self): lambda_client = client("lambda", region_name=AWS_REGION_EU_WEST_1) function_arn = lambda_client.create_function( FunctionName=function_name, - Runtime="nodejs4.3", + Runtime="python3.8", Role=role_arn, Handler="index.handler", Code={"ZipFile": b"fileb://file-path/to/your-deployment-package.zip"}, @@ -149,13 +180,73 @@ def test_function_not_public(self): Tags={"tag1": "value1", "tag2": "value2"}, )["FunctionArn"] - # Attach the policy to the lambda function with a specific AWS account number as principal + # Attach the policy to the lambda function with a wildcard principal lambda_client.add_permission( FunctionName=function_name, StatementId="public-access", Action="lambda:InvokeFunction", - Principal=AWS_ACCOUNT_NUMBER, - SourceArn=function_arn, + Principal="*", + ) + + # Create a security group for ALB + sg = ec2_client.create_security_group( + GroupName="alb-sg", + Description="Security group for ALB", + VpcId=vpc_id, + ) + sg_id = sg["GroupId"] + ec2_client.authorize_security_group_ingress( + GroupId=sg_id, + IpPermissions=[ + { + "IpProtocol": "tcp", + "FromPort": 80, + "ToPort": 80, + "IpRanges": [{"CidrIp": "0.0.0.0/0"}], + } + ], + ) + + # Create the ALB + elbv2_client = client("elbv2", region_name=AWS_REGION_EU_WEST_1) + lb = elbv2_client.create_load_balancer( + Name="test-alb", + Subnets=[subnet_a["Subnet"]["SubnetId"], subnet_b["Subnet"]["SubnetId"]], + SecurityGroups=[sg_id], + Scheme="internet-facing", + Type="application", + IpAddressType="ipv4", + ) + lb_arn = lb["LoadBalancers"][0]["LoadBalancerArn"] + + # Create the Target Group for Lambda + target_group = elbv2_client.create_target_group( + Name="test-tg", + TargetType="lambda", + ) + target_group_arn = target_group["TargetGroups"][0]["TargetGroupArn"] + + # Add permission for ALB to invoke the Lambda function + lambda_client.add_permission( + FunctionName=function_name, + StatementId="alb-access", + Action="lambda:InvokeFunction", + Principal="elasticloadbalancing.amazonaws.com", + SourceArn=target_group_arn, + ) + + # Attach Lambda to Target Group + elbv2_client.register_targets( + TargetGroupArn=target_group_arn, + Targets=[{"Id": function_arn}], + ) + + # Create ALB Listener + elbv2_client.create_listener( + LoadBalancerArn=lb_arn, + Protocol="HTTP", + Port=80, + DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}], ) aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) @@ -181,50 +272,166 @@ def test_function_not_public(self): assert result[0].region == AWS_REGION_EU_WEST_1 assert result[0].resource_id == function_name assert result[0].resource_arn == function_arn - assert result[0].status == "PASS" + assert result[0].status == "FAIL" assert ( result[0].status_extended - == f"Lambda function {function_name} has a policy resource-based policy not public." + == f"Lambda function {function_name} is publicly accessible." ) assert result[0].resource_tags == [{"tag1": "value1", "tag2": "value2"}] - def test_function_public_with_canonical(self): - lambda_client = mock.MagicMock - function_name = "test-lambda" - function_runtime = "nodejs4.3" - function_arn = f"arn:aws:lambda:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:function/{function_name}" - lambda_policy = { + @mock_aws + def test_function_linked_through_alb_but_not_public_due_to_restrictive_policies( + self, + ): + # Create the mock VPC + ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) + vpc = ec2_client.create_vpc(CidrBlock="10.0.0.0/16") + vpc_id = vpc["Vpc"]["VpcId"] + + # Create subnets + subnet_a = ec2_client.create_subnet( + VpcId=vpc_id, + CidrBlock="10.0.1.0/24", + AvailabilityZone=f"{AWS_REGION_EU_WEST_1}a", + ) + subnet_b = ec2_client.create_subnet( + VpcId=vpc_id, + CidrBlock="10.0.2.0/24", + AvailabilityZone=f"{AWS_REGION_EU_WEST_1}b", + ) + + # Create an Internet Gateway + igw = ec2_client.create_internet_gateway() + igw_id = igw["InternetGateway"]["InternetGatewayId"] + ec2_client.attach_internet_gateway(InternetGatewayId=igw_id, VpcId=vpc_id) + + # Create a Route Table and associate it with subnets + route_table = ec2_client.create_route_table(VpcId=vpc_id) + route_table_id = route_table["RouteTable"]["RouteTableId"] + ec2_client.create_route( + RouteTableId=route_table_id, + DestinationCidrBlock="0.0.0.0/0", + GatewayId=igw_id, + ) + ec2_client.associate_route_table( + RouteTableId=route_table_id, SubnetId=subnet_a["Subnet"]["SubnetId"] + ) + ec2_client.associate_route_table( + RouteTableId=route_table_id, SubnetId=subnet_b["Subnet"]["SubnetId"] + ) + + # Create the mock IAM role + iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1) + role_name = "test-role" + assume_role_policy_document = { "Version": "2012-10-17", "Statement": [ { - "Sid": "public-access", - "Principal": {"CanonicalUser": ["*"]}, "Effect": "Allow", - "Action": [ - "lambda:InvokeFunction", - ], - "Resource": [function_arn], + "Principal": {"Service": "lambda.amazonaws.com"}, + "Action": "sts:AssumeRole", } ], } + role_arn = iam_client.create_role( + RoleName=role_name, + AssumeRolePolicyDocument=dumps(assume_role_policy_document), + )["Role"]["Arn"] - lambda_client.functions = { - "function_name": Function( - name=function_name, - security_groups=[], - arn=function_arn, - region=AWS_REGION_EU_WEST_1, - runtime=function_runtime, - policy=lambda_policy, - ) - } + function_name = "test-lambda" + + # Create the lambda function using boto3 client + lambda_client = client("lambda", region_name=AWS_REGION_EU_WEST_1) + function_arn = lambda_client.create_function( + FunctionName=function_name, + Runtime="python3.8", + Role=role_arn, + Handler="index.handler", + Code={"ZipFile": b"fileb://file-path/to/your-deployment-package.zip"}, + Description="Test Lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + Tags={"tag1": "value1", "tag2": "value2"}, + )["FunctionArn"] + + # Create a security group for ALB + sg = ec2_client.create_security_group( + GroupName="alb-sg", + Description="Security group for ALB", + VpcId=vpc_id, + ) + sg_id = sg["GroupId"] + ec2_client.authorize_security_group_ingress( + GroupId=sg_id, + IpPermissions=[ + { + "IpProtocol": "tcp", + "FromPort": 80, + "ToPort": 80, + "IpRanges": [{"CidrIp": "0.0.0.0/0"}], + } + ], + ) + + # Create the ALB + elbv2_client = client("elbv2", region_name=AWS_REGION_EU_WEST_1) + lb = elbv2_client.create_load_balancer( + Name="test-alb", + Subnets=[subnet_a["Subnet"]["SubnetId"], subnet_b["Subnet"]["SubnetId"]], + SecurityGroups=[sg_id], + Scheme="internet-facing", + Type="application", + IpAddressType="ipv4", + ) + lb_arn = lb["LoadBalancers"][0]["LoadBalancerArn"] + + # Create the Target Group for Lambda + target_group = elbv2_client.create_target_group( + Name="test-tg", + TargetType="lambda", + ) + target_group_arn = target_group["TargetGroups"][0]["TargetGroupArn"] + + # Add permission for ALB to invoke the Lambda function + lambda_client.add_permission( + FunctionName=function_name, + StatementId="no-alb-access", + Action="lambda:InvokeFunction", + Principal="elasticloadbalancing.amazonaws.com", + SourceArn=function_arn, + ) + + # Attach Lambda to Target Group + elbv2_client.register_targets( + TargetGroupArn=target_group_arn, + Targets=[{"Id": function_arn}], + ) + + # Create ALB Listener + elbv2_client.create_listener( + LoadBalancerArn=lb_arn, + Protocol="HTTP", + Port=80, + DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}], + ) + + # Set restrictive policy on Lambda + lambda_client.put_function_concurrency( + FunctionName=function_name, + ReservedConcurrentExecutions=0, # Restrictive policy: no concurrent executions allowed + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + from prowler.providers.aws.services.awslambda.awslambda_service import Lambda with mock.patch( "prowler.providers.common.provider.Provider.get_global_provider", - return_value=set_mocked_aws_provider(), + return_value=aws_provider, ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", - new=lambda_client, + new=Lambda(aws_provider), ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( @@ -238,9 +445,141 @@ def test_function_public_with_canonical(self): assert result[0].region == AWS_REGION_EU_WEST_1 assert result[0].resource_id == function_name assert result[0].resource_arn == function_arn - assert result[0].status == "FAIL" + assert result[0].status == "PASS" assert ( result[0].status_extended - == f"Lambda function {function_name} has a policy resource-based policy with public access." + == f"Lambda function {function_name} is not publicly accessible." ) - assert result[0].resource_tags == [] + assert result[0].resource_tags == [{"tag1": "value1", "tag2": "value2"}] + + # @mock_aws + # def test_function_not_public_policies(self): + # # Create the mock IAM role + # iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1) + # role_name = "test-role" + # assume_role_policy_document = { + # "Version": "2012-10-17", + # "Statement": [ + # { + # "Effect": "Allow", + # "Principal": {"Service": "lambda.amazonaws.com"}, + # "Action": "sts:AssumeRole", + # } + # ], + # } + # role_arn = iam_client.create_role( + # RoleName=role_name, + # AssumeRolePolicyDocument=dumps(assume_role_policy_document), + # )["Role"]["Arn"] + + # function_name = "test-lambda" + + # # Create the lambda function using boto3 client + # lambda_client = client("lambda", region_name=AWS_REGION_EU_WEST_1) + # function_arn = lambda_client.create_function( + # FunctionName=function_name, + # Runtime="nodejs4.3", + # Role=role_arn, + # Handler="index.handler", + # Code={"ZipFile": b"fileb://file-path/to/your-deployment-package.zip"}, + # Description="Test Lambda function", + # Timeout=3, + # MemorySize=128, + # Publish=True, + # Tags={"tag1": "value1", "tag2": "value2"}, + # )["FunctionArn"] + + # # Attach the policy to the lambda function with a specific AWS account number as principal + # lambda_client.add_permission( + # FunctionName=function_name, + # StatementId="public-access", + # Action="lambda:InvokeFunction", + # Principal=AWS_ACCOUNT_NUMBER, + # ) + + # aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + # from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + + # with mock.patch( + # "prowler.providers.common.provider.Provider.get_global_provider", + # return_value=aws_provider, + # ), mock.patch( + # "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", + # new=Lambda(aws_provider), + # ): + # # Test Check + # from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( + # awslambda_function_not_publicly_accessible, + # ) + + # check = awslambda_function_not_publicly_accessible() + # result = check.execute() + + # assert len(result) == 1 + # assert result[0].region == AWS_REGION_EU_WEST_1 + # assert result[0].resource_id == function_name + # assert result[0].resource_arn == function_arn + # assert result[0].status == "PASS" + # assert ( + # result[0].status_extended + # == f"Lambda function {function_name} has a policy resource-based policy not public." + # ) + # assert result[0].resource_tags == [{"tag1": "value1", "tag2": "value2"}] + + # def test_function_public_with_canonical(self): + # lambda_client = mock.MagicMock + # function_name = "test-lambda" + # function_runtime = "nodejs4.3" + # function_arn = f"arn:aws:lambda:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:function/{function_name}" + # lambda_policy = { + # "Version": "2012-10-17", + # "Statement": [ + # { + # "Sid": "public-access", + # "Principal": {"CanonicalUser": ["*"]}, + # "Effect": "Allow", + # "Action": [ + # "lambda:InvokeFunction", + # ], + # "Resource": [function_arn], + # } + # ], + # } + + # lambda_client.functions = { + # "function_name": Function( + # name=function_name, + # security_groups=[], + # arn=function_arn, + # region=AWS_REGION_EU_WEST_1, + # runtime=function_runtime, + # policy=lambda_policy, + # ) + # } + + # with mock.patch( + # "prowler.providers.common.provider.Provider.get_global_provider", + # return_value=set_mocked_aws_provider(), + # ), mock.patch( + # "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", + # new=lambda_client, + # ): + # # Test Check + # from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( + # awslambda_function_not_publicly_accessible, + # ) + + # check = awslambda_function_not_publicly_accessible() + # result = check.execute() + + # assert len(result) == 1 + # assert result[0].region == AWS_REGION_EU_WEST_1 + # assert result[0].resource_id == function_name + # assert result[0].resource_arn == function_arn + # assert result[0].status == "FAIL" + # assert ( + # result[0].status_extended + # == f"Lambda function {function_name} has a policy resource-based policy with public access." + # ) + # assert result[0].resource_tags == [] From b30d260c57a0cdc37d514ea55f18494ab14566d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Wed, 7 Aug 2024 16:56:42 +0200 Subject: [PATCH 06/20] test(awslambda): Mock ELBv2 service properly --- ...a_function_not_publicly_accessible_test.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py b/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py index b5930d61fad..1505d009794 100644 --- a/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py +++ b/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py @@ -13,6 +13,7 @@ def test_no_functions(self): aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 with mock.patch( "prowler.providers.common.provider.Provider.get_global_provider", @@ -20,6 +21,9 @@ def test_no_functions(self): ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", new=Lambda(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", + new=ELBv2(aws_provider), ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( @@ -79,6 +83,7 @@ def test_function_public_but_not_internet_exposed(self): aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 with mock.patch( "prowler.providers.common.provider.Provider.get_global_provider", @@ -86,6 +91,9 @@ def test_function_public_but_not_internet_exposed(self): ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", new=Lambda(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", + new=ELBv2(aws_provider), ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( @@ -163,7 +171,7 @@ def test_function_public_with_alb(self): AssumeRolePolicyDocument=dumps(assume_role_policy_document), )["Role"]["Arn"] - function_name = "test-lambda" + function_name = "test-public-lambda" # Create the lambda function using boto3 client lambda_client = client("lambda", region_name=AWS_REGION_EU_WEST_1) @@ -221,7 +229,7 @@ def test_function_public_with_alb(self): # Create the Target Group for Lambda target_group = elbv2_client.create_target_group( - Name="test-tg", + Name="test-public-lambda-tg", TargetType="lambda", ) target_group_arn = target_group["TargetGroups"][0]["TargetGroupArn"] @@ -252,6 +260,7 @@ def test_function_public_with_alb(self): aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 with mock.patch( "prowler.providers.common.provider.Provider.get_global_provider", @@ -259,6 +268,9 @@ def test_function_public_with_alb(self): ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", new=Lambda(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", + new=ELBv2(aws_provider), ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( @@ -425,6 +437,7 @@ def test_function_linked_through_alb_but_not_public_due_to_restrictive_policies( aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 with mock.patch( "prowler.providers.common.provider.Provider.get_global_provider", @@ -432,6 +445,9 @@ def test_function_linked_through_alb_but_not_public_due_to_restrictive_policies( ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", new=Lambda(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", + new=ELBv2(aws_provider), ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( From 08c554f1140b5eea47e6c359818b5e191933ae3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Wed, 7 Aug 2024 16:58:51 +0200 Subject: [PATCH 07/20] fix(awslambda): Fix arbitrary position in sanitized URL --- .../awslambda_function_not_publicly_accessible.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py index 978e78b0c48..2543f9891e5 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py @@ -47,7 +47,7 @@ def execute(self): or "*" in statement["Principal"].get("AWS", "") or "*" in statement["Principal"].get("CanonicalUser", "") or "elasticloadbalancing.amazonaws.com" - in statement["Principal"].get("Service", "") + == statement["Principal"].get("Service", "") ) and ( "*" in statement["Action"] From d50c5c2274cf6add1b90796404e44f3a85a32ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Wed, 7 Aug 2024 17:13:33 +0200 Subject: [PATCH 08/20] feat(awslabmda): Add SecurityGroups to ELBv2 --- .../awslambda_function_not_publicly_accessible.py | 4 ++++ prowler/providers/aws/services/elbv2/elbv2_service.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py index 2543f9891e5..d13151c1b7b 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py @@ -31,6 +31,10 @@ def execute(self): None, # Bug: If prowler is filtering by ARN, this will fail because loadbalancersv2 are filtered ) if lb and lb.scheme == "internet-facing": + # 1.1 Check if the ALB security group allows public access + for sg_id in lb.security_group_ids: + pass + target_group_redirects_alb_to_function = tg_arn break diff --git a/prowler/providers/aws/services/elbv2/elbv2_service.py b/prowler/providers/aws/services/elbv2/elbv2_service.py index ddce9785bc3..94f2e233279 100644 --- a/prowler/providers/aws/services/elbv2/elbv2_service.py +++ b/prowler/providers/aws/services/elbv2/elbv2_service.py @@ -49,6 +49,7 @@ def _describe_load_balancers(self, regional_client): type=elbv2["Type"], dns=elbv2.get("DNSName", None), scheme=elbv2.get("Scheme", None), + security_group_ids=elbv2.get("SecurityGroups", []), ) except Exception as error: logger.error( @@ -231,6 +232,7 @@ class LoadBalancerv2(BaseModel): drop_invalid_header_fields: Optional[str] listeners: Dict[str, Listenerv2] = {} scheme: Optional[str] + security_group_ids: list[str] tags: Optional[list] = [] From 234344f33e6c2a8afdb078c7ffb4fff876ba77a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Wed, 7 Aug 2024 17:43:40 +0200 Subject: [PATCH 09/20] chore(ec2): Change SGs to dict and respective for loops --- .../providers/aws/services/ec2/ec2_service.py | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/prowler/providers/aws/services/ec2/ec2_service.py b/prowler/providers/aws/services/ec2/ec2_service.py index e0ab1fb8da4..8876ef1dbf6 100644 --- a/prowler/providers/aws/services/ec2/ec2_service.py +++ b/prowler/providers/aws/services/ec2/ec2_service.py @@ -18,7 +18,7 @@ def __init__(self, provider): self.instances = [] self.__threading_call__(self.__describe_instances__) self.__threading_call__(self.__get_instance_user_data__, self.instances) - self.security_groups = [] + self.security_groups = {} self.regions_with_sgs = [] self.__threading_call__(self.__describe_security_groups__) self.network_acls = [] @@ -120,18 +120,15 @@ def __describe_security_groups__(self, regional_client): for sg_group in ingress_rule.get("UserIdGroupPairs", []): if sg_group.get("GroupId"): associated_sgs.append(sg_group["GroupId"]) - self.security_groups.append( - SecurityGroup( - name=sg["GroupName"], - arn=arn, - region=regional_client.region, - id=sg["GroupId"], - ingress_rules=sg["IpPermissions"], - egress_rules=sg["IpPermissionsEgress"], - associated_sgs=associated_sgs, - vpc_id=sg["VpcId"], - tags=sg.get("Tags"), - ) + self.security_groups[arn] = SecurityGroup( + name=sg["GroupName"], + region=regional_client.region, + id=sg["GroupId"], + ingress_rules=sg["IpPermissions"], + egress_rules=sg["IpPermissionsEgress"], + associated_sgs=associated_sgs, + vpc_id=sg["VpcId"], + tags=sg.get("Tags"), ) if sg["GroupName"] != "default": self.regions_with_sgs.append(regional_client.region) @@ -267,7 +264,7 @@ def __add_network_interfaces_to_security_groups__( ): try: for sg in interface_security_groups: - for security_group in self.security_groups: + for security_group in self.security_groups.values(): if security_group.id == sg["GroupId"]: security_group.network_interfaces.append(interface) except Exception as error: @@ -563,7 +560,6 @@ class NetworkInterface(BaseModel): class SecurityGroup(BaseModel): name: str - arn: str region: str id: str vpc_id: str From dcdec71f9b219c00798711125d01467f85de9dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Wed, 7 Aug 2024 17:44:30 +0200 Subject: [PATCH 10/20] fix(ec2): Fix loops iterations --- tests/providers/aws/services/ec2/ec2_service_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/providers/aws/services/ec2/ec2_service_test.py b/tests/providers/aws/services/ec2/ec2_service_test.py index 3824e4221a7..b9fa8bcccdd 100644 --- a/tests/providers/aws/services/ec2/ec2_service_test.py +++ b/tests/providers/aws/services/ec2/ec2_service_test.py @@ -150,11 +150,11 @@ def test__describe_security_groups__(self): ec2 = EC2(aws_provider) assert sg_id in str(ec2.security_groups) - for security_group in ec2.security_groups: + for sg_arn, security_group in ec2.security_groups.items(): if security_group.id == sg_id: assert security_group.name == "test-security-group" assert ( - security_group.arn + sg_arn == f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:security-group/{security_group.id}" ) assert re.match(r"sg-[0-9a-z]{17}", security_group.id) @@ -524,7 +524,7 @@ def test__describe_network_interfaces__(self): {"Key": "string", "Value": "string"}, ] # Check if ENI was added to security group - for sg in ec2.security_groups: + for sg in ec2.security_groups.values(): if sg.id == eni.groups[0]["GroupId"]: assert sg.network_interfaces == ec2.network_interfaces From 901b7e37dc8899228c1d5a9135ddfb7d8e6d3782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Thu, 8 Aug 2024 11:01:10 +0200 Subject: [PATCH 11/20] fix(ec2): Fix loops iterations for all affected checks --- docs/developer-guide/checks.md | 2 +- .../dms_instance_no_public_access.py | 2 +- ...2_instance_port_cassandra_exposed_to_internet.py | 2 +- .../ec2_instance_port_cifs_exposed_to_internet.py | 2 +- ...port_elasticsearch_kibana_exposed_to_internet.py | 2 +- .../ec2_instance_port_ftp_exposed_to_internet.py | 2 +- .../ec2_instance_port_kafka_exposed_to_internet.py | 2 +- ...c2_instance_port_kerberos_exposed_to_internet.py | 2 +- .../ec2_instance_port_ldap_exposed_to_internet.py | 2 +- ...2_instance_port_memcached_exposed_to_internet.py | 2 +- ...ec2_instance_port_mongodb_exposed_to_internet.py | 2 +- .../ec2_instance_port_mysql_exposed_to_internet.py | 2 +- .../ec2_instance_port_oracle_exposed_to_internet.py | 2 +- ..._instance_port_postgresql_exposed_to_internet.py | 2 +- .../ec2_instance_port_rdp_exposed_to_internet.py | 2 +- .../ec2_instance_port_redis_exposed_to_internet.py | 2 +- ...2_instance_port_sqlserver_exposed_to_internet.py | 2 +- .../ec2_instance_port_ssh_exposed_to_internet.py | 2 +- .../ec2_instance_port_telnet_exposed_to_internet.py | 2 +- ...roup_allow_ingress_from_internet_to_all_ports.py | 8 ++++---- ...group_allow_ingress_from_internet_to_any_port.py | 13 ++++++------- ...ess_from_internet_to_port_mongodb_27017_27018.py | 10 +++++----- ...w_ingress_from_internet_to_tcp_ftp_port_20_21.py | 10 +++++----- ...up_allow_ingress_from_internet_to_tcp_port_22.py | 10 +++++----- ..._allow_ingress_from_internet_to_tcp_port_3389.py | 10 +++++----- ...internet_to_tcp_port_cassandra_7199_9160_8888.py | 10 +++++----- ..._tcp_port_elasticsearch_kibana_9200_9300_5601.py | 10 +++++----- ..._ingress_from_internet_to_tcp_port_kafka_9092.py | 10 +++++----- ...ess_from_internet_to_tcp_port_memcached_11211.py | 10 +++++----- ..._ingress_from_internet_to_tcp_port_mysql_3306.py | 10 +++++----- ...ss_from_internet_to_tcp_port_oracle_1521_2483.py | 10 +++++----- ...gress_from_internet_to_tcp_port_postgres_5432.py | 10 +++++----- ..._ingress_from_internet_to_tcp_port_redis_6379.py | 10 +++++----- ...rom_internet_to_tcp_port_sql_server_1433_1434.py | 10 +++++----- ...w_ingress_from_internet_to_tcp_port_telnet_23.py | 10 +++++----- ...ec2_securitygroup_allow_wide_open_public_ipv4.py | 4 ++-- .../ec2_securitygroup_default_restrict_traffic.py | 4 ++-- .../ec2_securitygroup_from_launch_wizard.py | 4 ++-- .../ec2_securitygroup_not_used.py | 6 +++--- ..._securitygroup_with_many_ingress_egress_rules.py | 4 ++-- .../emr_cluster_publicly_accesible.py | 4 ++-- .../rds_instance_no_public_access.py | 2 +- 42 files changed, 113 insertions(+), 114 deletions(-) diff --git a/docs/developer-guide/checks.md b/docs/developer-guide/checks.md index f03568edfa8..e85cb1694ed 100644 --- a/docs/developer-guide/checks.md +++ b/docs/developer-guide/checks.md @@ -222,7 +222,7 @@ class ec2_securitygroup_with_many_ingress_egress_rules(Check): max_security_group_rules = ec2_client.audit_config.get( "max_security_group_rules", 50 ) - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): ``` ```yaml title="config.yaml" diff --git a/prowler/providers/aws/services/dms/dms_instance_no_public_access/dms_instance_no_public_access.py b/prowler/providers/aws/services/dms/dms_instance_no_public_access/dms_instance_no_public_access.py index f4f4cd79df2..ee7a9f22e37 100644 --- a/prowler/providers/aws/services/dms/dms_instance_no_public_access/dms_instance_no_public_access.py +++ b/prowler/providers/aws/services/dms/dms_instance_no_public_access/dms_instance_no_public_access.py @@ -22,7 +22,7 @@ def execute(self): if instance.security_groups: report.status = "PASS" report.status_extended = f"DMS Replication Instance {instance.id} is set as publicly accessible but filtered with security groups." - for security_group in ec2_client.security_groups: + for security_group in ec2_client.security_groups.values(): if security_group.id in instance.security_groups: for ingress_rule in security_group.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_cassandra_exposed_to_internet/ec2_instance_port_cassandra_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_cassandra_exposed_to_internet/ec2_instance_port_cassandra_exposed_to_internet.py index 4547d3e0ada..db1d66cf546 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_cassandra_exposed_to_internet/ec2_instance_port_cassandra_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_cassandra_exposed_to_internet/ec2_instance_port_cassandra_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_cifs_exposed_to_internet/ec2_instance_port_cifs_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_cifs_exposed_to_internet/ec2_instance_port_cifs_exposed_to_internet.py index ec64a6ff499..75ecf9b2a38 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_cifs_exposed_to_internet/ec2_instance_port_cifs_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_cifs_exposed_to_internet/ec2_instance_port_cifs_exposed_to_internet.py @@ -22,7 +22,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_elasticsearch_kibana_exposed_to_internet/ec2_instance_port_elasticsearch_kibana_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_elasticsearch_kibana_exposed_to_internet/ec2_instance_port_elasticsearch_kibana_exposed_to_internet.py index 740f270efb1..a337f6c7b3d 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_elasticsearch_kibana_exposed_to_internet/ec2_instance_port_elasticsearch_kibana_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_elasticsearch_kibana_exposed_to_internet/ec2_instance_port_elasticsearch_kibana_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_ftp_exposed_to_internet/ec2_instance_port_ftp_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_ftp_exposed_to_internet/ec2_instance_port_ftp_exposed_to_internet.py index f64276f5c88..cb02a726768 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_ftp_exposed_to_internet/ec2_instance_port_ftp_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_ftp_exposed_to_internet/ec2_instance_port_ftp_exposed_to_internet.py @@ -22,7 +22,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_kafka_exposed_to_internet/ec2_instance_port_kafka_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_kafka_exposed_to_internet/ec2_instance_port_kafka_exposed_to_internet.py index 99b0e9af955..7191cb1c333 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_kafka_exposed_to_internet/ec2_instance_port_kafka_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_kafka_exposed_to_internet/ec2_instance_port_kafka_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_kerberos_exposed_to_internet/ec2_instance_port_kerberos_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_kerberos_exposed_to_internet/ec2_instance_port_kerberos_exposed_to_internet.py index 8220a3977e2..95aae57a92f 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_kerberos_exposed_to_internet/ec2_instance_port_kerberos_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_kerberos_exposed_to_internet/ec2_instance_port_kerberos_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_ldap_exposed_to_internet/ec2_instance_port_ldap_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_ldap_exposed_to_internet/ec2_instance_port_ldap_exposed_to_internet.py index ebfb0ec847e..da0fddbdde6 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_ldap_exposed_to_internet/ec2_instance_port_ldap_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_ldap_exposed_to_internet/ec2_instance_port_ldap_exposed_to_internet.py @@ -22,7 +22,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_memcached_exposed_to_internet/ec2_instance_port_memcached_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_memcached_exposed_to_internet/ec2_instance_port_memcached_exposed_to_internet.py index cf6e9b7ea37..01847510d19 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_memcached_exposed_to_internet/ec2_instance_port_memcached_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_memcached_exposed_to_internet/ec2_instance_port_memcached_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_mongodb_exposed_to_internet/ec2_instance_port_mongodb_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_mongodb_exposed_to_internet/ec2_instance_port_mongodb_exposed_to_internet.py index b30b8208859..cd2709db115 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_mongodb_exposed_to_internet/ec2_instance_port_mongodb_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_mongodb_exposed_to_internet/ec2_instance_port_mongodb_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_mysql_exposed_to_internet/ec2_instance_port_mysql_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_mysql_exposed_to_internet/ec2_instance_port_mysql_exposed_to_internet.py index 91363fd8102..fb3b67decf7 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_mysql_exposed_to_internet/ec2_instance_port_mysql_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_mysql_exposed_to_internet/ec2_instance_port_mysql_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_oracle_exposed_to_internet/ec2_instance_port_oracle_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_oracle_exposed_to_internet/ec2_instance_port_oracle_exposed_to_internet.py index 8dc2799a933..9b29cebe6a5 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_oracle_exposed_to_internet/ec2_instance_port_oracle_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_oracle_exposed_to_internet/ec2_instance_port_oracle_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_postgresql_exposed_to_internet/ec2_instance_port_postgresql_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_postgresql_exposed_to_internet/ec2_instance_port_postgresql_exposed_to_internet.py index d9e91ef86c3..6212fe93c16 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_postgresql_exposed_to_internet/ec2_instance_port_postgresql_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_postgresql_exposed_to_internet/ec2_instance_port_postgresql_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_rdp_exposed_to_internet/ec2_instance_port_rdp_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_rdp_exposed_to_internet/ec2_instance_port_rdp_exposed_to_internet.py index c24610083e2..6272e046377 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_rdp_exposed_to_internet/ec2_instance_port_rdp_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_rdp_exposed_to_internet/ec2_instance_port_rdp_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_redis_exposed_to_internet/ec2_instance_port_redis_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_redis_exposed_to_internet/ec2_instance_port_redis_exposed_to_internet.py index 56e53241113..dfe48ed1f0f 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_redis_exposed_to_internet/ec2_instance_port_redis_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_redis_exposed_to_internet/ec2_instance_port_redis_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_sqlserver_exposed_to_internet/ec2_instance_port_sqlserver_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_sqlserver_exposed_to_internet/ec2_instance_port_sqlserver_exposed_to_internet.py index 7ad33cb7915..3b7bf52b5e0 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_sqlserver_exposed_to_internet/ec2_instance_port_sqlserver_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_sqlserver_exposed_to_internet/ec2_instance_port_sqlserver_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_ssh_exposed_to_internet/ec2_instance_port_ssh_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_ssh_exposed_to_internet/ec2_instance_port_ssh_exposed_to_internet.py index ccc15e2f197..2d1eba9927f 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_ssh_exposed_to_internet/ec2_instance_port_ssh_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_ssh_exposed_to_internet/ec2_instance_port_ssh_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_telnet_exposed_to_internet/ec2_instance_port_telnet_exposed_to_internet.py b/prowler/providers/aws/services/ec2/ec2_instance_port_telnet_exposed_to_internet/ec2_instance_port_telnet_exposed_to_internet.py index 662bab579e2..2c9bad6a276 100644 --- a/prowler/providers/aws/services/ec2/ec2_instance_port_telnet_exposed_to_internet/ec2_instance_port_telnet_exposed_to_internet.py +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_telnet_exposed_to_internet/ec2_instance_port_telnet_exposed_to_internet.py @@ -20,7 +20,7 @@ def execute(self): report.resource_tags = instance.tags is_open_port = False if instance.security_groups: - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id in instance.security_groups: for ingress_rule in sg.ingress_rules: if check_security_group( diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_all_ports/ec2_securitygroup_allow_ingress_from_internet_to_all_ports.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_all_ports/ec2_securitygroup_allow_ingress_from_internet_to_all_ports.py index 266fee1ac8c..ecea9f35fa0 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_all_ports/ec2_securitygroup_allow_ingress_from_internet_to_all_ports.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_all_ports/ec2_securitygroup_allow_ingress_from_internet_to_all_ports.py @@ -1,13 +1,13 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_all_ports(Check): def execute(self): findings = [] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -20,13 +20,13 @@ def execute(self): report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have all ports open to the Internet." report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags for ingress_rule in security_group.ingress_rules: if check_security_group(ingress_rule, "-1", any_address=True): ec2_client.set_failed_check( self.__class__.__name__, - security_group.arn, + security_group_arn, ) report.status = "FAIL" report.status_extended = f"Security group {security_group.name} ({security_group.id}) has all ports open to the Internet." diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port/ec2_securitygroup_allow_ingress_from_internet_to_any_port.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port/ec2_securitygroup_allow_ingress_from_internet_to_any_port.py index c07decc84c2..4a340eccc7d 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port/ec2_securitygroup_allow_ingress_from_internet_to_any_port.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port/ec2_securitygroup_allow_ingress_from_internet_to_any_port.py @@ -1,17 +1,17 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.ec2_service import NetworkInterface -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.ec2_service import NetworkInterface +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_any_port(Check): def execute(self): findings = [] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -24,12 +24,12 @@ def execute(self): report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have any port open to the Internet." report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: @@ -61,7 +61,6 @@ def check_enis( ): report.status_extended = f"Security group {security_group_name} ({security_group_id}) has at least one port open to the Internet but is exclusively not attached to any network interface." for eni in enis: - if self.is_allowed_eni_type(eni_type=eni.type): report.status = "PASS" report.status_extended = f"Security group {security_group_name} ({security_group_id}) has at least one port open to the Internet but is exclusively attached to an allowed network interface type ({eni.type})." diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018/ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018/ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.py index cf6a45734a7..e2395deac5b 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018/ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018/ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018.py @@ -1,17 +1,17 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_port_mongodb_27017_27018(Check): def execute(self): findings = [] check_ports = [27017, 27018] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -22,14 +22,14 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have MongoDB ports 27017 and 27018 open to the Internet." # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.py index e19460c6ae0..cb4ce51d120 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21/ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21.py @@ -1,17 +1,17 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_ftp_port_20_21(Check): def execute(self): findings = [] check_ports = [20, 21] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -24,12 +24,12 @@ def execute(self): report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have FTP ports 20 and 21 open to the Internet." report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.py index 8d3d9f6f598..878fa6da605 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22.py @@ -1,17 +1,17 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22(Check): def execute(self): findings = [] check_ports = [22] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -24,12 +24,12 @@ def execute(self): report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have SSH port 22 open to the Internet." report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.py index 97b6c0b39a8..25cc1442347 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389.py @@ -1,17 +1,17 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389(Check): def execute(self): findings = [] check_ports = [3389] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -24,12 +24,12 @@ def execute(self): report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have Microsoft RDP port 3389 open to the Internet." report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.py index a5152b759c7..3f7a55ff995 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888.py @@ -1,10 +1,10 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9160_8888( @@ -13,7 +13,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_cassandra_7199_9 def execute(self): findings = [] check_ports = [7199, 9160, 8888] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -24,14 +24,14 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have Casandra ports 7199, 8888 and 9160 open to the Internet." # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.py index 38c094a7069..d665dafafcd 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601.py @@ -1,10 +1,10 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_kibana_9200_9300_5601( @@ -13,7 +13,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_elasticsearch_ki def execute(self): findings = [] check_ports = [9200, 9300, 5601] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -24,14 +24,14 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have Elasticsearch/Kibana ports 9200, 9300 and 5601 open to the Internet." # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.py index c84003608a7..db0619e23a8 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092.py @@ -1,17 +1,17 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_kafka_9092(Check): def execute(self): findings = [] check_ports = [9092] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -22,14 +22,14 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have Kafka port 9092 open to the Internet." # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.py index 309848123bc..8ba1c53243f 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211.py @@ -1,17 +1,17 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_memcached_11211(Check): def execute(self): findings = [] check_ports = [11211] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -22,14 +22,14 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have Memcached port 11211 open to the Internet." # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.py index 182c1eb1c2e..104ea17bd73 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306.py @@ -1,17 +1,17 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306(Check): def execute(self): findings = [] check_ports = [3306] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -22,14 +22,14 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have MySQL port 3306 open to the Internet." # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.py index 2808585f472..58c740814c3 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483.py @@ -1,17 +1,17 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_oracle_1521_2483(Check): def execute(self): findings = [] check_ports = [1521, 2483] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -22,14 +22,14 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have Oracle ports 1521 and 2483 open to the Internet." # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/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.py b/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.py index 8e0d797db76..340469b7789 100644 --- a/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.py +++ b/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.py @@ -1,17 +1,17 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432(Check): def execute(self): findings = [] check_ports = [5432] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -22,14 +22,14 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have Postgres port 5432 open to the Internet." # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.py index 8170a85929a..8b78c9a22bb 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379.py @@ -1,17 +1,17 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_redis_6379(Check): def execute(self): findings = [] check_ports = [6379] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -22,14 +22,14 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have Redis port 6379 open to the Internet." # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.py index 307d53a9a79..8ee235db95d 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434.py @@ -1,10 +1,10 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_1434( @@ -13,7 +13,7 @@ class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_sql_server_1433_ def execute(self): findings = [] check_ports = [1433, 1434] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -24,14 +24,14 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have Microsoft SQL Server ports 1433 and 1434 open to the Internet." # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.py index 1fcbf1e8c6c..34d3e480f21 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23/ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23.py @@ -1,17 +1,17 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.vpc.vpc_client import vpc_client from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import ( ec2_securitygroup_allow_ingress_from_internet_to_all_ports, ) +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group +from prowler.providers.aws.services.vpc.vpc_client import vpc_client class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23(Check): def execute(self): findings = [] check_ports = [23] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -22,14 +22,14 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have Telnet port 23 open to the Internet." # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice if not ec2_client.is_failed_check( ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__, - security_group.arn, + security_group_arn, ): # Loop through every security group's ingress rule and check it for ingress_rule in security_group.ingress_rules: diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4.py index 549c8b30647..8d5e67e82dd 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_allow_wide_open_public_ipv4/ec2_securitygroup_allow_wide_open_public_ipv4.py @@ -9,7 +9,7 @@ class ec2_securitygroup_allow_wide_open_public_ipv4(Check): def execute(self): findings = [] cidr_treshold = 24 - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the SG is in use if ec2_client.provider.scan_unused_services or ( security_group.vpc_id in vpc_client.vpcs @@ -20,7 +20,7 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) has no potential wide-open non-RFC1918 address." diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_default_restrict_traffic/ec2_securitygroup_default_restrict_traffic.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_default_restrict_traffic/ec2_securitygroup_default_restrict_traffic.py index b9a4c300859..787f8b6e098 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_default_restrict_traffic/ec2_securitygroup_default_restrict_traffic.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_default_restrict_traffic/ec2_securitygroup_default_restrict_traffic.py @@ -6,7 +6,7 @@ class ec2_securitygroup_default_restrict_traffic(Check): def execute(self): findings = [] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Check if ignoring flag is set and if the VPC and the default SG are in used if security_group.name == "default" and ( ec2_client.provider.scan_unused_services @@ -20,7 +20,7 @@ def execute(self): report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "FAIL" report.status_extended = ( diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard.py index f67211120c3..9624e57c1ef 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_from_launch_wizard/ec2_securitygroup_from_launch_wizard.py @@ -5,12 +5,12 @@ class ec2_securitygroup_from_launch_wizard(Check): def execute(self): findings = [] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): report = Check_Report_AWS(self.metadata()) report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) was not created using the EC2 Launch Wizard." diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used.py index 8a232b33be4..076ea605e92 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used.py @@ -6,14 +6,14 @@ class ec2_securitygroup_not_used(Check): def execute(self): findings = [] - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): # Default security groups can not be deleted, so ignore them if security_group.name != "default": report = Check_Report_AWS(self.metadata()) report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) it is being used." @@ -22,7 +22,7 @@ def execute(self): for function in awslambda_client.functions.values(): if security_group.id in function.security_groups: sg_in_lambda = True - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if security_group.id in sg.associated_sgs: sg_associated = True if ( diff --git a/prowler/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules.py b/prowler/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules.py index f2fbca7ce71..a6db96a23b4 100644 --- a/prowler/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules.py +++ b/prowler/providers/aws/services/ec2/ec2_securitygroup_with_many_ingress_egress_rules/ec2_securitygroup_with_many_ingress_egress_rules.py @@ -10,12 +10,12 @@ def execute(self): max_security_group_rules = ec2_client.audit_config.get( "max_security_group_rules", 50 ) - for security_group in ec2_client.security_groups: + for security_group_arn, security_group in ec2_client.security_groups.items(): report = Check_Report_AWS(self.metadata()) report.region = security_group.region report.resource_details = security_group.name report.resource_id = security_group.id - report.resource_arn = security_group.arn + report.resource_arn = security_group_arn report.resource_tags = security_group.tags report.status = "PASS" report.status_extended = f"Security group {security_group.name} ({security_group.id}) has {len(security_group.ingress_rules)} inbound rules and {len(security_group.egress_rules)} outbound rules." diff --git a/prowler/providers/aws/services/emr/emr_cluster_publicly_accesible/emr_cluster_publicly_accesible.py b/prowler/providers/aws/services/emr/emr_cluster_publicly_accesible/emr_cluster_publicly_accesible.py index 18d6330c054..61ee1693961 100644 --- a/prowler/providers/aws/services/emr/emr_cluster_publicly_accesible/emr_cluster_publicly_accesible.py +++ b/prowler/providers/aws/services/emr/emr_cluster_publicly_accesible/emr_cluster_publicly_accesible.py @@ -40,7 +40,7 @@ def execute(self): master_public_security_groups = [] for master_sg in master_node_sg_groups: master_sg_public = False - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id == master_sg: for ingress_rule in sg.ingress_rules: if check_security_group(ingress_rule, -1): @@ -62,7 +62,7 @@ def execute(self): slave_public_security_groups = [] for slave_sg in slave_node_sg_groups: slave_sg_public = False - for sg in ec2_client.security_groups: + for sg in ec2_client.security_groups.values(): if sg.id == slave_sg: for ingress_rule in sg.ingress_rules: if check_security_group(ingress_rule, -1): diff --git a/prowler/providers/aws/services/rds/rds_instance_no_public_access/rds_instance_no_public_access.py b/prowler/providers/aws/services/rds/rds_instance_no_public_access/rds_instance_no_public_access.py index 85d25cead30..b99610ac7d4 100644 --- a/prowler/providers/aws/services/rds/rds_instance_no_public_access/rds_instance_no_public_access.py +++ b/prowler/providers/aws/services/rds/rds_instance_no_public_access/rds_instance_no_public_access.py @@ -25,7 +25,7 @@ def execute(self): report.status_extended = f"RDS Instance {db_instance.id} is set as publicly accessible but filtered with security groups." db_instance_port = db_instance.endpoint.get("Port") if db_instance_port: - for security_group in ec2_client.security_groups: + for security_group in ec2_client.security_groups.values(): if security_group.id in db_instance.security_groups: for ingress_rule in security_group.ingress_rules: if check_security_group( From 0e2798200c5c14106e3be9bd4489f421503f497c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Thu, 8 Aug 2024 14:08:49 +0200 Subject: [PATCH 12/20] feat(awslambda): Verificate ABL security group is public --- ...lambda_function_not_publicly_accessible.py | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py index d13151c1b7b..06352bdd2b1 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py @@ -1,5 +1,7 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.awslambda.awslambda_client import awslambda_client +from prowler.providers.aws.services.ec2.ec2_client import ec2_client +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group from prowler.providers.aws.services.elbv2.elbv2_client import elbv2_client @@ -19,7 +21,7 @@ def execute(self): ) # 1. Check if the function is associated with a public load balancer - target_group_redirects_alb_to_function = "" + target_group_redirects_public_alb_to_function = "" for tg_arn, target_group in elbv2_client.target_groups.items(): if ( @@ -33,15 +35,24 @@ def execute(self): if lb and lb.scheme == "internet-facing": # 1.1 Check if the ALB security group allows public access for sg_id in lb.security_group_ids: - pass - - target_group_redirects_alb_to_function = tg_arn - break + security_group = ec2_client.security_groups.get( + f"arn:{awslambda_client.audited_partition}:ec2:{awslambda_client.region}:{awslambda_client.audited_account}:security-group/{sg_id}", + None, + ) + if security_group: + for ingress_rule in security_group.ingress_rules: + if check_security_group( + ingress_rule, protocol="-1", ports=None + ): + target_group_redirects_public_alb_to_function = ( + tg_arn + ) + break public_policy = False # 2. Check if the function policy allows public access - if target_group_redirects_alb_to_function and function.policy: + if target_group_redirects_public_alb_to_function and function.policy: for statement in function.policy["Statement"]: # Only check allow statements if ( @@ -67,7 +78,7 @@ def execute(self): statement["Condition"] .get("ArnLike", {}) .get("AWS:SourceArn", "") - == target_group_redirects_alb_to_function + == target_group_redirects_public_alb_to_function ): public_policy = True break From 878c74ee10921f58745f42ff514f1ed5c973fd33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Thu, 8 Aug 2024 14:10:13 +0200 Subject: [PATCH 13/20] test(awslambda): Add test case for private segurity group --- ...a_function_not_publicly_accessible_test.py | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py b/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py index 1505d009794..9a0ae4d9963 100644 --- a/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py +++ b/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py @@ -13,6 +13,7 @@ def test_no_functions(self): aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + from prowler.providers.aws.services.ec2.ec2_service import EC2 from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 with mock.patch( @@ -24,6 +25,9 @@ def test_no_functions(self): ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", new=ELBv2(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.ec2_client", + new=EC2(aws_provider), ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( @@ -83,6 +87,7 @@ def test_function_public_but_not_internet_exposed(self): aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + from prowler.providers.aws.services.ec2.ec2_service import EC2 from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 with mock.patch( @@ -94,6 +99,9 @@ def test_function_public_but_not_internet_exposed(self): ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", new=ELBv2(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.ec2_client", + new=EC2(aws_provider), ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( @@ -260,6 +268,7 @@ def test_function_public_with_alb(self): aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + from prowler.providers.aws.services.ec2.ec2_service import EC2 from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 with mock.patch( @@ -271,6 +280,9 @@ def test_function_public_with_alb(self): ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", new=ELBv2(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.ec2_client", + new=EC2(aws_provider), ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( @@ -437,6 +449,7 @@ def test_function_linked_through_alb_but_not_public_due_to_restrictive_policies( aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + from prowler.providers.aws.services.ec2.ec2_service import EC2 from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 with mock.patch( @@ -448,6 +461,190 @@ def test_function_linked_through_alb_but_not_public_due_to_restrictive_policies( ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", new=ELBv2(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.ec2_client", + new=EC2(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( + awslambda_function_not_publicly_accessible, + ) + + check = awslambda_function_not_publicly_accessible() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION_EU_WEST_1 + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Lambda function {function_name} is not publicly accessible." + ) + assert result[0].resource_tags == [{"tag1": "value1", "tag2": "value2"}] + + @mock_aws + def test_function_not_public_with_alb_for_secure_security_group(self): + # Create the mock VPC + ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) + vpc = ec2_client.create_vpc(CidrBlock="10.0.0.0/16") + vpc_id = vpc["Vpc"]["VpcId"] + + # Create subnets + subnet_a = ec2_client.create_subnet( + VpcId=vpc_id, + CidrBlock="10.0.1.0/24", + AvailabilityZone=f"{AWS_REGION_EU_WEST_1}a", + ) + subnet_b = ec2_client.create_subnet( + VpcId=vpc_id, + CidrBlock="10.0.2.0/24", + AvailabilityZone=f"{AWS_REGION_EU_WEST_1}b", + ) + + # Create an Internet Gateway + igw = ec2_client.create_internet_gateway() + igw_id = igw["InternetGateway"]["InternetGatewayId"] + ec2_client.attach_internet_gateway(InternetGatewayId=igw_id, VpcId=vpc_id) + + # Create a Route Table and associate it with subnets + route_table = ec2_client.create_route_table(VpcId=vpc_id) + route_table_id = route_table["RouteTable"]["RouteTableId"] + ec2_client.create_route( + RouteTableId=route_table_id, + DestinationCidrBlock="0.0.0.0/0", + GatewayId=igw_id, + ) + ec2_client.associate_route_table( + RouteTableId=route_table_id, SubnetId=subnet_a["Subnet"]["SubnetId"] + ) + ec2_client.associate_route_table( + RouteTableId=route_table_id, SubnetId=subnet_b["Subnet"]["SubnetId"] + ) + + # Create the mock IAM role + iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1) + role_name = "test-role" + assume_role_policy_document = { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": {"Service": "lambda.amazonaws.com"}, + "Action": "sts:AssumeRole", + } + ], + } + role_arn = iam_client.create_role( + RoleName=role_name, + AssumeRolePolicyDocument=dumps(assume_role_policy_document), + )["Role"]["Arn"] + + function_name = "test-lambda" + + # Create the lambda function using boto3 client + lambda_client = client("lambda", region_name=AWS_REGION_EU_WEST_1) + function_arn = lambda_client.create_function( + FunctionName=function_name, + Runtime="python3.8", + Role=role_arn, + Handler="index.handler", + Code={"ZipFile": b"fileb://file-path/to/your-deployment-package.zip"}, + Description="Test Lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + Tags={"tag1": "value1", "tag2": "value2"}, + )["FunctionArn"] + + # Attach the policy to the lambda function with a wildcard principal + lambda_client.add_permission( + FunctionName=function_name, + StatementId="no-public-access", + Action="lambda:InvokeFunction", + Principal="*", + ) + + # Create a security group for ALB + sg = ec2_client.create_security_group( + GroupName="alb-sg", + Description="Security group for ALB", + VpcId=vpc_id, + ) + sg_id = sg["GroupId"] + ec2_client.authorize_security_group_ingress( + GroupId=sg_id, + IpPermissions=[ + { + "IpProtocol": "tcp", + "FromPort": 80, + "ToPort": 80, + "IpRanges": [{"CidrIp": "10.0.2.0/24"}], # Private IP range + } + ], + ) + + # Create the ALB + elbv2_client = client("elbv2", region_name=AWS_REGION_EU_WEST_1) + lb = elbv2_client.create_load_balancer( + Name="test-alb", + Subnets=[subnet_a["Subnet"]["SubnetId"], subnet_b["Subnet"]["SubnetId"]], + SecurityGroups=[sg_id], + Scheme="internet-facing", + Type="application", + IpAddressType="ipv4", + ) + lb_arn = lb["LoadBalancers"][0]["LoadBalancerArn"] + + # Create the Target Group for Lambda + target_group = elbv2_client.create_target_group( + Name="test-tg", + TargetType="lambda", + ) + target_group_arn = target_group["TargetGroups"][0]["TargetGroupArn"] + + # Add permission for ALB to invoke the Lambda function + lambda_client.add_permission( + FunctionName=function_name, + StatementId="alb-access", + Action="lambda:InvokeFunction", + Principal="elasticloadbalancing.amazonaws.com", + SourceArn=target_group_arn, + ) + + # Attach Lambda to Target Group + elbv2_client.register_targets( + TargetGroupArn=target_group_arn, + Targets=[{"Id": function_arn}], + ) + + # Create ALB Listener + elbv2_client.create_listener( + LoadBalancerArn=lb_arn, + Protocol="HTTP", + Port=80, + DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}], + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + from prowler.providers.aws.services.ec2.ec2_service import EC2 + from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", + new=Lambda(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", + new=ELBv2(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.ec2_client", + new=EC2(aws_provider), ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( From 1dee21b4474bae6b8b9b75ba312f4f05382de36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Tue, 13 Aug 2024 18:11:40 +0200 Subject: [PATCH 14/20] chore(awslambda): Revert changes --- ...tion_not_publicly_accessible.metadata.json | 4 +- ...lambda_function_not_publicly_accessible.py | 88 +-- .../aws/services/elbv2/elbv2_service.py | 44 -- ...a_function_not_publicly_accessible_test.py | 636 ++---------------- .../aws/services/elbv2/elbv2_service_test.py | 71 -- 5 files changed, 61 insertions(+), 782 deletions(-) diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json index e52f7f1bdd5..cdc5176105c 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json @@ -1,14 +1,14 @@ { "Provider": "aws", "CheckID": "awslambda_function_not_publicly_accessible", - "CheckTitle": "Check if Lambda functions is publicly accessible through Internet", + "CheckTitle": "Check if Lambda functions have resource-based policy set as Public.", "CheckType": [], "ServiceName": "lambda", "SubServiceName": "", "ResourceIdTemplate": "arn:partition:lambda:region:account-id:function/function-name", "Severity": "critical", "ResourceType": "AwsLambdaFunction", - "Description": "Check if Lambda functions is publicly accessible through Internet via ALB and function's permission.", + "Description": "Check if Lambda functions have resource-based policy set as Public.", "Risk": "Publicly accessible services could expose sensitive data to bad actors.", "RelatedUrl": "https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html", "Remediation": { diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py index 06352bdd2b1..a39b8576443 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py @@ -1,8 +1,5 @@ from prowler.lib.check.models import Check, Check_Report_AWS from prowler.providers.aws.services.awslambda.awslambda_client import awslambda_client -from prowler.providers.aws.services.ec2.ec2_client import ec2_client -from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group -from prowler.providers.aws.services.elbv2.elbv2_client import elbv2_client class awslambda_function_not_publicly_accessible(Check): @@ -16,81 +13,30 @@ def execute(self): report.resource_tags = function.tags report.status = "PASS" - report.status_extended = ( - f"Lambda function {function.name} is not publicly accessible." - ) + report.status_extended = f"Lambda function {function.name} has a policy resource-based policy not public." - # 1. Check if the function is associated with a public load balancer - target_group_redirects_public_alb_to_function = "" - - for tg_arn, target_group in elbv2_client.target_groups.items(): - if ( - target_group.target_type == "lambda" - ) and function.arn in target_group.targets: - for lb_arn in target_group.load_balancer_arns: - lb = elbv2_client.loadbalancersv2.get( - lb_arn, - None, # Bug: If prowler is filtering by ARN, this will fail because loadbalancersv2 are filtered - ) - if lb and lb.scheme == "internet-facing": - # 1.1 Check if the ALB security group allows public access - for sg_id in lb.security_group_ids: - security_group = ec2_client.security_groups.get( - f"arn:{awslambda_client.audited_partition}:ec2:{awslambda_client.region}:{awslambda_client.audited_account}:security-group/{sg_id}", - None, - ) - if security_group: - for ingress_rule in security_group.ingress_rules: - if check_security_group( - ingress_rule, protocol="-1", ports=None - ): - target_group_redirects_public_alb_to_function = ( - tg_arn - ) - break - - public_policy = False - - # 2. Check if the function policy allows public access - if target_group_redirects_public_alb_to_function and function.policy: + public_access = False + if function.policy: for statement in function.policy["Statement"]: # Only check allow statements - if ( - statement["Effect"] == "Allow" - and ( + if statement["Effect"] == "Allow": + if ( "*" in statement["Principal"] - or "*" in statement["Principal"].get("AWS", "") - or "*" in statement["Principal"].get("CanonicalUser", "") - or "elasticloadbalancing.amazonaws.com" - == statement["Principal"].get("Service", "") - ) - and ( - "*" in statement["Action"] - or "lambda:InvokeFunction" in statement["Action"] - ) - and ( - "*" in statement["Resource"] - or function.arn in statement["Resource"] - ) - ): - if statement.get("Condition", {}): - if ( - statement["Condition"] - .get("ArnLike", {}) - .get("AWS:SourceArn", "") - == target_group_redirects_public_alb_to_function - ): - public_policy = True - break - else: - public_policy = True + or ( + "AWS" in statement["Principal"] + and "*" in statement["Principal"]["AWS"] + ) + or ( + "CanonicalUser" in statement["Principal"] + and "*" in statement["Principal"]["CanonicalUser"] + ) + ): + public_access = True break - if public_policy: + if public_access: report.status = "FAIL" - report.status_extended = ( - f"Lambda function {function.name} is publicly accessible." - ) + report.status_extended = f"Lambda function {function.name} has a policy resource-based policy with public access." findings.append(report) diff --git a/prowler/providers/aws/services/elbv2/elbv2_service.py b/prowler/providers/aws/services/elbv2/elbv2_service.py index 94f2e233279..dd3626dd3a7 100644 --- a/prowler/providers/aws/services/elbv2/elbv2_service.py +++ b/prowler/providers/aws/services/elbv2/elbv2_service.py @@ -27,8 +27,6 @@ def __init__(self, provider): ], ) self.__threading_call__(self._describe_tags, self.loadbalancersv2.items()) - self.target_groups = {} - self.__threading_call__(self._describe_target_groups) def _describe_load_balancers(self, regional_client): logger.info("ELBv2 - Describing load balancers...") @@ -49,7 +47,6 @@ def _describe_load_balancers(self, regional_client): type=elbv2["Type"], dns=elbv2.get("DNSName", None), scheme=elbv2.get("Scheme", None), - security_group_ids=elbv2.get("SecurityGroups", []), ) except Exception as error: logger.error( @@ -177,35 +174,6 @@ def _describe_tags(self, load_balancer): f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - def _describe_target_groups(self, regional_client): - logger.info("ELBv2 - Describing target groups...") - try: - describe_target_groups_paginator = regional_client.get_paginator( - "describe_target_groups" - ) - for page in describe_target_groups_paginator.paginate(): - for target_group in page["TargetGroups"]: - targets = { - (tg["Target"]["Id"], tg["Target"].get("Port", 0)) - for tg in regional_client.describe_target_health( - TargetGroupArn=target_group["TargetGroupArn"] - )["TargetHealthDescriptions"] - } - - self.target_groups[target_group["TargetGroupArn"]] = TargetGroup( - name=target_group.get("TargetGroupName", ""), - protocol=target_group.get("Protocol", ""), - port=target_group.get("Port", 0), - vpc_id=target_group.get("VpcId", ""), - target_type=target_group.get("TargetType", ""), - targets=dict(targets), - load_balancer_arns=target_group.get("LoadBalancerArns", []), - ) - except Exception as error: - logger.error( - f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) - class ListenerRule(BaseModel): arn: str @@ -232,16 +200,4 @@ class LoadBalancerv2(BaseModel): drop_invalid_header_fields: Optional[str] listeners: Dict[str, Listenerv2] = {} scheme: Optional[str] - security_group_ids: list[str] tags: Optional[list] = [] - - -class TargetGroup(BaseModel): - name: str - protocol: str - port: int - vpc_id: str - target_type: str - # Key: ID, Value: Port (only available for IP targets) - targets: Dict[str, int] = [] - load_balancer_arns: list[str] diff --git a/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py b/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py index 9a0ae4d9963..1b9a1460327 100644 --- a/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py +++ b/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py @@ -4,7 +4,12 @@ from boto3 import client from moto import mock_aws -from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider +from prowler.providers.aws.services.awslambda.awslambda_service import Function +from tests.providers.aws.utils import ( + AWS_ACCOUNT_NUMBER, + AWS_REGION_EU_WEST_1, + set_mocked_aws_provider, +) class Test_awslambda_function_not_publicly_accessible: @@ -13,8 +18,6 @@ def test_no_functions(self): aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) from prowler.providers.aws.services.awslambda.awslambda_service import Lambda - from prowler.providers.aws.services.ec2.ec2_service import EC2 - from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 with mock.patch( "prowler.providers.common.provider.Provider.get_global_provider", @@ -22,12 +25,6 @@ def test_no_functions(self): ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", new=Lambda(aws_provider), - ), mock.patch( - "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", - new=ELBv2(aws_provider), - ), mock.patch( - "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.ec2_client", - new=EC2(aws_provider), ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( @@ -40,7 +37,7 @@ def test_no_functions(self): assert len(result) == 0 @mock_aws - def test_function_public_but_not_internet_exposed(self): + def test_function_public(self): # Create the mock IAM role iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1) role_name = "test-role" @@ -82,194 +79,12 @@ def test_function_public_but_not_internet_exposed(self): StatementId="public-access", Action="lambda:InvokeFunction", Principal="*", + SourceArn=function_arn, ) aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) from prowler.providers.aws.services.awslambda.awslambda_service import Lambda - from prowler.providers.aws.services.ec2.ec2_service import EC2 - from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 - - with mock.patch( - "prowler.providers.common.provider.Provider.get_global_provider", - return_value=aws_provider, - ), mock.patch( - "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", - new=Lambda(aws_provider), - ), mock.patch( - "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", - new=ELBv2(aws_provider), - ), mock.patch( - "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.ec2_client", - new=EC2(aws_provider), - ): - # Test Check - from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( - awslambda_function_not_publicly_accessible, - ) - - check = awslambda_function_not_publicly_accessible() - result = check.execute() - - assert len(result) == 1 - assert result[0].region == AWS_REGION_EU_WEST_1 - assert result[0].resource_id == function_name - assert result[0].resource_arn == function_arn - assert result[0].status == "PASS" - assert ( - result[0].status_extended - == f"Lambda function {function_name} is not publicly accessible." - ) - assert result[0].resource_tags == [{"tag1": "value1", "tag2": "value2"}] - - @mock_aws - def test_function_public_with_alb(self): - # Create the mock VPC - ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) - vpc = ec2_client.create_vpc(CidrBlock="10.0.0.0/16") - vpc_id = vpc["Vpc"]["VpcId"] - - # Create subnets - subnet_a = ec2_client.create_subnet( - VpcId=vpc_id, - CidrBlock="10.0.1.0/24", - AvailabilityZone=f"{AWS_REGION_EU_WEST_1}a", - ) - subnet_b = ec2_client.create_subnet( - VpcId=vpc_id, - CidrBlock="10.0.2.0/24", - AvailabilityZone=f"{AWS_REGION_EU_WEST_1}b", - ) - - # Create an Internet Gateway - igw = ec2_client.create_internet_gateway() - igw_id = igw["InternetGateway"]["InternetGatewayId"] - ec2_client.attach_internet_gateway(InternetGatewayId=igw_id, VpcId=vpc_id) - - # Create a Route Table and associate it with subnets - route_table = ec2_client.create_route_table(VpcId=vpc_id) - route_table_id = route_table["RouteTable"]["RouteTableId"] - ec2_client.create_route( - RouteTableId=route_table_id, - DestinationCidrBlock="0.0.0.0/0", - GatewayId=igw_id, - ) - ec2_client.associate_route_table( - RouteTableId=route_table_id, SubnetId=subnet_a["Subnet"]["SubnetId"] - ) - ec2_client.associate_route_table( - RouteTableId=route_table_id, SubnetId=subnet_b["Subnet"]["SubnetId"] - ) - - # Create the mock IAM role - iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1) - role_name = "test-role" - assume_role_policy_document = { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": {"Service": "lambda.amazonaws.com"}, - "Action": "sts:AssumeRole", - } - ], - } - role_arn = iam_client.create_role( - RoleName=role_name, - AssumeRolePolicyDocument=dumps(assume_role_policy_document), - )["Role"]["Arn"] - - function_name = "test-public-lambda" - - # Create the lambda function using boto3 client - lambda_client = client("lambda", region_name=AWS_REGION_EU_WEST_1) - function_arn = lambda_client.create_function( - FunctionName=function_name, - Runtime="python3.8", - Role=role_arn, - Handler="index.handler", - Code={"ZipFile": b"fileb://file-path/to/your-deployment-package.zip"}, - Description="Test Lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - Tags={"tag1": "value1", "tag2": "value2"}, - )["FunctionArn"] - - # Attach the policy to the lambda function with a wildcard principal - lambda_client.add_permission( - FunctionName=function_name, - StatementId="public-access", - Action="lambda:InvokeFunction", - Principal="*", - ) - - # Create a security group for ALB - sg = ec2_client.create_security_group( - GroupName="alb-sg", - Description="Security group for ALB", - VpcId=vpc_id, - ) - sg_id = sg["GroupId"] - ec2_client.authorize_security_group_ingress( - GroupId=sg_id, - IpPermissions=[ - { - "IpProtocol": "tcp", - "FromPort": 80, - "ToPort": 80, - "IpRanges": [{"CidrIp": "0.0.0.0/0"}], - } - ], - ) - - # Create the ALB - elbv2_client = client("elbv2", region_name=AWS_REGION_EU_WEST_1) - lb = elbv2_client.create_load_balancer( - Name="test-alb", - Subnets=[subnet_a["Subnet"]["SubnetId"], subnet_b["Subnet"]["SubnetId"]], - SecurityGroups=[sg_id], - Scheme="internet-facing", - Type="application", - IpAddressType="ipv4", - ) - lb_arn = lb["LoadBalancers"][0]["LoadBalancerArn"] - - # Create the Target Group for Lambda - target_group = elbv2_client.create_target_group( - Name="test-public-lambda-tg", - TargetType="lambda", - ) - target_group_arn = target_group["TargetGroups"][0]["TargetGroupArn"] - - # Add permission for ALB to invoke the Lambda function - lambda_client.add_permission( - FunctionName=function_name, - StatementId="alb-access", - Action="lambda:InvokeFunction", - Principal="elasticloadbalancing.amazonaws.com", - SourceArn=target_group_arn, - ) - - # Attach Lambda to Target Group - elbv2_client.register_targets( - TargetGroupArn=target_group_arn, - Targets=[{"Id": function_arn}], - ) - - # Create ALB Listener - elbv2_client.create_listener( - LoadBalancerArn=lb_arn, - Protocol="HTTP", - Port=80, - DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}], - ) - - aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) - - from prowler.providers.aws.services.awslambda.awslambda_service import Lambda - from prowler.providers.aws.services.ec2.ec2_service import EC2 - from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 with mock.patch( "prowler.providers.common.provider.Provider.get_global_provider", @@ -277,12 +92,6 @@ def test_function_public_with_alb(self): ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", new=Lambda(aws_provider), - ), mock.patch( - "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", - new=ELBv2(aws_provider), - ), mock.patch( - "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.ec2_client", - new=EC2(aws_provider), ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( @@ -299,51 +108,12 @@ def test_function_public_with_alb(self): assert result[0].status == "FAIL" assert ( result[0].status_extended - == f"Lambda function {function_name} is publicly accessible." + == f"Lambda function {function_name} has a policy resource-based policy with public access." ) assert result[0].resource_tags == [{"tag1": "value1", "tag2": "value2"}] @mock_aws - def test_function_linked_through_alb_but_not_public_due_to_restrictive_policies( - self, - ): - # Create the mock VPC - ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) - vpc = ec2_client.create_vpc(CidrBlock="10.0.0.0/16") - vpc_id = vpc["Vpc"]["VpcId"] - - # Create subnets - subnet_a = ec2_client.create_subnet( - VpcId=vpc_id, - CidrBlock="10.0.1.0/24", - AvailabilityZone=f"{AWS_REGION_EU_WEST_1}a", - ) - subnet_b = ec2_client.create_subnet( - VpcId=vpc_id, - CidrBlock="10.0.2.0/24", - AvailabilityZone=f"{AWS_REGION_EU_WEST_1}b", - ) - - # Create an Internet Gateway - igw = ec2_client.create_internet_gateway() - igw_id = igw["InternetGateway"]["InternetGatewayId"] - ec2_client.attach_internet_gateway(InternetGatewayId=igw_id, VpcId=vpc_id) - - # Create a Route Table and associate it with subnets - route_table = ec2_client.create_route_table(VpcId=vpc_id) - route_table_id = route_table["RouteTable"]["RouteTableId"] - ec2_client.create_route( - RouteTableId=route_table_id, - DestinationCidrBlock="0.0.0.0/0", - GatewayId=igw_id, - ) - ec2_client.associate_route_table( - RouteTableId=route_table_id, SubnetId=subnet_a["Subnet"]["SubnetId"] - ) - ec2_client.associate_route_table( - RouteTableId=route_table_id, SubnetId=subnet_b["Subnet"]["SubnetId"] - ) - + def test_function_not_public(self): # Create the mock IAM role iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1) role_name = "test-role" @@ -368,7 +138,7 @@ def test_function_linked_through_alb_but_not_public_due_to_restrictive_policies( lambda_client = client("lambda", region_name=AWS_REGION_EU_WEST_1) function_arn = lambda_client.create_function( FunctionName=function_name, - Runtime="python3.8", + Runtime="nodejs4.3", Role=role_arn, Handler="index.handler", Code={"ZipFile": b"fileb://file-path/to/your-deployment-package.zip"}, @@ -379,78 +149,18 @@ def test_function_linked_through_alb_but_not_public_due_to_restrictive_policies( Tags={"tag1": "value1", "tag2": "value2"}, )["FunctionArn"] - # Create a security group for ALB - sg = ec2_client.create_security_group( - GroupName="alb-sg", - Description="Security group for ALB", - VpcId=vpc_id, - ) - sg_id = sg["GroupId"] - ec2_client.authorize_security_group_ingress( - GroupId=sg_id, - IpPermissions=[ - { - "IpProtocol": "tcp", - "FromPort": 80, - "ToPort": 80, - "IpRanges": [{"CidrIp": "0.0.0.0/0"}], - } - ], - ) - - # Create the ALB - elbv2_client = client("elbv2", region_name=AWS_REGION_EU_WEST_1) - lb = elbv2_client.create_load_balancer( - Name="test-alb", - Subnets=[subnet_a["Subnet"]["SubnetId"], subnet_b["Subnet"]["SubnetId"]], - SecurityGroups=[sg_id], - Scheme="internet-facing", - Type="application", - IpAddressType="ipv4", - ) - lb_arn = lb["LoadBalancers"][0]["LoadBalancerArn"] - - # Create the Target Group for Lambda - target_group = elbv2_client.create_target_group( - Name="test-tg", - TargetType="lambda", - ) - target_group_arn = target_group["TargetGroups"][0]["TargetGroupArn"] - - # Add permission for ALB to invoke the Lambda function + # Attach the policy to the lambda function with a specific AWS account number as principal lambda_client.add_permission( FunctionName=function_name, - StatementId="no-alb-access", + StatementId="public-access", Action="lambda:InvokeFunction", - Principal="elasticloadbalancing.amazonaws.com", + Principal=AWS_ACCOUNT_NUMBER, SourceArn=function_arn, ) - # Attach Lambda to Target Group - elbv2_client.register_targets( - TargetGroupArn=target_group_arn, - Targets=[{"Id": function_arn}], - ) - - # Create ALB Listener - elbv2_client.create_listener( - LoadBalancerArn=lb_arn, - Protocol="HTTP", - Port=80, - DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}], - ) - - # Set restrictive policy on Lambda - lambda_client.put_function_concurrency( - FunctionName=function_name, - ReservedConcurrentExecutions=0, # Restrictive policy: no concurrent executions allowed - ) - aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) from prowler.providers.aws.services.awslambda.awslambda_service import Lambda - from prowler.providers.aws.services.ec2.ec2_service import EC2 - from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 with mock.patch( "prowler.providers.common.provider.Provider.get_global_provider", @@ -458,12 +168,6 @@ def test_function_linked_through_alb_but_not_public_due_to_restrictive_policies( ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", new=Lambda(aws_provider), - ), mock.patch( - "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", - new=ELBv2(aws_provider), - ), mock.patch( - "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.ec2_client", - new=EC2(aws_provider), ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( @@ -480,171 +184,47 @@ def test_function_linked_through_alb_but_not_public_due_to_restrictive_policies( assert result[0].status == "PASS" assert ( result[0].status_extended - == f"Lambda function {function_name} is not publicly accessible." + == f"Lambda function {function_name} has a policy resource-based policy not public." ) assert result[0].resource_tags == [{"tag1": "value1", "tag2": "value2"}] - @mock_aws - def test_function_not_public_with_alb_for_secure_security_group(self): - # Create the mock VPC - ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) - vpc = ec2_client.create_vpc(CidrBlock="10.0.0.0/16") - vpc_id = vpc["Vpc"]["VpcId"] - - # Create subnets - subnet_a = ec2_client.create_subnet( - VpcId=vpc_id, - CidrBlock="10.0.1.0/24", - AvailabilityZone=f"{AWS_REGION_EU_WEST_1}a", - ) - subnet_b = ec2_client.create_subnet( - VpcId=vpc_id, - CidrBlock="10.0.2.0/24", - AvailabilityZone=f"{AWS_REGION_EU_WEST_1}b", - ) - - # Create an Internet Gateway - igw = ec2_client.create_internet_gateway() - igw_id = igw["InternetGateway"]["InternetGatewayId"] - ec2_client.attach_internet_gateway(InternetGatewayId=igw_id, VpcId=vpc_id) - - # Create a Route Table and associate it with subnets - route_table = ec2_client.create_route_table(VpcId=vpc_id) - route_table_id = route_table["RouteTable"]["RouteTableId"] - ec2_client.create_route( - RouteTableId=route_table_id, - DestinationCidrBlock="0.0.0.0/0", - GatewayId=igw_id, - ) - ec2_client.associate_route_table( - RouteTableId=route_table_id, SubnetId=subnet_a["Subnet"]["SubnetId"] - ) - ec2_client.associate_route_table( - RouteTableId=route_table_id, SubnetId=subnet_b["Subnet"]["SubnetId"] - ) - - # Create the mock IAM role - iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1) - role_name = "test-role" - assume_role_policy_document = { + def test_function_public_with_canonical(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "nodejs4.3" + function_arn = f"arn:aws:lambda:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:function/{function_name}" + lambda_policy = { "Version": "2012-10-17", "Statement": [ { + "Sid": "public-access", + "Principal": {"CanonicalUser": ["*"]}, "Effect": "Allow", - "Principal": {"Service": "lambda.amazonaws.com"}, - "Action": "sts:AssumeRole", + "Action": [ + "lambda:InvokeFunction", + ], + "Resource": [function_arn], } ], } - role_arn = iam_client.create_role( - RoleName=role_name, - AssumeRolePolicyDocument=dumps(assume_role_policy_document), - )["Role"]["Arn"] - - function_name = "test-lambda" - - # Create the lambda function using boto3 client - lambda_client = client("lambda", region_name=AWS_REGION_EU_WEST_1) - function_arn = lambda_client.create_function( - FunctionName=function_name, - Runtime="python3.8", - Role=role_arn, - Handler="index.handler", - Code={"ZipFile": b"fileb://file-path/to/your-deployment-package.zip"}, - Description="Test Lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - Tags={"tag1": "value1", "tag2": "value2"}, - )["FunctionArn"] - - # Attach the policy to the lambda function with a wildcard principal - lambda_client.add_permission( - FunctionName=function_name, - StatementId="no-public-access", - Action="lambda:InvokeFunction", - Principal="*", - ) - - # Create a security group for ALB - sg = ec2_client.create_security_group( - GroupName="alb-sg", - Description="Security group for ALB", - VpcId=vpc_id, - ) - sg_id = sg["GroupId"] - ec2_client.authorize_security_group_ingress( - GroupId=sg_id, - IpPermissions=[ - { - "IpProtocol": "tcp", - "FromPort": 80, - "ToPort": 80, - "IpRanges": [{"CidrIp": "10.0.2.0/24"}], # Private IP range - } - ], - ) - - # Create the ALB - elbv2_client = client("elbv2", region_name=AWS_REGION_EU_WEST_1) - lb = elbv2_client.create_load_balancer( - Name="test-alb", - Subnets=[subnet_a["Subnet"]["SubnetId"], subnet_b["Subnet"]["SubnetId"]], - SecurityGroups=[sg_id], - Scheme="internet-facing", - Type="application", - IpAddressType="ipv4", - ) - lb_arn = lb["LoadBalancers"][0]["LoadBalancerArn"] - - # Create the Target Group for Lambda - target_group = elbv2_client.create_target_group( - Name="test-tg", - TargetType="lambda", - ) - target_group_arn = target_group["TargetGroups"][0]["TargetGroupArn"] - - # Add permission for ALB to invoke the Lambda function - lambda_client.add_permission( - FunctionName=function_name, - StatementId="alb-access", - Action="lambda:InvokeFunction", - Principal="elasticloadbalancing.amazonaws.com", - SourceArn=target_group_arn, - ) - - # Attach Lambda to Target Group - elbv2_client.register_targets( - TargetGroupArn=target_group_arn, - Targets=[{"Id": function_arn}], - ) - # Create ALB Listener - elbv2_client.create_listener( - LoadBalancerArn=lb_arn, - Protocol="HTTP", - Port=80, - DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}], - ) - - aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) - - from prowler.providers.aws.services.awslambda.awslambda_service import Lambda - from prowler.providers.aws.services.ec2.ec2_service import EC2 - from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2 + lambda_client.functions = { + "function_name": Function( + name=function_name, + security_groups=[], + arn=function_arn, + region=AWS_REGION_EU_WEST_1, + runtime=function_runtime, + policy=lambda_policy, + ) + } with mock.patch( "prowler.providers.common.provider.Provider.get_global_provider", - return_value=aws_provider, + return_value=set_mocked_aws_provider(), ), mock.patch( "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", - new=Lambda(aws_provider), - ), mock.patch( - "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.elbv2_client", - new=ELBv2(aws_provider), - ), mock.patch( - "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.ec2_client", - new=EC2(aws_provider), + new=lambda_client, ): # Test Check from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( @@ -658,141 +238,9 @@ def test_function_not_public_with_alb_for_secure_security_group(self): assert result[0].region == AWS_REGION_EU_WEST_1 assert result[0].resource_id == function_name assert result[0].resource_arn == function_arn - assert result[0].status == "PASS" + assert result[0].status == "FAIL" assert ( result[0].status_extended - == f"Lambda function {function_name} is not publicly accessible." + == f"Lambda function {function_name} has a policy resource-based policy with public access." ) - assert result[0].resource_tags == [{"tag1": "value1", "tag2": "value2"}] - - # @mock_aws - # def test_function_not_public_policies(self): - # # Create the mock IAM role - # iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1) - # role_name = "test-role" - # assume_role_policy_document = { - # "Version": "2012-10-17", - # "Statement": [ - # { - # "Effect": "Allow", - # "Principal": {"Service": "lambda.amazonaws.com"}, - # "Action": "sts:AssumeRole", - # } - # ], - # } - # role_arn = iam_client.create_role( - # RoleName=role_name, - # AssumeRolePolicyDocument=dumps(assume_role_policy_document), - # )["Role"]["Arn"] - - # function_name = "test-lambda" - - # # Create the lambda function using boto3 client - # lambda_client = client("lambda", region_name=AWS_REGION_EU_WEST_1) - # function_arn = lambda_client.create_function( - # FunctionName=function_name, - # Runtime="nodejs4.3", - # Role=role_arn, - # Handler="index.handler", - # Code={"ZipFile": b"fileb://file-path/to/your-deployment-package.zip"}, - # Description="Test Lambda function", - # Timeout=3, - # MemorySize=128, - # Publish=True, - # Tags={"tag1": "value1", "tag2": "value2"}, - # )["FunctionArn"] - - # # Attach the policy to the lambda function with a specific AWS account number as principal - # lambda_client.add_permission( - # FunctionName=function_name, - # StatementId="public-access", - # Action="lambda:InvokeFunction", - # Principal=AWS_ACCOUNT_NUMBER, - # ) - - # aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) - - # from prowler.providers.aws.services.awslambda.awslambda_service import Lambda - - # with mock.patch( - # "prowler.providers.common.provider.Provider.get_global_provider", - # return_value=aws_provider, - # ), mock.patch( - # "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", - # new=Lambda(aws_provider), - # ): - # # Test Check - # from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( - # awslambda_function_not_publicly_accessible, - # ) - - # check = awslambda_function_not_publicly_accessible() - # result = check.execute() - - # assert len(result) == 1 - # assert result[0].region == AWS_REGION_EU_WEST_1 - # assert result[0].resource_id == function_name - # assert result[0].resource_arn == function_arn - # assert result[0].status == "PASS" - # assert ( - # result[0].status_extended - # == f"Lambda function {function_name} has a policy resource-based policy not public." - # ) - # assert result[0].resource_tags == [{"tag1": "value1", "tag2": "value2"}] - - # def test_function_public_with_canonical(self): - # lambda_client = mock.MagicMock - # function_name = "test-lambda" - # function_runtime = "nodejs4.3" - # function_arn = f"arn:aws:lambda:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:function/{function_name}" - # lambda_policy = { - # "Version": "2012-10-17", - # "Statement": [ - # { - # "Sid": "public-access", - # "Principal": {"CanonicalUser": ["*"]}, - # "Effect": "Allow", - # "Action": [ - # "lambda:InvokeFunction", - # ], - # "Resource": [function_arn], - # } - # ], - # } - - # lambda_client.functions = { - # "function_name": Function( - # name=function_name, - # security_groups=[], - # arn=function_arn, - # region=AWS_REGION_EU_WEST_1, - # runtime=function_runtime, - # policy=lambda_policy, - # ) - # } - - # with mock.patch( - # "prowler.providers.common.provider.Provider.get_global_provider", - # return_value=set_mocked_aws_provider(), - # ), mock.patch( - # "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", - # new=lambda_client, - # ): - # # Test Check - # from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( - # awslambda_function_not_publicly_accessible, - # ) - - # check = awslambda_function_not_publicly_accessible() - # result = check.execute() - - # assert len(result) == 1 - # assert result[0].region == AWS_REGION_EU_WEST_1 - # assert result[0].resource_id == function_name - # assert result[0].resource_arn == function_arn - # assert result[0].status == "FAIL" - # assert ( - # result[0].status_extended - # == f"Lambda function {function_name} has a policy resource-based policy with public access." - # ) - # assert result[0].resource_tags == [] + assert result[0].resource_tags == [] diff --git a/tests/providers/aws/services/elbv2/elbv2_service_test.py b/tests/providers/aws/services/elbv2/elbv2_service_test.py index b19fd664ffc..cb7bd517106 100644 --- a/tests/providers/aws/services/elbv2/elbv2_service_test.py +++ b/tests/providers/aws/services/elbv2/elbv2_service_test.py @@ -327,74 +327,3 @@ def test_describe_tags(self): elbv2.loadbalancersv2[lb["LoadBalancerArn"]].tags[1]["Key"] == "Environment" ) assert elbv2.loadbalancersv2[lb["LoadBalancerArn"]].tags[1]["Value"] == "dev" - - @mock_aws - def test_describe_target_groups(self): - conn = client("elbv2", region_name=AWS_REGION_EU_WEST_1) - ec2 = resource("ec2", region_name=AWS_REGION_EU_WEST_1) - - security_group = ec2.create_security_group( - GroupName="a-security-group", Description="First One" - ) - vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default") - subnet1 = ec2.create_subnet( - VpcId=vpc.id, - CidrBlock="172.28.7.192/26", - AvailabilityZone=AWS_REGION_EU_WEST_1_AZA, - ) - subnet2 = ec2.create_subnet( - VpcId=vpc.id, - CidrBlock="172.28.7.0/26", - AvailabilityZone=AWS_REGION_EU_WEST_1_AZB, - ) - - lb = conn.create_load_balancer( - Name="my-lb", - Subnets=[subnet1.id, subnet2.id], - SecurityGroups=[security_group.id], - Scheme="internal", - )["LoadBalancers"][0] - - target_group = conn.create_target_group( - Name="my-target-group", - Protocol="HTTP", - Port=80, - VpcId=vpc.id, - HealthCheckProtocol="HTTP", - HealthCheckPort="80", - HealthCheckPath="/", - )["TargetGroups"][0] - - conn.create_listener( - LoadBalancerArn=lb["LoadBalancerArn"], - Protocol="HTTP", - Port=80, - DefaultActions=[ - {"Type": "forward", "TargetGroupArn": target_group["TargetGroupArn"]} - ], - ) - - # ELBv2 client for this test class - aws_provider = set_mocked_aws_provider( - [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] - ) - - elbv2 = ELBv2(aws_provider) - - assert len(elbv2.target_groups) == 1 - assert target_group["TargetGroupArn"] in elbv2.target_groups - assert ( - elbv2.target_groups[target_group["TargetGroupArn"]].name - == "my-target-group" - ) - assert elbv2.target_groups[target_group["TargetGroupArn"]].protocol == "HTTP" - assert elbv2.target_groups[target_group["TargetGroupArn"]].port == 80 - assert elbv2.target_groups[target_group["TargetGroupArn"]].vpc_id == vpc.id - assert elbv2.target_groups[ - target_group["TargetGroupArn"] - ].load_balancer_arns == [lb["LoadBalancerArn"]] - assert ( - elbv2.target_groups[target_group["TargetGroupArn"]].target_type - == "instance" - ) - assert elbv2.target_groups[target_group["TargetGroupArn"]].targets == {} From c4bc0032568062df649c282b497dd90416706572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Tue, 13 Aug 2024 18:20:19 +0200 Subject: [PATCH 15/20] feat(awslabmda): Add other resources comprobations that can invoke the function --- ...slambda_function_not_publicly_accessible.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py index a39b8576443..37dc9b19ce4 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py @@ -22,13 +22,19 @@ def execute(self): if statement["Effect"] == "Allow": if ( "*" in statement["Principal"] + or "*" in statement["Principal"].get("AWS", "") + or "*" in statement["Principal"].get("CanonicalUser", "") or ( - "AWS" in statement["Principal"] - and "*" in statement["Principal"]["AWS"] - ) - or ( - "CanonicalUser" in statement["Principal"] - and "*" in statement["Principal"]["CanonicalUser"] + ( + "elasticloadbalancing.amazonaws.com" + == statement["Principal"].get("Service", "") + or "apigateway.amazonaws.com" + == statement["Principal"].get("Service", "") + ) + and ( + "*" in statement.get("Action", "") + or "InvokeFunction" in statement.get("Action", "") + ) ) ): public_access = True From 1f4b6d73ff6da651f92c3f2c2ca0808d2743933d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Tue, 13 Aug 2024 19:16:26 +0200 Subject: [PATCH 16/20] feat(awslambda): Verificate that other services can invoke function --- ...lambda_function_not_publicly_accessible.py | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py index 37dc9b19ce4..9bb06c55504 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py @@ -19,26 +19,32 @@ def execute(self): if function.policy: for statement in function.policy["Statement"]: # Only check allow statements - if statement["Effect"] == "Allow": - if ( - "*" in statement["Principal"] - or "*" in statement["Principal"].get("AWS", "") - or "*" in statement["Principal"].get("CanonicalUser", "") - or ( - ( - "elasticloadbalancing.amazonaws.com" - == statement["Principal"].get("Service", "") - or "apigateway.amazonaws.com" - == statement["Principal"].get("Service", "") - ) - and ( - "*" in statement.get("Action", "") - or "InvokeFunction" in statement.get("Action", "") + if statement["Effect"] == "Allow" and ( + "*" in statement["Principal"] + or ( + isinstance(statement["Principal"], dict) + and ( + "*" in statement["Principal"].get("AWS", "") + or "*" + in statement["Principal"].get("CanonicalUser", "") + or ( # Check if function can be invoked by other AWS services + ( + "elasticloadbalancing.amazonaws.com" + == statement["Principal"].get("Service", "") + or "apigateway.amazonaws.com" + == statement["Principal"].get("Service", "") + ) + and ( + "*" in statement.get("Action", "") + or "InvokeFunction" + in statement.get("Action", "") + ) ) ) - ): - public_access = True - break + ) + ): + public_access = True + break if public_access: report.status = "FAIL" From 1cdb1e9a22f2edd43cd0ae7136d537d15e98920f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Tue, 13 Aug 2024 19:17:50 +0200 Subject: [PATCH 17/20] chore(awslambda): Add note about false PASS case --- .../awslambda_function_not_publicly_accessible.metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json index cdc5176105c..218244c8502 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json @@ -28,5 +28,5 @@ ], "DependsOn": [], "RelatedTo": [], - "Notes": "" + "Notes": "Exists a false PASS case. A function could be exposed publicly with a policy that allows the Principal as an AWS account ID and the Action as lambda:InvokeFunction. In this case the function could be exposed by other resource like an ALB or API Gateway inside of the Principal AWS Account." } From 9a445d4ac5ecb748c06d67373aa939ef65db27b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Tue, 13 Aug 2024 19:19:46 +0200 Subject: [PATCH 18/20] test(awslambda): Add test case for ALB expose case --- ...a_function_not_publicly_accessible_test.py | 230 ++++++++++++++++++ 1 file changed, 230 insertions(+) diff --git a/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py b/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py index 1b9a1460327..86c240c8c9f 100644 --- a/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py +++ b/tests/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py @@ -244,3 +244,233 @@ def test_function_public_with_canonical(self): == f"Lambda function {function_name} has a policy resource-based policy with public access." ) assert result[0].resource_tags == [] + + @mock_aws + def test_function_public_with_alb(self): + # Create the mock VPC + ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) + vpc = ec2_client.create_vpc(CidrBlock="10.0.0.0/16") + vpc_id = vpc["Vpc"]["VpcId"] + + # Create subnets + subnet_a = ec2_client.create_subnet( + VpcId=vpc_id, + CidrBlock="10.0.1.0/24", + AvailabilityZone=f"{AWS_REGION_EU_WEST_1}a", + ) + subnet_b = ec2_client.create_subnet( + VpcId=vpc_id, + CidrBlock="10.0.2.0/24", + AvailabilityZone=f"{AWS_REGION_EU_WEST_1}b", + ) + + # Create an Internet Gateway + igw = ec2_client.create_internet_gateway() + igw_id = igw["InternetGateway"]["InternetGatewayId"] + ec2_client.attach_internet_gateway(InternetGatewayId=igw_id, VpcId=vpc_id) + + # Create a Route Table and associate it with subnets + route_table = ec2_client.create_route_table(VpcId=vpc_id) + route_table_id = route_table["RouteTable"]["RouteTableId"] + ec2_client.create_route( + RouteTableId=route_table_id, + DestinationCidrBlock="0.0.0.0/0", + GatewayId=igw_id, + ) + ec2_client.associate_route_table( + RouteTableId=route_table_id, SubnetId=subnet_a["Subnet"]["SubnetId"] + ) + ec2_client.associate_route_table( + RouteTableId=route_table_id, SubnetId=subnet_b["Subnet"]["SubnetId"] + ) + + # Create the mock IAM role + iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1) + role_name = "test-role" + assume_role_policy_document = { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": {"Service": "lambda.amazonaws.com"}, + "Action": "sts:AssumeRole", + } + ], + } + role_arn = iam_client.create_role( + RoleName=role_name, + AssumeRolePolicyDocument=dumps(assume_role_policy_document), + )["Role"]["Arn"] + + function_name = "test-public-lambda" + + # Create the lambda function using boto3 client + lambda_client = client("lambda", region_name=AWS_REGION_EU_WEST_1) + function_arn = lambda_client.create_function( + FunctionName=function_name, + Runtime="python3.8", + Role=role_arn, + Handler="index.handler", + Code={"ZipFile": b"fileb://file-path/to/your-deployment-package.zip"}, + Description="Test Lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + Tags={"tag1": "value1", "tag2": "value2"}, + )["FunctionArn"] + + # Attach the policy to the lambda function with a wildcard principal + lambda_client.add_permission( + FunctionName=function_name, + StatementId="public-access", + Action="lambda:InvokeFunction", + Principal="*", + ) + + # Create a security group for ALB + sg = ec2_client.create_security_group( + GroupName="alb-sg", + Description="Security group for ALB", + VpcId=vpc_id, + ) + sg_id = sg["GroupId"] + ec2_client.authorize_security_group_ingress( + GroupId=sg_id, + IpPermissions=[ + { + "IpProtocol": "tcp", + "FromPort": 80, + "ToPort": 80, + "IpRanges": [{"CidrIp": "0.0.0.0/0"}], + } + ], + ) + + # Create the ALB + elbv2_client = client("elbv2", region_name=AWS_REGION_EU_WEST_1) + lb = elbv2_client.create_load_balancer( + Name="test-alb", + Subnets=[subnet_a["Subnet"]["SubnetId"], subnet_b["Subnet"]["SubnetId"]], + SecurityGroups=[sg_id], + Scheme="internet-facing", + Type="application", + IpAddressType="ipv4", + ) + lb_arn = lb["LoadBalancers"][0]["LoadBalancerArn"] + + # Create the Target Group for Lambda + target_group = elbv2_client.create_target_group( + Name="test-public-lambda-tg", + TargetType="lambda", + ) + target_group_arn = target_group["TargetGroups"][0]["TargetGroupArn"] + + # Add permission for ALB to invoke the Lambda function + lambda_client.add_permission( + FunctionName=function_name, + StatementId="alb-access", + Action="lambda:InvokeFunction", + Principal="elasticloadbalancing.amazonaws.com", + SourceArn=target_group_arn, + ) + + # Attach Lambda to Target Group + elbv2_client.register_targets( + TargetGroupArn=target_group_arn, + Targets=[{"Id": function_arn}], + ) + + # Create ALB Listener + elbv2_client.create_listener( + LoadBalancerArn=lb_arn, + Protocol="HTTP", + Port=80, + DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}], + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", + new=Lambda(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( + awslambda_function_not_publicly_accessible, + ) + + check = awslambda_function_not_publicly_accessible() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION_EU_WEST_1 + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Lambda function test-public-lambda has a policy resource-based policy with public access." + ) + assert result[0].resource_tags == [{"tag1": "value1", "tag2": "value2"}] + + # def test_function_could_be_invoked_by_specific_aws_account(self): + # lambda_client = mock.MagicMock + # function_name = "test-lambda" + # function_runtime = "nodejs4.3" + # function_arn = f"arn:aws:lambda:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:function/{function_name}" + # lambda_policy = { # If there is an ALB or API Gateway in specified AWS Account, the lambda function could be invoked and exposed by them + # "Version": "2012-10-17", + # "Statement": [ + # { + # "Sid": "public-access", + # "Principal": {"AWS": AWS_ACCOUNT_NUMBER}, + # "Effect": "Allow", + # "Action": [ + # "lambda:InvokeFunction", + # ], + # "Resource": [function_arn], + # } + # ], + # } + + # lambda_client.functions = { + # "function_name": Function( + # name=function_name, + # security_groups=[], + # arn=function_arn, + # region=AWS_REGION_EU_WEST_1, + # runtime=function_runtime, + # policy=lambda_policy, + # ) + # } + + # with mock.patch( + # "prowler.providers.common.provider.Provider.get_global_provider", + # return_value=set_mocked_aws_provider(), + # ), mock.patch( + # "prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible.awslambda_client", + # new=lambda_client, + # ): + # # Test Check + # from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( + # awslambda_function_not_publicly_accessible, + # ) + + # check = awslambda_function_not_publicly_accessible() + # result = check.execute() + + # assert len(result) == 1 + # assert result[0].region == AWS_REGION_EU_WEST_1 + # assert result[0].resource_id == function_name + # assert result[0].resource_arn == function_arn + # assert result[0].status == "FAIL" + # assert ( + # result[0].status_extended + # == f"Lambda function {function_name} has a policy resource-based policy with public access." + # ) + # assert result[0].resource_tags == [] From f8f77d55c75328e4588c1fd6960d2c72c0098d96 Mon Sep 17 00:00:00 2001 From: Sergio Garcia <38561120+sergargar@users.noreply.github.com> Date: Fri, 16 Aug 2024 11:59:40 -0400 Subject: [PATCH 19/20] check all AWS services --- .../awslambda_function_not_publicly_accessible.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py index 9bb06c55504..292bd739538 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py @@ -29,10 +29,8 @@ def execute(self): in statement["Principal"].get("CanonicalUser", "") or ( # Check if function can be invoked by other AWS services ( - "elasticloadbalancing.amazonaws.com" - == statement["Principal"].get("Service", "") - or "apigateway.amazonaws.com" - == statement["Principal"].get("Service", "") + "amazonaws.com" + in statement["Principal"].get("Service", "") ) and ( "*" in statement.get("Action", "") From bdb9d779b5920b3124b1ada46da21b8dbed40d37 Mon Sep 17 00:00:00 2001 From: Sergio Garcia <38561120+sergargar@users.noreply.github.com> Date: Fri, 16 Aug 2024 12:06:23 -0400 Subject: [PATCH 20/20] enhance check's metadata --- .../awslambda_function_not_publicly_accessible.metadata.json | 2 +- .../awslambda_function_not_publicly_accessible.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json index 218244c8502..8bff12f44d5 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json @@ -28,5 +28,5 @@ ], "DependsOn": [], "RelatedTo": [], - "Notes": "Exists a false PASS case. A function could be exposed publicly with a policy that allows the Principal as an AWS account ID and the Action as lambda:InvokeFunction. In this case the function could be exposed by other resource like an ALB or API Gateway inside of the Principal AWS Account." + "Notes": "It gives a false positive if the function is exposed publicly by an other public resource like an ALB or API Gateway in an AWS Account when an AWS account ID is set as the principal of the policy." } diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py index 292bd739538..20c6526b929 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py +++ b/prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py @@ -29,7 +29,7 @@ def execute(self): in statement["Principal"].get("CanonicalUser", "") or ( # Check if function can be invoked by other AWS services ( - "amazonaws.com" + ".amazonaws.com" in statement["Principal"].get("Service", "") ) and (