Skip to content

Commit

Permalink
Improve publicly accessible checks to include targets of ELBs prowler…
Browse files Browse the repository at this point in the history
…-cloud#3237

Wrote checks for EC2, Lambda, and ECS to make sure they are not behind any public facing ALBs and ELBs in VPCs that have no security groups
  • Loading branch information
abant07 committed May 3, 2024
1 parent 6ceb2c1 commit 25e2b70
Show file tree
Hide file tree
Showing 19 changed files with 1,570 additions and 0 deletions.
@@ -0,0 +1,32 @@
{
"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": ""
}
@@ -0,0 +1,28 @@
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_functions = {}

for target_group in elbv2_client.target_groups:
if target_group.target_type == "lambda":
public_lambda_functions[target_group.target] = target_group.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_functions:
report.status = "FAIL"
report.status_extended = f"Lambda function {function.name} is behind a internet facing load balancer {function.arn}."
findings.append(report)
return findings
@@ -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": ""
}
@@ -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.elb.elb_client import elb_client


class ec2_instance_not_directly_publicly_accessible_via_elb(Check):
def execute(self):
findings = []
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 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
@@ -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": ""
}
@@ -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 = {}

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
@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "ecs_container_not_directly_publicly_accessible_via_elbv2",
"CheckTitle": "Check for internet facing ALBs in front of a ECS container",
"CheckType": [
"Data Protection"
],
"ServiceName": "ecs",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "critical",
"ResourceType": "AwsEcsService",
"Description": "Check if the load balancer in front of the ECS container is public, and if so, check if it has security groups to have a firewall",
"Risk": "Having your ECS containers public with no security groups are prone to be comprimised",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Place security groups on your ECS containers",
"Url": ""
}
},
"Categories": [
"internet-facing"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}

@@ -0,0 +1,30 @@
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 = []
public_instances = {}

for tg in elbv2_client.target_groups:
if tg.target_type == "ip":
public_instances[tg.target] = tg.lbdns

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 any 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_instances:
report.status = "FAIL"
report.status_extended = f"ECS container {container.arn} is behind a internet facing load balancer {public_instances[container.ipv4]}."
elif container.ipv6 in public_instances:
report.status = "FAIL"
report.status_extended = f"ECS container {container.arn} is behind a internet facing load balancer {public_instances[container.ipv6]}."
findings.append(report)
return findings
41 changes: 41 additions & 0 deletions prowler/providers/aws/services/ecs/ecs_service.py
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
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]

0 comments on commit 25e2b70

Please sign in to comment.