diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_inside_vpc/__init__.py b/prowler/providers/aws/services/awslambda/awslambda_function_inside_vpc/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_inside_vpc/awslambda_function_inside_vpc.metadata.json b/prowler/providers/aws/services/awslambda/awslambda_function_inside_vpc/awslambda_function_inside_vpc.metadata.json new file mode 100644 index 0000000000..ed9b02372e --- /dev/null +++ b/prowler/providers/aws/services/awslambda/awslambda_function_inside_vpc/awslambda_function_inside_vpc.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "aws", + "CheckID": "awslambda_function_inside_vpc", + "CheckTitle": "Ensure AWS Lambda Functions Are Deployed Inside a VPC", + "CheckType": [], + "ServiceName": "lambda", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:lambda:region:account-id:function/function-name", + "Severity": "low", + "ResourceType": "AwsLambdaFunction", + "Description": "This check verifies whether an AWS Lambda function is deployed within a Virtual Private Cloud (VPC). Deploying Lambda functions inside a VPC improves security by allowing control over the network environment, reducing the exposure to public internet threats.", + "Risk": "Lambda functions not deployed in a VPC may expose your application to increased security risks, including unauthorized access and data breaches. Without the network isolation provided by a VPC, your Lambda functions are more vulnerable to attacks.", + "RelatedUrl": "https://docs.aws.amazon.com/lambda/latest/dg/configuration-vpc.html", + "Remediation": { + "Code": { + "CLI": "aws lambda update-function-configuration --region --function-name --vpc-config SubnetIds=,,SecurityGroupIds=", + "NativeIaC": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/Lambda/function-in-vpc.html", + "Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/lambda-controls.html#lambda-3", + "Terraform": "https://docs.prowler.com/checks/aws/general-policies/ensure-that-aws-lambda-function-is-configured-inside-a-vpc-1/" + }, + "Recommendation": { + "Text": "Configure your AWS Lambda functions to operate within a Virtual Private Cloud (VPC) to enhance security and control network access.", + "Url": "" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_inside_vpc/awslambda_function_inside_vpc.py b/prowler/providers/aws/services/awslambda/awslambda_function_inside_vpc/awslambda_function_inside_vpc.py new file mode 100644 index 0000000000..c48e7d4d50 --- /dev/null +++ b/prowler/providers/aws/services/awslambda/awslambda_function_inside_vpc/awslambda_function_inside_vpc.py @@ -0,0 +1,27 @@ +from typing import List + +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.awslambda.awslambda_client import awslambda_client + + +class awslambda_function_inside_vpc(Check): + def execute(self) -> List[Check_Report_AWS]: + findings = [] + for function_arn, function in awslambda_client.functions.items(): + report = Check_Report_AWS(self.metadata()) + report.region = function.region + report.resource_id = function.name + report.resource_arn = function_arn + report.resource_tags = function.tags + report.status = "FAIL" + report.status_extended = ( + f"Lambda function {function.name} is not inside a VPC" + ) + + if function.vpc_id: + report.status = "PASS" + report.status_extended = f"Lambda function {function.name} is inside of VPC {function.vpc_id}" + + findings.append(report) + + return findings diff --git a/prowler/providers/aws/services/awslambda/awslambda_service.py b/prowler/providers/aws/services/awslambda/awslambda_service.py index 287bbb74a5..ab4f4f3e3c 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_service.py +++ b/prowler/providers/aws/services/awslambda/awslambda_service.py @@ -37,13 +37,13 @@ class Lambda(AWSService): ): lambda_name = function["FunctionName"] lambda_arn = function["FunctionArn"] + vpc_config = function.get("VpcConfig", {}) # We must use the Lambda ARN as the dict key since we could have Lambdas in different regions with the same name self.functions[lambda_arn] = Function( name=lambda_name, arn=lambda_arn, - security_groups=function.get("VpcConfig", {}).get( - "SecurityGroupIds", [] - ), + security_groups=vpc_config.get("SecurityGroupIds", []), + vpc_id=vpc_config.get("VpcId"), region=regional_client.region, ) if "Runtime" in function: @@ -201,4 +201,5 @@ class Function(BaseModel): policy: dict = None code: LambdaCode = None url_config: URLConfig = None + vpc_id: Optional[str] tags: Optional[list] = [] diff --git a/tests/providers/aws/services/awslambda/awslambda_function_inside_vpc/awslambda_function_inside_vpc_test.py b/tests/providers/aws/services/awslambda/awslambda_function_inside_vpc/awslambda_function_inside_vpc_test.py new file mode 100644 index 0000000000..05a2d89f4e --- /dev/null +++ b/tests/providers/aws/services/awslambda/awslambda_function_inside_vpc/awslambda_function_inside_vpc_test.py @@ -0,0 +1,155 @@ +from json import dumps +from unittest import mock + +from boto3 import client +from moto import mock_aws + +from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider + + +class Test_awslambda_function_inside_vpc: + def test_no_functions(self): + from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + + aws_provider = set_mocked_aws_provider(AWS_REGION_EU_WEST_1) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_inside_vpc.awslambda_function_inside_vpc.awslambda_client", + new=Lambda(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.awslambda.awslambda_function_inside_vpc.awslambda_function_inside_vpc import ( + awslambda_function_inside_vpc, + ) + + check = awslambda_function_inside_vpc() + result = check.execute() + + assert len(result) == 0 + + @mock_aws + def test_function_inside_vpc(self): + # Create IAM Role for Lambda Function + 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"] + + # Create Lambda Function inside VPC + lambda_client = client("lambda", region_name=AWS_REGION_EU_WEST_1) + function_name = "test_function" + function_arn = lambda_client.create_function( + FunctionName=function_name, + Runtime="python3.8", + Role=role_arn, + Handler="lambda_function.lambda_handler", + Code={"ZipFile": b"file not used"}, + VpcConfig={ + "SubnetIds": ["subnet-12345678"], + "SecurityGroupIds": ["sg-12345678"], + }, + )["FunctionArn"] + + from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_inside_vpc.awslambda_function_inside_vpc.awslambda_client", + new=Lambda(aws_provider), + ): + from prowler.providers.aws.services.awslambda.awslambda_function_inside_vpc.awslambda_function_inside_vpc import ( + awslambda_function_inside_vpc, + ) + + check = awslambda_function_inside_vpc() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Lambda function test_function is inside of VPC vpc-123abc" + ) + 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].resource_tags == [{}] + + @mock_aws + def test_function_outside_vpc(self): + # Create IAM Role for Lambda Function + 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"] + + # Create Lambda Function outside VPC + lambda_client = client("lambda", region_name=AWS_REGION_EU_WEST_1) + function_name = "test_function" + function_arn = lambda_client.create_function( + FunctionName=function_name, + Runtime="python3.8", + Role=role_arn, + Handler="lambda_function.lambda_handler", + Code={"ZipFile": b"file not used"}, + )["FunctionArn"] + + from prowler.providers.aws.services.awslambda.awslambda_service import Lambda + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.awslambda.awslambda_function_inside_vpc.awslambda_function_inside_vpc.awslambda_client", + new=Lambda(aws_provider), + ): + from prowler.providers.aws.services.awslambda.awslambda_function_inside_vpc.awslambda_function_inside_vpc import ( + awslambda_function_inside_vpc, + ) + + check = awslambda_function_inside_vpc() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Lambda function test_function is not inside a VPC" + ) + 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].resource_tags == [{}] diff --git a/tests/providers/aws/services/awslambda/awslambda_service_test.py b/tests/providers/aws/services/awslambda/awslambda_service_test.py index d900ac5f6a..62c90cc67e 100644 --- a/tests/providers/aws/services/awslambda/awslambda_service_test.py +++ b/tests/providers/aws/services/awslambda/awslambda_service_test.py @@ -220,6 +220,7 @@ class Test_Lambda_Service: assert awslambda.functions[ lambda_arn_1 ].url_config.cors_config.allow_origins == ["*"] + assert awslambda.functions[lambda_arn_1].vpc_id == "vpc-123abc" assert awslambda.functions[lambda_arn_1].tags == [{"test": "test"}]