Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aws): Add new AWS Lambda check to check public access via other services #3642

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"Provider": "aws",
"CheckID": "awslambda_function_not_directly_publicly_accessible_via_elbv2",
"CheckTitle": "Check if Lambda functions have public application load balancer ahead of them.",
"CheckType": [],
"ServiceName": "lambda",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:lambda:region:account-id:function/function-name",
"Severity": "critical",
"ResourceType": "AwsLambdaFunction",
"Description": "Check if Lambda functions have public application load balancer ahead of them.",
"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": {
"Code": {
"CLI": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/Lambda/function-exposed.html",
"NativeIaC": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/Lambda/function-exposed.html",
"Other": "",
"Terraform": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/Lambda/function-exposed.html"
},
"Recommendation": {
"Text": "Place security groups around public load balancers",
"Url": "https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html"
}
},
"Categories": [
"internet-exposed"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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_directly_publicly_accessible_via_elbv2(Check):
def execute(self):
findings = []
public_lambda_funcs = {}

for tg in elbv2_client.target_groups:
if tg.target_type == 'lambda':
public_lambda_funcs[tg.target] = tg.lbdns

for function in awslambda_client.functions.values():
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 = "PASS"
report.status_extended = f"Lambda function {function.name} is not behind a internet facing load balancer."

if function.arn in public_lambda_funcs:
report.status = "FAIL"
report.status_extended = f"Lambda function {function.name} is behind a internet facing load balancer {public_lambda_funcs[function.arn]}."
findings.append(report)
return findings
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "ec2_instance_not_directly_publicly_accessible_via_elb",
"CheckTitle": "Check for EC2 instances behind internet facing classic load balancers.",
"CheckType": [
"Infrastructure Security"
],
"ServiceName": "ec2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsEc2Instance",
"Description": "Check for EC2 instances behind internet facing classic load balancers.",
"Risk": "Exposing an EC2 to a classic load balancer that is internet facing can lead to comprimisation",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Apply security groups to classic load balancers",
"Url": ""
}
},
"Categories": [
"internet-exposed"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
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.elb.elb_client import elb_client


class ec2_instance_not_directly_publicly_accessible_via_elb(Check):
def execute(self):
findings = []
public_instances = {}

#classic load balancers
for lb in elb_client.loadbalancers:
#check for public elbs
if lb.scheme == "internet-facing" and len(lb.security_groups) > 0:
for instance in lb.instances:
public_instances[instance] = lb

for instance in ec2_client.instances:
if instance.state != "terminated":
report = Check_Report_AWS(self.metadata())
report.region = instance.region
report.resource_id = instance.id
report.resource_arn = instance.arn
report.resource_tags = instance.tags
report.status = "PASS"
report.status_extended = f"EC2 Instance {instance.id} is not behind a internet facing classic load balancer."

#if the instanceId of the public lb is the same as the instances that are active, fail
if instance.id in public_instances:
report.status = "FAIL"
report.status_extended = f"EC2 Instance {instance.id} is behind a internet facing classic load balancer {public_instances[instance.id].dns}."
findings.append(report)
return findings

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "ec2_instance_not_directly_publicly_accessible_via_elbv2",
"CheckTitle": "Check for EC2 instances behind internet facing ALB/NLB/GLB.",
"CheckType": [
"Infrastructure Security"
],
"ServiceName": "ec2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsEc2Instance",
"Description": "Check for EC2 instances behind internet facing ALB/NLB/GLB.",
"Risk": "Exposing an EC2 to a ALB/NLB/GLB that is internet facing can lead to comprimisation",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Apply security groups to load balancers",
"Url": ""
}
},
"Categories": [
"internet-exposed"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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.elbv2.elbv2_client import elbv2_client

class ec2_instance_not_directly_publicly_accessible_via_elbv2(Check):
def execute(self):
findings = []
public_instances = {} # instanceid: elb

#target groups already filtered by public elbv2
for tg in elbv2_client.target_groups:
if tg.target_type == 'instance':
public_instances[tg.target] = tg.lbdns

for instance in ec2_client.instances:
if instance.state != "terminated":
report = Check_Report_AWS(self.metadata())
report.region = instance.region
report.resource_id = instance.id
report.resource_arn = instance.arn
report.resource_tags = instance.tags
report.status = "PASS"
report.status_extended = f"EC2 Instance {instance.id} is not behind a internet facing load balancer."

#if the instanceId of the public lb is the same as the instances that are active, fail
if instance.id in public_instances:
report.status = "FAIL"
report.status_extended = f"EC2 Instance {instance.id} is behind a internet facing load balancer {public_instances[instance.id]}."
findings.append(report)
return findings
9 changes: 9 additions & 0 deletions prowler/providers/aws/services/elb/elb_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ def __describe_load_balancers__(self, regional_client):
policies=listener["PolicyNames"],
)
)

instance_ids = []
for id in elb["Instances"]:
instance_ids.append(id['InstanceId'])

self.loadbalancers.append(
LoadBalancer(
name=elb["LoadBalancerName"],
Expand All @@ -45,6 +50,8 @@ def __describe_load_balancers__(self, regional_client):
region=regional_client.region,
scheme=elb["Scheme"],
listeners=listeners,
security_groups=elb["SecurityGroups"],
instances=instance_ids
)
)

Expand Down Expand Up @@ -98,3 +105,5 @@ class LoadBalancer(BaseModel):
access_logs: Optional[bool]
listeners: list[Listener]
tags: Optional[list] = []
security_groups: list[str]
instances: list[str]
50 changes: 50 additions & 0 deletions prowler/providers/aws/services/elbv2/elbv2_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ def __init__(self, audit_info):
self.loadbalancersv2 = []
self.__threading_call__(self.__describe_load_balancers__)
self.listeners = []
self.target_groups = []
self.__threading_call__(self.__describe_target_groups__)
self.__threading_call__(self.__describe_listeners__)
self.__threading_call__(self.__describe_load_balancer_attributes__)
self.__threading_call__(self.__describe_rules__)
Expand All @@ -40,6 +42,7 @@ def __describe_load_balancers__(self, regional_client):
arn=elbv2["LoadBalancerArn"],
type=elbv2["Type"],
listeners=[],
security_groups=elbv2["SecurityGroups"],
)
if "DNSName" in elbv2:
lb.dns = elbv2["DNSName"]
Expand All @@ -50,6 +53,45 @@ def __describe_load_balancers__(self, regional_client):
logger.error(
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:
for lb in self.loadbalancersv2:
try:
if lb.scheme == 'internet-facing' and lb.type == "application" and len(lb.security_groups) > 0:
describe_target_groups_paginator = regional_client.get_paginator(
"describe_target_groups"
)
for page in describe_target_groups_paginator.paginate(LoadBalancerArn=lb.arn):
for target_group in page["TargetGroups"]:
for target_health in regional_client.describe_target_health(TargetGroupArn=target_group["TargetGroupArn"])["TargetHealthDescriptions"]:
tg = TargetGroups(
name=target_group["TargetGroupName"],
arn=target_group["TargetGroupArn"],
target_type=target_group["TargetType"],
target=target_health['Target']['Id'],
)
if "DNSName" in lb:
tg.lbdns = lb.dns
self.target_groups.append(tg)
except ClientError as error:
if error.response["Error"]["Code"] == "LoadBalancerNotFound":
logger.warning(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
else:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error (
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)

def __describe_listeners__(self, regional_client):
logger.info("ELBv2 - Describing listeners...")
Expand Down Expand Up @@ -235,3 +277,11 @@ class LoadBalancerv2(BaseModel):
listeners: list[Listenerv2]
scheme: Optional[str]
tags: Optional[list] = []
security_groups: list[str]

class TargetGroups(BaseModel):
name: str
arn: str
target_type: str
target: str
lbdns: Optional[str]
Loading
Loading