From 1ac22bddd6746d1fd92ea345b95d74872de2d5ec Mon Sep 17 00:00:00 2001 From: Sergio Garcia <38561120+sergargar@users.noreply.github.com> Date: Thu, 19 Oct 2023 12:13:24 +0200 Subject: [PATCH] fix(security group): check if security groups are used by Lambda (#2944) --- .../services/awslambda/awslambda_service.py | 4 + .../ec2_securitygroup_not_used.py | 7 +- ...rations_cloudtrail_logging_enabled_test.py | 4 + ...lambda_function_no_secrets_in_code_test.py | 2 + ...a_function_no_secrets_in_variables_test.py | 3 + ...a_function_not_publicly_accessible_test.py | 3 + ...awslambda_function_url_cors_policy_test.py | 3 + .../awslambda_function_url_public_test.py | 2 + ..._function_using_supported_runtimes_test.py | 7 +- .../ec2_securitygroup_not_used_test.py | 80 ++++++++++++++++++- 10 files changed, 112 insertions(+), 3 deletions(-) diff --git a/prowler/providers/aws/services/awslambda/awslambda_service.py b/prowler/providers/aws/services/awslambda/awslambda_service.py index 5b2997e0..3b9d307e 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_service.py +++ b/prowler/providers/aws/services/awslambda/awslambda_service.py @@ -50,6 +50,9 @@ class Lambda(AWSService): self.functions[lambda_arn] = Function( name=lambda_name, arn=lambda_arn, + security_groups=function.get("VpcConfig", {}).get( + "SecurityGroupIds", [] + ), region=regional_client.region, ) if "Runtime" in function: @@ -183,6 +186,7 @@ class URLConfig(BaseModel): class Function(BaseModel): name: str arn: str + security_groups: list runtime: Optional[str] environment: dict = None region: str 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 382e428a..d7d9daa8 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 @@ -1,4 +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 @@ -16,7 +17,11 @@ class ec2_securitygroup_not_used(Check): 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." - if len(security_group.network_interfaces) == 0: + sg_in_lambda = False + for function in awslambda_client.functions.values(): + if security_group.id in function.security_groups: + sg_in_lambda = True + if len(security_group.network_interfaces) == 0 and not sg_in_lambda: report.status = "FAIL" report.status_extended = f"Security group {security_group.name} ({security_group.id}) it is not being used." diff --git a/tests/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled_test.py b/tests/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled_test.py index 268208b9..376aa5a5 100644 --- a/tests/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled_test.py +++ b/tests/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled_test.py @@ -100,6 +100,7 @@ class Test_awslambda_function_invoke_api_operations_cloudtrail_logging_enabled: lambda_client.functions = { function_name: Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -164,6 +165,7 @@ class Test_awslambda_function_invoke_api_operations_cloudtrail_logging_enabled: lambda_client.functions = { function_name: Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -240,6 +242,7 @@ class Test_awslambda_function_invoke_api_operations_cloudtrail_logging_enabled: lambda_client.functions = { function_name: Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -317,6 +320,7 @@ class Test_awslambda_function_invoke_api_operations_cloudtrail_logging_enabled: lambda_client.functions = { function_name: Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, diff --git a/tests/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code_test.py b/tests/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code_test.py index 784ed5a0..f24c0ce5 100644 --- a/tests/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code_test.py +++ b/tests/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code_test.py @@ -47,6 +47,7 @@ class Test_awslambda_function_no_secrets_in_code: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -95,6 +96,7 @@ class Test_awslambda_function_no_secrets_in_code: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, diff --git a/tests/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables_test.py b/tests/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables_test.py index 9a43ec79..3f3207cc 100644 --- a/tests/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables_test.py +++ b/tests/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables_test.py @@ -37,6 +37,7 @@ class Test_awslambda_function_no_secrets_in_variables: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -77,6 +78,7 @@ class Test_awslambda_function_no_secrets_in_variables: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -118,6 +120,7 @@ class Test_awslambda_function_no_secrets_in_variables: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, 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 2c643e7a..777095be 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 @@ -51,6 +51,7 @@ class Test_awslambda_function_not_publicly_accessible: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -106,6 +107,7 @@ class Test_awslambda_function_not_publicly_accessible: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -161,6 +163,7 @@ class Test_awslambda_function_not_publicly_accessible: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, diff --git a/tests/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy_test.py b/tests/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy_test.py index 7b7fee96..dd0de932 100644 --- a/tests/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy_test.py +++ b/tests/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy_test.py @@ -41,6 +41,7 @@ class Test_awslambda_function_url_cors_policy: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -85,6 +86,7 @@ class Test_awslambda_function_url_cors_policy: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -129,6 +131,7 @@ class Test_awslambda_function_url_cors_policy: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, diff --git a/tests/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public_test.py b/tests/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public_test.py index 9a79d9f7..bbb7898a 100644 --- a/tests/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public_test.py +++ b/tests/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public_test.py @@ -41,6 +41,7 @@ class Test_awslambda_function_url_public: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -85,6 +86,7 @@ class Test_awslambda_function_url_public: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, diff --git a/tests/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes_test.py b/tests/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes_test.py index e4cb7a41..7185996c 100644 --- a/tests/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes_test.py +++ b/tests/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes_test.py @@ -36,6 +36,7 @@ class Test_awslambda_function_using_supported_runtimes: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -93,6 +94,7 @@ class Test_awslambda_function_using_supported_runtimes: lambda_client.functions = { "function_name": Function( name=function_name, + security_groups=[], arn=function_arn, region=AWS_REGION, runtime=function_runtime, @@ -148,7 +150,10 @@ class Test_awslambda_function_using_supported_runtimes: ) lambda_client.functions = { "function_name": Function( - name=function_name, arn=function_arn, region=AWS_REGION + name=function_name, + security_groups=[], + arn=function_arn, + region=AWS_REGION, ) } diff --git a/tests/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used_test.py b/tests/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used_test.py index e51048a5..28acdf0d 100644 --- a/tests/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used_test.py +++ b/tests/providers/aws/services/ec2/ec2_securitygroup_not_used/ec2_securitygroup_not_used_test.py @@ -2,7 +2,7 @@ from re import search from unittest import mock from boto3 import client, resource, session -from moto import mock_ec2 +from moto import mock_ec2, mock_iam, mock_lambda from prowler.providers.aws.lib.audit_info.models import AWS_Audit_Info from prowler.providers.common.models import Audit_Metadata @@ -169,3 +169,81 @@ class Test_ec2_securitygroup_not_used: assert result[0].resource_id == sg.id assert result[0].resource_details == sg_name assert result[0].resource_tags == [] + + @mock_ec2 + @mock_lambda + @mock_iam + def test_ec2_used_default_sg_by_lambda(self): + # Create EC2 Mocked Resources + ec2 = resource("ec2", AWS_REGION) + ec2_client = client("ec2", region_name=AWS_REGION) + vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"] + sg_name = "test-sg" + sg = ec2.create_security_group( + GroupName=sg_name, Description="test", VpcId=vpc_id + ) + subnet = ec2.create_subnet(VpcId=vpc_id, CidrBlock="10.0.0.0/18") + subnet.create_network_interface(Groups=[sg.id]) + iam_client = client("iam", region_name=AWS_REGION) + iam_role = iam_client.create_role( + RoleName="my-role", + AssumeRolePolicyDocument="some policy", + Path="/my-path/", + )["Role"]["Arn"] + lambda_client = client("lambda", AWS_REGION) + lambda_client.create_function( + FunctionName="test-function", + Runtime="python3.11", + Role=iam_role, + Handler="lambda_function.lambda_handler", + Code={ + "ImageUri": f"{AWS_ACCOUNT_NUMBER}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr:prod" + }, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + VpcConfig={ + "SecurityGroupIds": [sg.id], + "SubnetIds": [subnet.id], + }, + ) + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + current_audit_info = self.set_mocked_audit_info() + + with mock.patch( + "prowler.providers.aws.lib.audit_info.audit_info.current_audit_info", + new=current_audit_info, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_securitygroup_not_used.ec2_securitygroup_not_used.ec2_client", + new=EC2(current_audit_info), + ): + # Test Check + from prowler.providers.aws.services.ec2.ec2_securitygroup_not_used.ec2_securitygroup_not_used import ( + ec2_securitygroup_not_used, + ) + + check = ec2_securitygroup_not_used() + result = check.execute() + + # One custom sg + assert len(result) == 1 + assert result[0].status == "PASS" + assert result[0].region == AWS_REGION + assert ( + result[0].status_extended + == f"Security group {sg_name} ({sg.id}) it is being used." + ) + assert search( + "it is being used", + result[0].status_extended, + ) + assert ( + result[0].resource_arn + == f"arn:{current_audit_info.audited_partition}:ec2:{AWS_REGION}:{current_audit_info.audited_account}:security-group/{sg.id}" + ) + assert result[0].resource_id == sg.id + assert result[0].resource_details == sg_name + assert result[0].resource_tags == []