Skip to content

Commit

Permalink
feat(ecs): add new check ecs_service_fargate_latest_platform_version (
Browse files Browse the repository at this point in the history
#5258)

Co-authored-by: Sergio <[email protected]>
  • Loading branch information
MarioRgzLpz and sergargar authored Oct 2, 2024
1 parent 158263a commit 2ffe7f3
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 1 deletion.
5 changes: 5 additions & 0 deletions prowler/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ aws:
8088,
]

# AWS ECS Configuration
# aws.ecs_service_fargate_latest_platform_version
fargate_linux_latest_version: "1.4.0"
fargate_windows_latest_version: "1.0.0"

# AWS VPC Configuration (vpc_endpoint_connections_trust_boundaries, vpc_endpoint_services_allowed_principals_trust_boundaries)
# AWS SSM Configuration (aws.ssm_documents_set_as_public)
# Single account environment: No action required. The AWS account number will be automatically added by the checks.
Expand Down
6 changes: 6 additions & 0 deletions prowler/providers/aws/services/ecs/ecs_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ def _describe_services(self, cluster):
.get("assignPublicIp", "DISABLED")
== "ENABLED"
),
launch_type=service_desc.get("launchType", ""),
platform_version=service_desc.get("platformVersion", ""),
platform_family=service_desc.get("platformFamily", ""),
tags=service_desc.get("tags", []),
)
cluster.services[service_arn] = service_obj
Expand Down Expand Up @@ -195,6 +198,9 @@ class Service(BaseModel):
name: str
arn: str
region: str
launch_type: str = ""
platform_version: Optional[str]
platform_family: Optional[str]
assign_public_ip: Optional[bool]
tags: Optional[list] = []

Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "ecs_service_fargate_latest_platform_version",
"CheckTitle": "ECS Fargate services should run on the latest Fargate platform version",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "ecs",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:service/{service-name}",
"Severity": "medium",
"ResourceType": "AwsEcsService",
"Description": "This control checks if Amazon ECS Fargate services are running the latest Fargate platform version. The control fails if the platform version is not the latest.",
"Risk": "Not running the latest Fargate platform version may expose your services to security vulnerabilities and bugs that are resolved in newer versions.",
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecs-fargate-latest-platform-version.html",
"Remediation": {
"Code": {
"CLI": "aws ecs update-service --cluster <cluster-name> --service <service-name> --platform-version LATEST",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-10",
"Terraform": ""
},
"Recommendation": {
"Text": "Update your ECS Fargate services to the latest platform version to ensure they are running in a secure and optimized environment.",
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html"
}
},
"Categories": [
"vulnerabilities"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ecs.ecs_client import ecs_client


class ecs_service_fargate_latest_platform_version(Check):
def execute(self):
findings = []
for service in ecs_client.services.values():
if service.launch_type == "FARGATE":
report = Check_Report_AWS(self.metadata())
report.region = service.region
report.resource_id = service.name
report.resource_arn = service.arn
report.resource_tags = service.tags
fargate_latest_linux_version = ecs_client.audit_config.get(
"fargate_linux_latest_version", "1.4.0"
)
fargate_latest_windows_version = ecs_client.audit_config.get(
"fargate_windows_latest_version", "1.0.0"
)
report.status = "PASS"
report.status_extended = f"ECS Service {service.name} is using latest FARGATE {service.platform_family} version {fargate_latest_linux_version if service.platform_family == 'Linux' else fargate_latest_windows_version}."
if (
service.platform_version != "LATEST"
and (
service.platform_family == "Linux"
and service.platform_version != fargate_latest_linux_version
)
or (
service.platform_family == "Windows"
and service.platform_version != fargate_latest_windows_version
)
):
report.status = "FAIL"
report.status_extended = f"ECS Service {service.name} is not using latest FARGATE {service.platform_family} version {fargate_latest_linux_version if service.platform_family == 'Linux' else fargate_latest_windows_version}, currently using {service.platform_version}."

findings.append(report)
return findings
2 changes: 2 additions & 0 deletions tests/config/config_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ def mock_prowler_get_latest_release(_, **kwargs):
8080,
8088,
],
"fargate_linux_latest_version": "1.4.0",
"fargate_windows_latest_version": "1.0.0",
"trusted_account_ids": [],
"log_group_retention_days": 365,
"max_idle_disconnect_timeout_in_seconds": 600,
Expand Down
5 changes: 5 additions & 0 deletions tests/config/fixtures/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ aws:
8088,
]

# AWS ECS Configuration
# aws.ecs_service_fargate_latest_platform_version
fargate_linux_latest_version: "1.4.0"
fargate_windows_latest_version: "1.0.0"

# AWS VPC Configuration (vpc_endpoint_connections_trust_boundaries, vpc_endpoint_services_allowed_principals_trust_boundaries)
# AWS SSM Configuration (aws.ssm_documents_set_as_public)
# Single account environment: No action required. The AWS account number will be automatically added by the checks.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
from unittest import mock

from prowler.providers.aws.services.ecs.ecs_service import Service
from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER, AWS_REGION_US_EAST_1

SERVICE_ARN = (
f"arn:aws:ecs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:service/sample-service"
)
SERVICE_NAME = "sample-service"


class Test_ecs_service_fargate_latest_platform_version:
def test_no_services(self):
ecs_client = mock.MagicMock
ecs_client.services = {}

with mock.patch(
"prowler.providers.aws.services.ecs.ecs_service.ECS",
ecs_client,
):
from prowler.providers.aws.services.ecs.ecs_service_fargate_latest_platform_version.ecs_service_fargate_latest_platform_version import (
ecs_service_fargate_latest_platform_version,
)

check = ecs_service_fargate_latest_platform_version()
result = check.execute()
assert len(result) == 0

def test_service_ec2_type(self):
ecs_client = mock.MagicMock
ecs_client.services = {}
ecs_client.services[SERVICE_ARN] = Service(
name=SERVICE_NAME,
arn=SERVICE_ARN,
region=AWS_REGION_US_EAST_1,
launch_type="EC2",
assign_public_ip=False,
tags=[],
)

with mock.patch(
"prowler.providers.aws.services.ecs.ecs_service.ECS",
ecs_client,
):
from prowler.providers.aws.services.ecs.ecs_service_fargate_latest_platform_version.ecs_service_fargate_latest_platform_version import (
ecs_service_fargate_latest_platform_version,
)

check = ecs_service_fargate_latest_platform_version()
result = check.execute()
assert len(result) == 0

def test_service_linux_latest_version(self):
ecs_client = mock.MagicMock
ecs_client.services = {}
ecs_client.services[SERVICE_ARN] = Service(
name=SERVICE_NAME,
arn=SERVICE_ARN,
region=AWS_REGION_US_EAST_1,
launch_type="FARGATE",
platform_family="Linux",
platform_version="1.4.0",
assign_public_ip=False,
tags=[],
)

ecs_client.audit_config = {
"fargate_linux_latest_version": "1.4.0",
}

with mock.patch(
"prowler.providers.aws.services.ecs.ecs_service.ECS",
ecs_client,
):
from prowler.providers.aws.services.ecs.ecs_service_fargate_latest_platform_version.ecs_service_fargate_latest_platform_version import (
ecs_service_fargate_latest_platform_version,
)

check = ecs_service_fargate_latest_platform_version()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].status_extended == (
f"ECS Service {SERVICE_NAME} is using latest FARGATE Linux version 1.4.0."
)
assert result[0].resource_id == SERVICE_NAME
assert result[0].resource_arn == SERVICE_ARN

def test_service_windows_latest_version(self):
ecs_client = mock.MagicMock
ecs_client.services = {}
ecs_client.services[SERVICE_ARN] = Service(
name=SERVICE_NAME,
arn=SERVICE_ARN,
region=AWS_REGION_US_EAST_1,
launch_type="FARGATE",
platform_family="Windows",
platform_version="1.0.0",
assign_public_ip=False,
tags=[],
)

ecs_client.audit_config = {
"fargate_windows_latest_version": "1.0.0",
}

with mock.patch(
"prowler.providers.aws.services.ecs.ecs_service.ECS",
ecs_client,
):
from prowler.providers.aws.services.ecs.ecs_service_fargate_latest_platform_version.ecs_service_fargate_latest_platform_version import (
ecs_service_fargate_latest_platform_version,
)

check = ecs_service_fargate_latest_platform_version()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].status_extended == (
f"ECS Service {SERVICE_NAME} is using latest FARGATE Windows version 1.0.0."
)
assert result[0].resource_id == SERVICE_NAME
assert result[0].resource_arn == SERVICE_ARN

def test_service_linux_no_latest_version(self):
ecs_client = mock.MagicMock
ecs_client.services = {}
ecs_client.services[SERVICE_ARN] = Service(
name=SERVICE_NAME,
arn=SERVICE_ARN,
region=AWS_REGION_US_EAST_1,
launch_type="FARGATE",
platform_family="Linux",
platform_version="1.2.0",
assign_public_ip=False,
tags=[],
)

ecs_client.audit_config = {
"fargate_linux_latest_version": "1.4.0",
}

with mock.patch(
"prowler.providers.aws.services.ecs.ecs_service.ECS",
ecs_client,
):
from prowler.providers.aws.services.ecs.ecs_service_fargate_latest_platform_version.ecs_service_fargate_latest_platform_version import (
ecs_service_fargate_latest_platform_version,
)

check = ecs_service_fargate_latest_platform_version()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].status_extended == (
f"ECS Service {SERVICE_NAME} is not using latest FARGATE Linux version 1.4.0, currently using 1.2.0."
)
assert result[0].resource_id == SERVICE_NAME
assert result[0].resource_arn == SERVICE_ARN

def test_service_windows_no_latest_version(self):
ecs_client = mock.MagicMock
ecs_client.services = {}
ecs_client.services[SERVICE_ARN] = Service(
name=SERVICE_NAME,
arn=SERVICE_ARN,
region=AWS_REGION_US_EAST_1,
launch_type="FARGATE",
platform_family="Windows",
platform_version="0.9.0",
assign_public_ip=False,
tags=[],
)

ecs_client.audit_config = {
"fargate_windows_latest_version": "1.0.0",
}

with mock.patch(
"prowler.providers.aws.services.ecs.ecs_service.ECS",
ecs_client,
):
from prowler.providers.aws.services.ecs.ecs_service_fargate_latest_platform_version.ecs_service_fargate_latest_platform_version import (
ecs_service_fargate_latest_platform_version,
)

check = ecs_service_fargate_latest_platform_version()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].status_extended == (
f"ECS Service {SERVICE_NAME} is not using latest FARGATE Windows version 1.0.0, currently using 0.9.0."
)
assert result[0].resource_id == SERVICE_NAME
assert result[0].resource_arn == SERVICE_ARN
7 changes: 6 additions & 1 deletion tests/providers/aws/services/ecs/ecs_service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,16 @@ def mock_make_api_call(self, operation_name, kwarg):
{
"serviceArn": "arn:aws:ecs:eu-west-1:123456789012:service/test_cluster_1/test_ecs_service",
"serviceName": "test_ecs_service",
"launchType": "EC2",
"networkConfiguration": {
"awsvpcConfiguration": {
"subnets": ["subnet-12345678"],
"securityGroups": ["sg-12345678"],
"assignPublicIp": "ENABLED",
}
},
"launchType": "FARGATE",
"platformVersion": "1.4.0",
"platformFamily": "Linux",
}
]
}
Expand Down Expand Up @@ -218,3 +220,6 @@ def test_describe_services(self):
assert ecs.services[service_arn].region == AWS_REGION_EU_WEST_1
assert ecs.services[service_arn].assign_public_ip
assert ecs.services[service_arn].tags == []
assert ecs.services[service_arn].launch_type == "FARGATE"
assert ecs.services[service_arn].platform_version == "1.4.0"
assert ecs.services[service_arn].platform_family == "Linux"

0 comments on commit 2ffe7f3

Please sign in to comment.