Compare commits

...

7 Commits

Author SHA1 Message Date
Pepe Fagoaga
7c8727f51f fix(elbv2): Add public attribute to TargetGroups 2024-04-18 08:39:57 +02:00
Sergio
69c6d59829 solve comment 2024-04-17 18:32:04 +02:00
Sergio
da6d821cd3 review PR 2024-04-17 18:19:19 +02:00
Pepe Fagoaga
63d052915a fix: tests 2024-04-17 10:01:27 +02:00
Pepe Fagoaga
006fbf7328 chore: general fixes 2024-04-17 08:48:20 +02:00
abant07
860f1d0f93 debugging awslambda test case for public alb check 2024-04-17 08:48:15 +02:00
abant07
0b552446b4 debugging lambda test for albs 2024-04-17 08:48:07 +02:00
14 changed files with 1208 additions and 0 deletions

View File

@@ -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": "",
"Other": "",
"Terraform": ""
},
"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": ""
}

View File

@@ -0,0 +1,29 @@
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

View File

@@ -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": ""
}

View File

@@ -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.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

View File

@@ -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": ""
}

View File

@@ -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 = []
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

View File

@@ -37,6 +37,11 @@ class ELB(AWSService):
policies=listener["PolicyNames"],
)
)
instance_ids = []
for id in elb["Instances"]:
instance_ids.append(id["InstanceId"])
self.loadbalancers.append(
LoadBalancer(
name=elb["LoadBalancerName"],
@@ -45,6 +50,8 @@ class ELB(AWSService):
region=regional_client.region,
scheme=elb["Scheme"],
listeners=listeners,
security_groups=elb["SecurityGroups"],
instances=instance_ids,
)
)
@@ -98,3 +105,5 @@ class LoadBalancer(BaseModel):
access_logs: Optional[bool]
listeners: list[Listener]
tags: Optional[list] = []
security_groups: list[str]
instances: list[str]

View File

@@ -16,6 +16,8 @@ class ELBv2(AWSService):
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__)
@@ -40,6 +42,7 @@ class ELBv2(AWSService):
arn=elbv2["LoadBalancerArn"],
type=elbv2["Type"],
listeners=[],
security_groups=elbv2["SecurityGroups"],
)
if "DNSName" in elbv2:
lb.dns = elbv2["DNSName"]
@@ -51,6 +54,55 @@ class ELBv2(AWSService):
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:
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"],
public=(
True
if lb.scheme == "internet-facing"
and lb.type == "application"
and len(lb.security_groups) > 0
else False
),
)
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...")
try:
@@ -235,3 +287,13 @@ 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
public: bool
lbdns: Optional[str]

View File

@@ -0,0 +1,316 @@
from unittest import mock
from boto3 import client, resource
from mock import patch
from moto import mock_aws
from tests.providers.aws.utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_US_EAST_1,
AWS_REGION_US_EAST_1_AZA,
AWS_REGION_US_EAST_1_AZB,
set_mocked_aws_provider,
)
# Mock generate_regional_clients()
def mock_generate_regional_clients(provider, service):
regional_client = provider._session.current_session.client(
service, region_name=AWS_REGION_US_EAST_1
)
regional_client.region = AWS_REGION_US_EAST_1
return {AWS_REGION_US_EAST_1: regional_client}
# Patch every AWS call using Boto3 and generate_regional_clients to have 1 client
@patch(
"prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients",
new=mock_generate_regional_clients,
)
class Test_awslambda_function_not_directly_publicly_accessible_via_elbv2:
@mock_aws
def test_no_functions(self):
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
lambda_client = mock.MagicMock
lambda_client.functions = {}
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=set_mocked_aws_provider(),
), mock.patch(
"prowler.providers.aws.services.awslambda.awslambda_function_no_secrets_in_variables.awslambda_function_no_secrets_in_variables.awslambda_client",
new=lambda_client,
), mock.patch(
"prowler.providers.aws.services.awslambda.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_function_not_directly_publicly_accessible_via_elbv2.elbv2_client",
new=ELBv2(set_mocked_aws_provider()),
):
# Test Check
from prowler.providers.aws.services.awslambda.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_function_not_directly_publicly_accessible_via_elbv2 import (
awslambda_function_not_directly_publicly_accessible_via_elbv2,
)
check = awslambda_function_not_directly_publicly_accessible_via_elbv2()
result = check.execute()
assert len(result) == 0
@mock_aws
def test_function_without_elbv2(self):
from prowler.providers.aws.services.awslambda.awslambda_service import Function
# Lambda client
lambda_client = mock.MagicMock
function_name = "test-lambda"
function_runtime = "nodejs4.3"
function_arn = f"arn:aws:lambda:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:function/{function_name}"
lambda_client.functions = {
"function_name": Function(
name=function_name,
security_groups=[],
arn=function_arn,
region=AWS_REGION_US_EAST_1,
runtime=function_runtime,
)
}
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=set_mocked_aws_provider(),
), mock.patch(
"prowler.providers.aws.services.awslambda.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_client",
new=lambda_client,
), mock.patch(
"prowler.providers.aws.services.awslambda.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_function_not_directly_publicly_accessible_via_elbv2.elbv2_client",
new=ELBv2(set_mocked_aws_provider()),
):
# Test Check
from prowler.providers.aws.services.awslambda.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_function_not_directly_publicly_accessible_via_elbv2 import (
awslambda_function_not_directly_publicly_accessible_via_elbv2,
)
check = awslambda_function_not_directly_publicly_accessible_via_elbv2()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Lambda function {function_name} is not behind an Internet facing Load Balancer."
)
assert result[0].region == AWS_REGION_US_EAST_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_elbv2_internal(self):
from prowler.providers.aws.services.awslambda.awslambda_service import Function
# Lambda client
lambda_client = mock.MagicMock
function_name = "test-lambda"
function_runtime = "nodejs4.3"
function_arn = f"arn:aws:lambda:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:function/{function_name}"
lambda_client.functions = {
"function_name": Function(
name=function_name,
security_groups=[],
arn=function_arn,
region=AWS_REGION_US_EAST_1,
runtime=function_runtime,
)
}
# ALB Client
conn = client("elbv2", region_name=AWS_REGION_US_EAST_1)
ec2 = resource("ec2", region_name=AWS_REGION_US_EAST_1)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id,
CidrBlock="172.28.7.192/26",
AvailabilityZone=AWS_REGION_US_EAST_1_AZA,
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id,
CidrBlock="172.28.7.0/26",
AvailabilityZone=AWS_REGION_US_EAST_1_AZB,
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
target_group = conn.create_target_group(
Name="a-target",
HealthCheckEnabled=True,
HealthCheckProtocol="HTTP",
HealthCheckPath="/",
HealthCheckIntervalSeconds=35,
HealthCheckTimeoutSeconds=5,
HealthyThresholdCount=5,
UnhealthyThresholdCount=2,
TargetType="lambda",
)["TargetGroups"][0]
target_group_arn = target_group["TargetGroupArn"]
conn.register_targets(
TargetGroupArn=target_group_arn,
Targets=[
{"Id": function_arn},
],
)
conn.create_listener(
LoadBalancerArn=lb["LoadBalancerArn"],
Protocol="HTTP",
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
)
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=set_mocked_aws_provider(),
), mock.patch(
"prowler.providers.aws.services.awslambda.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_client",
new=lambda_client,
), mock.patch(
"prowler.providers.aws.services.awslambda.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_function_not_directly_publicly_accessible_via_elbv2.elbv2_client",
new=ELBv2(set_mocked_aws_provider()),
):
# Test Check
from prowler.providers.aws.services.awslambda.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_function_not_directly_publicly_accessible_via_elbv2 import (
awslambda_function_not_directly_publicly_accessible_via_elbv2,
)
check = awslambda_function_not_directly_publicly_accessible_via_elbv2()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Lambda function {function_name} is not behind an Internet facing Load Balancer."
)
assert result[0].region == AWS_REGION_US_EAST_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_elbv2_public(self):
from prowler.providers.aws.services.awslambda.awslambda_service import Function
# Lambda client
lambda_client = mock.MagicMock
function_name = "test-lambda"
function_runtime = "nodejs4.3"
function_arn = f"arn:aws:lambda:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:function/{function_name}"
lambda_client.functions = {
"function_name": Function(
name=function_name,
security_groups=[],
arn=function_arn,
region=AWS_REGION_US_EAST_1,
runtime=function_runtime,
)
}
# ALB Client
conn = client("elbv2", region_name=AWS_REGION_US_EAST_1)
ec2 = resource("ec2", region_name=AWS_REGION_US_EAST_1)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id,
CidrBlock="172.28.7.192/26",
AvailabilityZone=AWS_REGION_US_EAST_1_AZA,
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id,
CidrBlock="172.28.7.0/26",
AvailabilityZone=AWS_REGION_US_EAST_1_AZB,
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internet-facing",
Type="application",
)["LoadBalancers"][0]
target_group = conn.create_target_group(
Name="a-target",
HealthCheckEnabled=True,
HealthCheckProtocol="HTTP",
HealthCheckPath="/",
HealthCheckIntervalSeconds=35,
HealthCheckTimeoutSeconds=5,
HealthyThresholdCount=5,
UnhealthyThresholdCount=2,
TargetType="lambda",
)["TargetGroups"][0]
target_group_arn = target_group["TargetGroupArn"]
conn.register_targets(
TargetGroupArn=target_group_arn,
Targets=[
{"Id": function_arn},
],
)
conn.create_listener(
LoadBalancerArn=lb["LoadBalancerArn"],
Protocol="HTTP",
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
)
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=set_mocked_aws_provider(),
), mock.patch(
"prowler.providers.aws.services.awslambda.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_client",
new=lambda_client,
), mock.patch(
"prowler.providers.aws.services.awslambda.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_function_not_directly_publicly_accessible_via_elbv2.elbv2_client",
new=ELBv2(set_mocked_aws_provider()),
):
# Test Check
from prowler.providers.aws.services.awslambda.awslambda_function_not_directly_publicly_accessible_via_elbv2.awslambda_function_not_directly_publicly_accessible_via_elbv2 import (
awslambda_function_not_directly_publicly_accessible_via_elbv2,
)
check = awslambda_function_not_directly_publicly_accessible_via_elbv2()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Lambda function {function_name} is behind an Internet facing Load Balancer through target group {target_group_arn}."
)
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_id == function_name
assert result[0].resource_arn == function_arn
assert result[0].resource_tags == []

View File

@@ -0,0 +1,341 @@
from unittest import mock
from boto3 import client, resource
from moto import mock_aws
from tests.providers.aws.utils import (
AWS_REGION_EU_WEST_1,
AWS_REGION_EU_WEST_1_AZA,
AWS_REGION_US_EAST_1,
set_mocked_aws_provider,
)
EXAMPLE_AMI_ID = "ami-12c6146b"
LOAD_BALANCER_DNS = f"my-lb.{AWS_REGION_US_EAST_1}.elb.amazonaws.com"
class Test_ec2_instance_not_directly_publicly_accessible_via_elb:
@mock_aws
def test_ec2_no_public_elbs(self):
from prowler.providers.aws.services.ec2.ec2_service import EC2
aws_provider = set_mocked_aws_provider(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
)
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_client",
new=EC2(aws_provider),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_instance_not_directly_publicly_accessible_via_elb import (
ec2_instance_not_directly_publicly_accessible_via_elb,
)
check = ec2_instance_not_directly_publicly_accessible_via_elb()
findings = check.execute()
assert len(findings) == 0
@mock_aws
def test_ec2_behind_public_elb(self):
elb = client("elb", region_name=AWS_REGION_EU_WEST_1)
ec2 = resource("ec2", region_name=AWS_REGION_EU_WEST_1)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
],
AvailabilityZones=[AWS_REGION_EU_WEST_1_AZA],
Scheme="internet-facing",
SecurityGroups=[security_group.id],
)
security_group_instance = ec2.create_security_group(
GroupName="sg01_instance",
Description="Test security group for EC2 instance",
)
iam = client("iam", "us-west-1")
profile_name = "fake_profile"
_ = iam.create_instance_profile(
InstanceProfileName=profile_name,
)
ec2 = resource("ec2", region_name=AWS_REGION_US_EAST_1)
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
instance = ec2.create_instances(
ImageId=EXAMPLE_AMI_ID,
MinCount=1,
MaxCount=1,
IamInstanceProfile={"Name": profile_name},
NetworkInterfaces=[
{
"DeviceIndex": 0,
"SubnetId": subnet.id,
"AssociatePublicIpAddress": False,
"Groups": [
security_group_instance.id
], # Assign instance to its own security group
}
],
)[0]
# Register the instance with the load balancer
elb.register_instances_with_load_balancer(
LoadBalancerName="my-lb",
Instances=[
{"InstanceId": instance.id},
],
)
from prowler.providers.aws.services.ec2.ec2_service import EC2
from prowler.providers.aws.services.elb.elb_service import ELB
aws_provider = set_mocked_aws_provider(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
)
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_client",
new=EC2(aws_provider),
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_instance_not_directly_publicly_accessible_via_elb.elb_client",
new=ELB(aws_provider),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_instance_not_directly_publicly_accessible_via_elb import (
ec2_instance_not_directly_publicly_accessible_via_elb,
)
check = ec2_instance_not_directly_publicly_accessible_via_elb()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"EC2 Instance {instance.id} is behind an Internet facing Classic Load Balancer {LOAD_BALANCER_DNS}."
)
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_id == instance.id
assert (
result[0].resource_arn
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:instance/{instance.id}"
)
assert result[0].resource_tags is None
@mock_aws
def test_ec2_behind_internal_elb(self):
elb = client("elb", region_name=AWS_REGION_EU_WEST_1)
ec2 = resource("ec2", region_name=AWS_REGION_EU_WEST_1)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
],
AvailabilityZones=[AWS_REGION_EU_WEST_1_AZA],
Scheme="internal",
SecurityGroups=[security_group.id],
)
security_group_instance = ec2.create_security_group(
GroupName="sg01_instance",
Description="Test security group for EC2 instance",
)
iam = client("iam", "us-west-1")
profile_name = "fake_profile"
_ = iam.create_instance_profile(
InstanceProfileName=profile_name,
)
ec2 = resource("ec2", region_name=AWS_REGION_US_EAST_1)
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
instance = ec2.create_instances(
ImageId=EXAMPLE_AMI_ID,
MinCount=1,
MaxCount=1,
IamInstanceProfile={"Name": profile_name},
NetworkInterfaces=[
{
"DeviceIndex": 0,
"SubnetId": subnet.id,
"AssociatePublicIpAddress": False,
"Groups": [
security_group_instance.id
], # Assign instance to its own security group
}
],
)[0]
# Register the instance with the load balancer
elb.register_instances_with_load_balancer(
LoadBalancerName="my-lb",
Instances=[
{"InstanceId": instance.id},
],
)
from prowler.providers.aws.services.ec2.ec2_service import EC2
from prowler.providers.aws.services.elb.elb_service import ELB
aws_provider = set_mocked_aws_provider(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
)
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_client",
new=EC2(aws_provider),
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_instance_not_directly_publicly_accessible_via_elb.elb_client",
new=ELB(aws_provider),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_instance_not_directly_publicly_accessible_via_elb import (
ec2_instance_not_directly_publicly_accessible_via_elb,
)
check = ec2_instance_not_directly_publicly_accessible_via_elb()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"EC2 Instance {instance.id} is not behind an Internet facing Classic Load Balancer."
)
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_id == instance.id
assert (
result[0].resource_arn
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:instance/{instance.id}"
)
assert result[0].resource_tags is None
@mock_aws
def test_two_ec2_behind_public_elb(self):
elb = client("elb", region_name=AWS_REGION_EU_WEST_1)
ec2 = resource("ec2", region_name=AWS_REGION_EU_WEST_1)
security_group = ec2.create_security_group(
GroupName="sg01", Description="Test security group sg01"
)
elb.create_load_balancer(
LoadBalancerName="my-lb",
Listeners=[
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
],
AvailabilityZones=[AWS_REGION_EU_WEST_1_AZA],
Scheme="internet-facing",
SecurityGroups=[security_group.id],
)
security_group_instance = ec2.create_security_group(
GroupName="sg01_instance",
Description="Test security group for EC2 instance",
)
iam = client("iam", "us-west-1")
profile_name = "fake_profile"
iam.create_instance_profile(
InstanceProfileName=profile_name,
)
ec2 = resource("ec2", region_name=AWS_REGION_US_EAST_1)
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
# Create two EC2 instances
instances = ec2.create_instances(
ImageId=EXAMPLE_AMI_ID,
MinCount=2,
MaxCount=2,
IamInstanceProfile={"Name": profile_name},
NetworkInterfaces=[
{
"DeviceIndex": 0,
"SubnetId": subnet.id,
"AssociatePublicIpAddress": False,
"Groups": [
security_group_instance.id
], # Assign instance to its own security group
}
],
)
# Register the instances with the load balancer
elb.register_instances_with_load_balancer(
LoadBalancerName="my-lb",
Instances=[{"InstanceId": instance.id} for instance in instances],
)
from prowler.providers.aws.services.ec2.ec2_service import EC2
from prowler.providers.aws.services.elb.elb_service import ELB
aws_provider = set_mocked_aws_provider(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
)
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_client",
new=EC2(aws_provider),
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_instance_not_directly_publicly_accessible_via_elb.elb_client",
new=ELB(aws_provider),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elb.ec2_instance_not_directly_publicly_accessible_via_elb import (
ec2_instance_not_directly_publicly_accessible_via_elb,
)
check = ec2_instance_not_directly_publicly_accessible_via_elb()
result = check.execute()
assert len(result) == 2
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"EC2 Instance {instances[0].id} is behind an Internet facing Classic Load Balancer {LOAD_BALANCER_DNS}."
)
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_id == instances[0].id
assert (
result[0].resource_arn
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:instance/{instances[0].id}"
)
assert result[0].resource_tags is None
assert result[1].status == "FAIL"
assert (
result[1].status_extended
== f"EC2 Instance {instances[1].id} is behind an Internet facing Classic Load Balancer {LOAD_BALANCER_DNS}."
)
assert result[1].region == AWS_REGION_US_EAST_1
assert result[1].resource_id == instances[1].id
assert (
result[1].resource_arn
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:instance/{instances[1].id}"
)
assert result[1].resource_tags is None

View File

@@ -0,0 +1,291 @@
from unittest import mock
from boto3 import client, resource
from moto import mock_aws
from tests.providers.aws.utils import (
AWS_REGION_EU_WEST_1,
AWS_REGION_EU_WEST_1_AZA,
AWS_REGION_EU_WEST_1_AZB,
AWS_REGION_US_EAST_1,
set_mocked_aws_provider,
)
EXAMPLE_AMI_ID = "ami-12c6146b"
class Test_ec2_instance_not_directly_publicly_accessible_via_elbv2:
@mock_aws
def test_no_elbsv2(self):
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
aws_provider = set_mocked_aws_provider(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
)
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.elbv2.elbv2_insecure_ssl_ciphers.elbv2_insecure_ssl_ciphers.elbv2_client",
new=ELBv2(aws_provider),
):
# Test Check
from prowler.providers.aws.services.elbv2.elbv2_insecure_ssl_ciphers.elbv2_insecure_ssl_ciphers import (
elbv2_insecure_ssl_ciphers,
)
check = elbv2_insecure_ssl_ciphers()
result = check.execute()
assert len(result) == 0
@mock_aws
def test_ec2_behind_public_elbv2(self):
conn = client("elbv2", region_name=AWS_REGION_EU_WEST_1)
ec2 = resource("ec2", region_name=AWS_REGION_EU_WEST_1)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id,
CidrBlock="172.28.7.192/26",
AvailabilityZone=AWS_REGION_EU_WEST_1_AZA,
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id,
CidrBlock="172.28.7.0/26",
AvailabilityZone=AWS_REGION_EU_WEST_1_AZB,
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internet-facing",
Type="application",
)["LoadBalancers"][0]
target_group = conn.create_target_group(
Name="a-target",
Protocol="HTTP",
Port=80,
VpcId=vpc.id,
HealthCheckProtocol="HTTP",
HealthCheckPath="/",
HealthCheckIntervalSeconds=30,
HealthCheckTimeoutSeconds=5,
HealthyThresholdCount=5,
UnhealthyThresholdCount=2,
TargetType="instance",
)["TargetGroups"][0]
target_group_arn = target_group["TargetGroupArn"]
security_group_instance = ec2.create_security_group(
GroupName="sg01_instance",
Description="Test security group for EC2 instance",
)
iam = client("iam", "us-west-1")
profile_name = "fake_profile"
iam.create_instance_profile(
InstanceProfileName=profile_name,
)
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
instance = ec2.create_instances(
ImageId=EXAMPLE_AMI_ID,
MinCount=1,
MaxCount=1,
IamInstanceProfile={"Name": profile_name},
NetworkInterfaces=[
{
"DeviceIndex": 0,
"SubnetId": subnet.id,
"AssociatePublicIpAddress": False,
"Groups": [security_group_instance.id],
}
],
)[0]
conn.register_targets(
TargetGroupArn=target_group_arn,
Targets=[
{"Id": instance.id},
],
)
conn.create_listener(
LoadBalancerArn=lb["LoadBalancerArn"],
Protocol="HTTP",
Port=80,
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
)
from prowler.providers.aws.services.ec2.ec2_service import EC2
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
aws_provider = set_mocked_aws_provider(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
)
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elbv2.ec2_instance_not_directly_publicly_accessible_via_elbv2.ec2_client",
new=EC2(aws_provider),
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elbv2.ec2_instance_not_directly_publicly_accessible_via_elbv2.elbv2_client",
new=ELBv2(aws_provider),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elbv2.ec2_instance_not_directly_publicly_accessible_via_elbv2 import (
ec2_instance_not_directly_publicly_accessible_via_elbv2,
)
check = ec2_instance_not_directly_publicly_accessible_via_elbv2()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"EC2 Instance {instance.id} is behind an Internet facing Load Balancer through target group {target_group_arn}."
)
assert result[0].region == AWS_REGION_EU_WEST_1
assert result[0].resource_id == instance.id
assert (
result[0].resource_arn
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_EU_WEST_1}:{aws_provider.identity.account}:instance/{instance.id}"
)
assert result[0].resource_tags is None
@mock_aws
def test_ec2_behind_private_elbv2(self):
conn = client("elbv2", region_name=AWS_REGION_EU_WEST_1)
ec2 = resource("ec2", region_name=AWS_REGION_EU_WEST_1)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id,
CidrBlock="172.28.7.192/26",
AvailabilityZone=AWS_REGION_EU_WEST_1_AZA,
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id,
CidrBlock="172.28.7.0/26",
AvailabilityZone=AWS_REGION_EU_WEST_1_AZB,
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
target_group = conn.create_target_group(
Name="a-target",
Protocol="HTTP",
Port=80,
VpcId=vpc.id,
HealthCheckProtocol="HTTP",
HealthCheckPath="/",
HealthCheckIntervalSeconds=30,
HealthCheckTimeoutSeconds=5,
HealthyThresholdCount=5,
UnhealthyThresholdCount=2,
TargetType="instance",
)["TargetGroups"][0]
target_group_arn = target_group["TargetGroupArn"]
security_group_instance = ec2.create_security_group(
GroupName="sg01_instance",
Description="Test security group for EC2 instance",
)
iam = client("iam", "us-west-1")
profile_name = "fake_profile"
iam.create_instance_profile(
InstanceProfileName=profile_name,
)
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
instance = ec2.create_instances(
ImageId=EXAMPLE_AMI_ID,
MinCount=1,
MaxCount=1,
IamInstanceProfile={"Name": profile_name},
NetworkInterfaces=[
{
"DeviceIndex": 0,
"SubnetId": subnet.id,
"AssociatePublicIpAddress": False,
"Groups": [security_group_instance.id],
}
],
)[0]
conn.register_targets(
TargetGroupArn=target_group_arn,
Targets=[
{"Id": instance.id},
],
)
conn.create_listener(
LoadBalancerArn=lb["LoadBalancerArn"],
Protocol="HTTP",
Port=80,
DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
)
from prowler.providers.aws.services.ec2.ec2_service import EC2
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
aws_provider = set_mocked_aws_provider(
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
)
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elbv2.ec2_instance_not_directly_publicly_accessible_via_elbv2.ec2_client",
new=EC2(aws_provider),
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elbv2.ec2_instance_not_directly_publicly_accessible_via_elbv2.elbv2_client",
new=ELBv2(aws_provider),
):
# Test Check
from prowler.providers.aws.services.ec2.ec2_instance_not_directly_publicly_accessible_via_elbv2.ec2_instance_not_directly_publicly_accessible_via_elbv2 import (
ec2_instance_not_directly_publicly_accessible_via_elbv2,
)
check = ec2_instance_not_directly_publicly_accessible_via_elbv2()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"EC2 Instance {instance.id} is not behind an Internet facing Load Balancer."
)
assert result[0].region == AWS_REGION_EU_WEST_1
assert result[0].resource_id == instance.id
assert (
result[0].resource_arn
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_EU_WEST_1}:{aws_provider.identity.account}:instance/{instance.id}"
)
assert result[0].resource_tags is None