chore(ssm): add trusted accounts variable to ssm check (#5118)

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
This commit is contained in:
Prowler Bot
2024-09-20 15:48:58 +02:00
committed by GitHub
parent feae73a9d3
commit 3051929780
6 changed files with 133 additions and 5 deletions

View File

@@ -127,6 +127,7 @@ aws:
]
# 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.
# Multi account environment: Any additional trusted account number should be added as a space separated list, e.g.
# trusted_account_ids : ["123456789012", "098765432109", "678901234567"]

View File

@@ -43,6 +43,7 @@ aws:
]
# 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.
# Multi account environment: Any additional trusted account number should be added as a space separated list, e.g.
# trusted_account_ids : ["123456789012", "098765432109", "678901234567"]

View File

@@ -11,12 +11,25 @@ class ssm_documents_set_as_public(Check):
report.resource_arn = document.arn
report.resource_id = document.name
report.resource_tags = document.tags
if document.account_owners:
report.status = "FAIL"
report.status_extended = f"SSM Document {document.name} is public."
else:
trusted_account_ids = ssm_client.audit_config.get("trusted_account_ids", [])
if ssm_client.audited_account not in trusted_account_ids:
trusted_account_ids.append(ssm_client.audited_account)
if not document.account_owners or document.account_owners == [
ssm_client.audited_account
]:
report.status = "PASS"
report.status_extended = f"SSM Document {document.name} is not public."
elif document.account_owners == ["all"]:
report.status = "FAIL"
report.status_extended = f"SSM Document {document.name} is public."
elif all(owner in trusted_account_ids for owner in document.account_owners):
report.status = "PASS"
report.status_extended = f"SSM Document {document.name} is shared to trusted AWS accounts: {', '.join(document.account_owners)}."
elif not all(
owner in trusted_account_ids for owner in document.account_owners
):
report.status = "FAIL"
report.status_extended = f"SSM Document {document.name} is shared to non-trusted AWS accounts: {', '.join(document.account_owners)}."
findings.append(report)

View File

@@ -43,6 +43,7 @@ aws:
]
# 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.
# Multi account environment: Any additional trusted account number should be added as a space separated list, e.g.
# trusted_account_ids : ["123456789012", "098765432109", "678901234567"]

View File

@@ -19,6 +19,7 @@ ec2_allowed_instance_owners:
]
# 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.
# Multi account environment: Any additional trusted account number should be added as a space separated list, e.g.
# trusted_account_ids : ["123456789012", "098765432109", "678901234567"]

View File

@@ -22,7 +22,7 @@ class Test_ssm_documents_set_as_public:
assert len(result) == 0
def test_document_public(self):
def test_document_public_account_owners(self):
ssm_client = mock.MagicMock
document_name = "test-document"
document_arn = f"arn:aws:ssm:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:document/{document_name}"
@@ -48,6 +48,42 @@ class Test_ssm_documents_set_as_public:
check = ssm_documents_set_as_public()
result = check.execute()
assert len(result) == 1
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_id == document_name
assert result[0].resource_arn == document_arn
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"SSM Document {document_name} is shared to non-trusted AWS accounts: 111111111111, 111111222222."
)
def test_document_public_all_account_owners(self):
ssm_client = mock.MagicMock
document_name = "test-document"
document_arn = f"arn:aws:ssm:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:document/{document_name}"
ssm_client.audited_account = AWS_ACCOUNT_NUMBER
ssm_client.documents = {
document_name: Document(
arn=document_arn,
name=document_name,
region=AWS_REGION_US_EAST_1,
content="",
account_owners=["all"],
)
}
with mock.patch(
"prowler.providers.aws.services.ssm.ssm_service.SSM",
new=ssm_client,
):
# Test Check
from prowler.providers.aws.services.ssm.ssm_documents_set_as_public.ssm_documents_set_as_public import (
ssm_documents_set_as_public,
)
check = ssm_documents_set_as_public()
result = check.execute()
assert len(result) == 1
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_id == document_name
@@ -57,6 +93,81 @@ class Test_ssm_documents_set_as_public:
result[0].status_extended == f"SSM Document {document_name} is public."
)
def test_document_public_to_other_trusted_AWS_accounts(self):
ssm_client = mock.MagicMock
document_name = "test-document"
document_arn = f"arn:aws:ssm:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:document/{document_name}"
ssm_client.audited_account = AWS_ACCOUNT_NUMBER
ssm_client.documents = {
document_name: Document(
arn=document_arn,
name=document_name,
region=AWS_REGION_US_EAST_1,
content="",
account_owners=["111111111333", "111111222444"],
)
}
ssm_client.audit_config = {
"trusted_account_ids": ["111111111333", "111111222444"]
}
with mock.patch(
"prowler.providers.aws.services.ssm.ssm_service.SSM",
new=ssm_client,
):
# Test Check
from prowler.providers.aws.services.ssm.ssm_documents_set_as_public.ssm_documents_set_as_public import (
ssm_documents_set_as_public,
)
check = ssm_documents_set_as_public()
result = check.execute()
assert len(result) == 1
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_id == document_name
assert result[0].resource_arn == document_arn
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"SSM Document {document_name} is shared to trusted AWS accounts: 111111111333, 111111222444."
)
def test_document_public_to_self_account(self):
ssm_client = mock.MagicMock
document_name = "test-document"
document_arn = f"arn:aws:ssm:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:document/{document_name}"
ssm_client.audited_account = AWS_ACCOUNT_NUMBER
ssm_client.documents = {
document_name: Document(
arn=document_arn,
name=document_name,
region=AWS_REGION_US_EAST_1,
content="",
account_owners=[AWS_ACCOUNT_NUMBER],
)
}
with mock.patch(
"prowler.providers.aws.services.ssm.ssm_service.SSM",
new=ssm_client,
):
# Test Check
from prowler.providers.aws.services.ssm.ssm_documents_set_as_public.ssm_documents_set_as_public import (
ssm_documents_set_as_public,
)
check = ssm_documents_set_as_public()
result = check.execute()
assert len(result) == 1
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_id == document_name
assert result[0].resource_arn == document_arn
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"SSM Document {document_name} is not public."
)
def test_document_not_public(self):
ssm_client = mock.MagicMock
document_name = "test-document"