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(internet-exposed): Improve publicly accessible checks to include targets of ELBs #3920

Closed
wants to merge 11 commits into from
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"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,31 @@
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 = []

if awslambda_client.functions:
public_lambda_functions = {}
for target_group in elbv2_client.target_groups:
if target_group.public and target_group.target_type == "lambda":
public_lambda_functions[target_group.target] = target_group.arn


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 an Internet facing Load Balancer."

if function.arn in public_lambda_functions:
report.status = "FAIL"
report.status_extended = f"Lambda function {function.name} is behind an Internet facing Load Balancer through target group {public_lambda_functions[function.arn]}."
findings.append(report)

return findings
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"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,33 @@
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 = []

if ec2_client.instances:
public_instances = {}
for lb in elb_client.loadbalancers:
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 an Internet facing Classic Load Balancer."

if instance.id in public_instances:
report.status = "FAIL"
report.status_extended = f"EC2 Instance {instance.id} is behind an 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,31 @@
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 = []

if ec2_client.instances:
public_instances = {}
for tg in elbv2_client.target_groups:
if tg.public and tg.target_type == "instance":
public_instances[tg.target] = tg.arn

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 an Internet facing Load Balancer."

if instance.id in public_instances:
report.status = "FAIL"
report.status_extended = f"EC2 Instance {instance.id} is behind an Internet facing Load Balancer through target group {public_instances[instance.id]}."
findings.append(report)

return findings
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "ecs_container_not_directly_publicly_accessible_via_elbv2",
"CheckTitle": "Check for ECS containers behind internet facing ALB/NLB/GLB.",
"CheckType": [
"Infrastructure Security"
],
"ServiceName": "ecs",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "critical",
"ResourceType": "AwsEcsService",
"Description": "Check for ECS container behind internet facing ALB/NLB/GLB.",
"Risk": "Exposing an ECS container 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-facing"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ecs.ecs_client import ecs_client
from prowler.providers.aws.services.elbv2.elbv2_client import elbv2_client


class ecs_container_not_directly_publicly_accessible_via_elbv2(Check):
def execute(self):
findings = []

if ecs_client.containers:
public_containers = {}
for tg in elbv2_client.target_groups:
if tg.public and tg.target_type == "ip":
public_containers[tg.target] = tg.arn

for container in ecs_client.containers:
report = Check_Report_AWS(self.metadata())
report.resource_arn = container.arn
report.resource_tags = container.tags
report.status = "PASS"
report.status_extended = f"ECS Container {container.arn} is not behind an Internet facing Load Balancer."

# if the container private ip of the public lb is the same as the instances that are active, fail
if container.ipv4 in public_containers:
report.status = "FAIL"
report.status_extended = f"ECS Container {container.arn} is behind an Internet facing Load Balancer through target group {public_containers[container.ipv4]}."
elif container.ipv6 in public_containers:
report.status = "FAIL"
report.status_extended = f"ECS Container {container.arn} is behind an Internet facing Load Balancer through target group {public_containers[container.ipv6]}."
findings.append(report)
return findings
41 changes: 41 additions & 0 deletions prowler/providers/aws/services/ecs/ecs_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ def __init__(self, provider):
# Call AWSService's __init__
super().__init__(__class__.__name__, provider)
self.task_definitions = []
self.containers = []
self.__threading_call__(self.__list_task_definitions__)
self.__threading_call__(self.__describe_container_instances__)
self.__describe_task_definition__()

def __list_task_definitions__(self, regional_client):
Expand Down Expand Up @@ -72,6 +74,38 @@ def __describe_task_definition__(self):
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)

def __describe_container_instances__(self, regional_client):
logger.info("ECS - Describing Container Instances...")
try:
for container in regional_client.describe_container_instances()["containerInstances"]:
if not self.audit_resources or (
is_resource_filtered(container, self.audit_resources)
):
cont = Containers(
arn=container["containerInstanceArn"],
tags=container["tags"],
)
for attachment in container["attachments"]:
if attachment["type"] == "ElasticNetworkInterface":
for detail in attachment["details"]:
if detail["name"] == "networkInterfaceId":
for eni in regional_client.describe_network_interfaces(NetworkInterfaceIds=detail["value"])["NetworkInterfaces"]:
cont.availability_zone = eni["AvailabilityZone"]
for ipv6 in eni["Ipv6Addresses"]:
if ipv6["Primary"]:
cont.ipv6 = ipv6["Ipv6Addresses"]
break
for ipv4 in eni["PrivateIpAddresses"]:
if ipv4["Primary"]:
cont.ipv4 = ipv4["PrivateIpAddress"]
break

self.containers.append(cont)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)


class ContainerEnvVariable(BaseModel):
name: str
Expand All @@ -86,3 +120,10 @@ class TaskDefinition(BaseModel):
environment_variables: list[ContainerEnvVariable]
tags: Optional[list] = []
network_mode: Optional[str]

class Containers(BaseModel):
arn: str
availability_zone: str
ipv6: Optional[str]
ipv4: Optional[str]
tags: Optional[list] = []
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]
Loading