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

chore(awslambda): Enhance function public access check called from other resource #4679

Merged
merged 22 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
38912d1
feat(elbv2): Add SGs attribute
puchy22 Aug 7, 2024
cd0b0c4
test(elbv2): Add new tests for target groups attributes
puchy22 Aug 7, 2024
6c420fd
chore(awslabmda): Remove unnecesary class header
puchy22 Aug 7, 2024
d1fdbe8
chore(awslabmda): Change to have in count that is really public Inter…
puchy22 Aug 7, 2024
4e5a5f7
test(awslambda): Adapt new tests for case of ALBs
puchy22 Aug 7, 2024
b30d260
test(awslambda): Mock ELBv2 service properly
puchy22 Aug 7, 2024
08c554f
fix(awslambda): Fix arbitrary position in sanitized URL
puchy22 Aug 7, 2024
d50c5c2
feat(awslabmda): Add SecurityGroups to ELBv2
puchy22 Aug 7, 2024
234344f
chore(ec2): Change SGs to dict and respective for loops
puchy22 Aug 7, 2024
dcdec71
fix(ec2): Fix loops iterations
puchy22 Aug 7, 2024
901b7e3
fix(ec2): Fix loops iterations for all affected checks
puchy22 Aug 8, 2024
52cff6c
Merge branch 'PRWLR-4428-change-security-groups-to-dict' into PRWLR-4…
puchy22 Aug 8, 2024
0e27982
feat(awslambda): Verificate ABL security group is public
puchy22 Aug 8, 2024
878c74e
test(awslambda): Add test case for private segurity group
puchy22 Aug 8, 2024
0080ffc
Merge branch 'master' into PRWLR-4354-enhance-aws-lambda-public-acces…
puchy22 Aug 12, 2024
1dee21b
chore(awslambda): Revert changes
puchy22 Aug 13, 2024
c4bc003
feat(awslabmda): Add other resources comprobations that can invoke th…
puchy22 Aug 13, 2024
1f4b6d7
feat(awslambda): Verificate that other services can invoke function
puchy22 Aug 13, 2024
1cdb1e9
chore(awslambda): Add note about false PASS case
puchy22 Aug 13, 2024
9a445d4
test(awslambda): Add test case for ALB expose case
puchy22 Aug 13, 2024
f8f77d5
check all AWS services
MrCloudSec Aug 16, 2024
bdb9d77
enhance check's metadata
MrCloudSec Aug 16, 2024
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
@@ -1,14 +1,14 @@
{
"Provider": "aws",
"CheckID": "awslambda_function_not_publicly_accessible",
"CheckTitle": "Check if Lambda functions have resource-based policy set as Public.",
"CheckTitle": "Check if Lambda functions is publicly accessible through Internet",
"CheckType": [],
"ServiceName": "lambda",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:lambda:region:account-id:function/function-name",
"Severity": "critical",
"ResourceType": "AwsLambdaFunction",
"Description": "Check if Lambda functions have resource-based policy set as Public.",
"Description": "Check if Lambda functions is publicly accessible through Internet via ALB and function's permission.",
"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": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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_publicly_accessible(Check):
Expand All @@ -13,30 +14,68 @@
report.resource_tags = function.tags

report.status = "PASS"
report.status_extended = f"Lambda function {function.name} has a policy resource-based policy not public."
report.status_extended = (
f"Lambda function {function.name} is not publicly accessible."
)

public_access = False
if function.policy:
# 1. Check if the function is associated with a public load balancer
target_group_redirects_alb_to_function = ""

for tg_arn, target_group in elbv2_client.target_groups.items():
if (
target_group.target_type == "lambda"
) and function.arn in target_group.targets:
for lb_arn in target_group.load_balancer_arns:
lb = elbv2_client.loadbalancersv2.get(
lb_arn,
None, # Bug: If prowler is filtering by ARN, this will fail because loadbalancersv2 are filtered
)
if lb and lb.scheme == "internet-facing":
target_group_redirects_alb_to_function = tg_arn
break

public_policy = False

# 2. Check if the function policy allows public access
if target_group_redirects_alb_to_function and function.policy:
for statement in function.policy["Statement"]:
# Only check allow statements
if statement["Effect"] == "Allow":
if (
if (
statement["Effect"] == "Allow"
and (
"*" in statement["Principal"]
or (
"AWS" in statement["Principal"]
and "*" in statement["Principal"]["AWS"]
)
or (
"CanonicalUser" in statement["Principal"]
and "*" in statement["Principal"]["CanonicalUser"]
)
):
public_access = True
or "*" in statement["Principal"].get("AWS", "")
or "*" in statement["Principal"].get("CanonicalUser", "")
or "elasticloadbalancing.amazonaws.com"
in statement["Principal"].get("Service", "")
Fixed Show fixed Hide fixed
)
and (
"*" in statement["Action"]
or "lambda:InvokeFunction" in statement["Action"]
)
and (
"*" in statement["Resource"]
or function.arn in statement["Resource"]
)
):
if statement.get("Condition", {}):
if (
statement["Condition"]
.get("ArnLike", {})
.get("AWS:SourceArn", "")
== target_group_redirects_alb_to_function
):
public_policy = True
break
else:
public_policy = True
break

if public_access:
if public_policy:
report.status = "FAIL"
report.status_extended = f"Lambda function {function.name} has a policy resource-based policy with public access."
report.status_extended = (
f"Lambda function {function.name} is publicly accessible."
)

findings.append(report)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from prowler.providers.aws.lib.service.service import AWSService


################## Lambda
class Lambda(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
Expand Down
42 changes: 42 additions & 0 deletions prowler/providers/aws/services/elbv2/elbv2_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def __init__(self, provider):
],
)
self.__threading_call__(self._describe_tags, self.loadbalancersv2.items())
self.target_groups = {}
self.__threading_call__(self._describe_target_groups)

def _describe_load_balancers(self, regional_client):
logger.info("ELBv2 - Describing load balancers...")
Expand Down Expand Up @@ -174,6 +176,35 @@ def _describe_tags(self, load_balancer):
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:
describe_target_groups_paginator = regional_client.get_paginator(
"describe_target_groups"
)
for page in describe_target_groups_paginator.paginate():
for target_group in page["TargetGroups"]:
targets = {
(tg["Target"]["Id"], tg["Target"].get("Port", 0))
for tg in regional_client.describe_target_health(
TargetGroupArn=target_group["TargetGroupArn"]
)["TargetHealthDescriptions"]
}

self.target_groups[target_group["TargetGroupArn"]] = TargetGroup(
name=target_group.get("TargetGroupName", ""),
protocol=target_group.get("Protocol", ""),
port=target_group.get("Port", 0),
vpc_id=target_group.get("VpcId", ""),
target_type=target_group.get("TargetType", ""),
targets=dict(targets),
load_balancer_arns=target_group.get("LoadBalancerArns", []),
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)


class ListenerRule(BaseModel):
arn: str
Expand Down Expand Up @@ -201,3 +232,14 @@ class LoadBalancerv2(BaseModel):
listeners: Dict[str, Listenerv2] = {}
scheme: Optional[str]
tags: Optional[list] = []


class TargetGroup(BaseModel):
name: str
protocol: str
port: int
vpc_id: str
target_type: str
# Key: ID, Value: Port (only available for IP targets)
targets: Dict[str, int] = []
load_balancer_arns: list[str]
Loading
Loading