mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 04:21:52 +00:00
feat(ecs): Ensure ECS task definitions host's process namespace is not shared (#5146)
Co-authored-by: Sergio <sergio@prowler.com>
This commit is contained in:
committed by
GitHub
parent
ff05ce4da1
commit
9d4fa55c13
@@ -77,8 +77,11 @@ class ECS(AWSService):
|
||||
environment=environment,
|
||||
)
|
||||
)
|
||||
task_definition.pid_mode = response["taskDefinition"].get("pidMode", "")
|
||||
task_definition.tags = response.get("tags")
|
||||
task_definition.network_mode = response["taskDefinition"].get("networkMode")
|
||||
task_definition.network_mode = response["taskDefinition"].get(
|
||||
"networkMode", "bridge"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
@@ -178,6 +181,7 @@ class TaskDefinition(BaseModel):
|
||||
revision: str
|
||||
region: str
|
||||
container_definitions: list[ContainerDefinition] = []
|
||||
pid_mode: Optional[str]
|
||||
tags: Optional[list] = []
|
||||
network_mode: Optional[str]
|
||||
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ecs_task_definitions_host_namespace_not_shared",
|
||||
"CheckTitle": "ECS task definitions should not share the host's process namespace",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices"
|
||||
],
|
||||
"ServiceName": "ecs",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:task-definition/{task-definition-name}",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsEcsTaskDefinition",
|
||||
"Description": "This control checks if Amazon ECS task definitions are configured to share a host's process namespace with its containers. The control fails if the task definition shares the host's process namespace.",
|
||||
"Risk": "Sharing the host's process namespace with containers exposes host processes, potentially allowing unauthorized access or manipulation of host-level processes, undermining container isolation.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecs-task-definition-pid-mode-check.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ecs register-task-definition --family <task-family> --container-definitions '[{\"name\":\"<container-name>\",\"image\":\"<image>\",\"pidMode\":\"task\"}]'",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-3",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Avoid sharing the host's process namespace with containers to maintain process isolation and enhance security.",
|
||||
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/userguide/task_definition_parameters.html#container_definition_pid_mode"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"vulnerabilities"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.providers.aws.services.ecs.ecs_client import ecs_client
|
||||
|
||||
|
||||
class ecs_task_definitions_host_namespace_not_shared(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for task_definition in ecs_client.task_definitions.values():
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = task_definition.region
|
||||
report.resource_id = f"{task_definition.name}:{task_definition.revision}"
|
||||
report.resource_arn = task_definition.arn
|
||||
report.resource_tags = task_definition.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"ECS task definition {task_definition.name} with revision {task_definition.revision} does not share a host's process namespace with its containers."
|
||||
if task_definition.pid_mode == "host":
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"ECS task definition {task_definition.name} with revision {task_definition.revision} is configured to share a host's process namespace with its containers."
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -28,6 +28,7 @@ def mock_make_api_call(self, operation_name, kwarg):
|
||||
}
|
||||
],
|
||||
"networkMode": "host",
|
||||
"pidMode": "host",
|
||||
"tags": [],
|
||||
}
|
||||
}
|
||||
@@ -157,6 +158,7 @@ class Test_ECS_Service:
|
||||
assert ecs.task_definitions[task_arn].network_mode == "host"
|
||||
assert not ecs.task_definitions[task_arn].container_definitions[0].privileged
|
||||
assert ecs.task_definitions[task_arn].container_definitions[0].user == ""
|
||||
assert ecs.task_definitions[task_arn].pid_mode == "host"
|
||||
assert (
|
||||
not ecs.task_definitions[task_arn]
|
||||
.container_definitions[0]
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.aws.services.ecs.ecs_service import (
|
||||
ContainerDefinition,
|
||||
ContainerEnvVariable,
|
||||
TaskDefinition,
|
||||
)
|
||||
from tests.providers.aws.utils import (
|
||||
AWS_ACCOUNT_NUMBER,
|
||||
AWS_REGION_US_EAST_1,
|
||||
set_mocked_aws_provider,
|
||||
)
|
||||
|
||||
task_name = "test-task"
|
||||
task_revision = "1"
|
||||
container_name = "test-container"
|
||||
task_arn = f"arn:aws:ecs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:task-definition/{task_name}:{task_revision}"
|
||||
|
||||
|
||||
class Test_ecs_task_definitions_host_namespace_not_shared:
|
||||
def test_no_task_definitions(self):
|
||||
ecs_client = mock.MagicMock
|
||||
ecs_client.task_definitions = {}
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_aws_provider([AWS_REGION_US_EAST_1]),
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.ecs.ecs_service.ECS",
|
||||
ecs_client,
|
||||
):
|
||||
from prowler.providers.aws.services.ecs.ecs_task_definitions_host_namespace_not_shared.ecs_task_definitions_host_namespace_not_shared import (
|
||||
ecs_task_definitions_host_namespace_not_shared,
|
||||
)
|
||||
|
||||
check = ecs_task_definitions_host_namespace_not_shared()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_task_definition_no_host_pid_mode(self):
|
||||
ecs_client = mock.MagicMock
|
||||
ecs_client.task_definitions = {}
|
||||
ecs_client.task_definitions[task_arn] = TaskDefinition(
|
||||
name=task_name,
|
||||
arn=task_arn,
|
||||
revision=task_revision,
|
||||
region=AWS_REGION_US_EAST_1,
|
||||
network_mode="bridge",
|
||||
pid_mode="task",
|
||||
container_definitions=[
|
||||
ContainerDefinition(
|
||||
name=container_name,
|
||||
privileged=False,
|
||||
user="",
|
||||
environment=[
|
||||
ContainerEnvVariable(
|
||||
name="env_var_name_no_secrets",
|
||||
value="env_var_value_no_secrets",
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_aws_provider([AWS_REGION_US_EAST_1]),
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.ecs.ecs_service.ECS",
|
||||
ecs_client,
|
||||
):
|
||||
from prowler.providers.aws.services.ecs.ecs_task_definitions_host_namespace_not_shared.ecs_task_definitions_host_namespace_not_shared import (
|
||||
ecs_task_definitions_host_namespace_not_shared,
|
||||
)
|
||||
|
||||
check = ecs_task_definitions_host_namespace_not_shared()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"ECS task definition {task_name} with revision {task_revision} does not share a host's process namespace with its containers."
|
||||
)
|
||||
|
||||
def test_task_definition_host_pid_mode(self):
|
||||
ecs_client = mock.MagicMock
|
||||
ecs_client.task_definitions = {}
|
||||
ecs_client.task_definitions[task_arn] = TaskDefinition(
|
||||
name=task_name,
|
||||
arn=task_arn,
|
||||
revision=task_revision,
|
||||
region=AWS_REGION_US_EAST_1,
|
||||
network_mode="host",
|
||||
pid_mode="host",
|
||||
container_definitions=[
|
||||
ContainerDefinition(
|
||||
name=container_name,
|
||||
privileged=False,
|
||||
user="root",
|
||||
environment=[],
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_aws_provider([AWS_REGION_US_EAST_1]),
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.ecs.ecs_service.ECS",
|
||||
ecs_client,
|
||||
):
|
||||
from prowler.providers.aws.services.ecs.ecs_task_definitions_host_namespace_not_shared.ecs_task_definitions_host_namespace_not_shared import (
|
||||
ecs_task_definitions_host_namespace_not_shared,
|
||||
)
|
||||
|
||||
check = ecs_task_definitions_host_namespace_not_shared()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"ECS task definition {task_name} with revision {task_revision} is configured to share a host's process namespace with its containers."
|
||||
)
|
||||
|
||||
def test_task_definition_no_pid_mode(self):
|
||||
ecs_client = mock.MagicMock
|
||||
ecs_client.task_definitions = {}
|
||||
ecs_client.task_definitions[task_arn] = TaskDefinition(
|
||||
name=task_name,
|
||||
arn=f"arn:aws:ecs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:task-definition/{task_name}:{task_revision}",
|
||||
revision=task_revision,
|
||||
region=AWS_REGION_US_EAST_1,
|
||||
network_mode="host",
|
||||
pid_mode="",
|
||||
container_definitions=[
|
||||
ContainerDefinition(
|
||||
name=container_name,
|
||||
privileged=True,
|
||||
user="root",
|
||||
environment=[],
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_aws_provider([AWS_REGION_US_EAST_1]),
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.ecs.ecs_service.ECS",
|
||||
ecs_client,
|
||||
):
|
||||
from prowler.providers.aws.services.ecs.ecs_task_definitions_host_namespace_not_shared.ecs_task_definitions_host_namespace_not_shared import (
|
||||
ecs_task_definitions_host_namespace_not_shared,
|
||||
)
|
||||
|
||||
check = ecs_task_definitions_host_namespace_not_shared()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"ECS task definition {task_name} with revision {task_revision} does not share a host's process namespace with its containers."
|
||||
)
|
||||
Reference in New Issue
Block a user