feat(checks): New IAM Checks no full access to critical services (#2183)

This commit is contained in:
Gabriel Soltz
2023-04-12 07:47:21 +02:00
committed by GitHub
parent 9104d2e89e
commit 2f8a8988d7
8 changed files with 448 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "iam_policy_no_full_access_to_cloudtrail",
"CheckTitle": "Ensure IAM policies that allow full \"cloudtrail:*\" privileges are not created",
"CheckType": [
"Software and Configuration Checks",
"Industry and Regulatory Standards",
"CIS AWS Foundations Benchmark"
],
"ServiceName": "iam",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsIamPolicy",
"Description": "Ensure IAM policies that allow full \"cloudtrail:*\" privileges are not created",
"Risk": "CloudTrail is a critical service and IAM policies should follow least privilege model for this service in particular",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "It is more secure to start with a minimum set of permissions and grant additional permissions as necessary; rather than starting with permissions that are too lenient and then trying to tighten them later. List policies an analyze if permissions are the least possible to conduct business activities.",
"Url": "http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}

View File

@@ -0,0 +1,37 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.iam_client import iam_client
critical_service = "cloudtrail"
class iam_policy_no_full_access_to_cloudtrail(Check):
def execute(self) -> Check_Report_AWS:
findings = []
for policy in iam_client.policies:
report = Check_Report_AWS(self.metadata())
report.region = iam_client.region
report.resource_arn = policy.get("Arn")
report.resource_id = policy.get("PolicyName")
report.status = "PASS"
report.status_extended = f"Policy {policy.get('PolicyName')} does not allow '{critical_service}:*' privileges"
if policy.get("PolicyDocument"):
# Check the statements, if one includes critical_service:* stop iterating over the rest
if type(policy.get("PolicyDocument").get("Statement")) != list:
policy_statements = [policy.get("PolicyDocument").get("Statement")]
else:
policy_statements = policy.get("PolicyDocument").get("Statement")
for statement in policy_statements:
# Check policies with "Effect": "Allow" with "Action": "*" over "Resource": "*".
if (
statement.get("Effect") == "Allow"
and critical_service + ":*" in statement.get("Action")
and (
statement.get("Resource") == "*"
or statement.get("Resource") == ["*"]
)
):
report.status = "FAIL"
report.status_extended = f"Policy {policy.get('PolicyName')} allows '{critical_service}:*' privileges"
break
findings.append(report)
return findings

View File

@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "iam_policy_no_full_access_to_kms",
"CheckTitle": "Ensure IAM policies that allow full \"kms:*\" privileges are not created",
"CheckType": [
"Software and Configuration Checks",
"Industry and Regulatory Standards",
"CIS AWS Foundations Benchmark"
],
"ServiceName": "iam",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsIamPolicy",
"Description": "Ensure IAM policies that allow full \"kms:*\" privileges are not created",
"Risk": "KMS is a critical service and IAM policies should follow least privilege model for this service in particular",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "It is more secure to start with a minimum set of permissions and grant additional permissions as necessary; rather than starting with permissions that are too lenient and then trying to tighten them later. List policies an analyze if permissions are the least possible to conduct business activities.",
"Url": "http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}

View File

@@ -0,0 +1,37 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.iam_client import iam_client
critical_service = "kms"
class iam_policy_no_full_access_to_kms(Check):
def execute(self) -> Check_Report_AWS:
findings = []
for policy in iam_client.policies:
report = Check_Report_AWS(self.metadata())
report.region = iam_client.region
report.resource_arn = policy.get("Arn")
report.resource_id = policy.get("PolicyName")
report.status = "PASS"
report.status_extended = f"Policy {policy.get('PolicyName')} does not allow '{critical_service}:*' privileges"
if policy.get("PolicyDocument"):
# Check the statements, if one includes critical_service:* stop iterating over the rest
if type(policy.get("PolicyDocument").get("Statement")) != list:
policy_statements = [policy.get("PolicyDocument").get("Statement")]
else:
policy_statements = policy.get("PolicyDocument").get("Statement")
for statement in policy_statements:
# Check policies with "Effect": "Allow" with "Action": "*" over "Resource": "*".
if (
statement.get("Effect") == "Allow"
and critical_service + ":*" in statement.get("Action")
and (
statement.get("Resource") == "*"
or statement.get("Resource") == ["*"]
)
):
report.status = "FAIL"
report.status_extended = f"Policy {policy.get('PolicyName')} allows '{critical_service}:*' privileges"
break
findings.append(report)
return findings

View File

@@ -0,0 +1,155 @@
from json import dumps
from unittest import mock
from boto3 import client, session
from moto import mock_iam
from prowler.providers.aws.lib.audit_info.audit_info import AWS_Audit_Info
from prowler.providers.aws.services.iam.iam_service import IAM
class Test_iam_policy_no_full_access_to_cloudtrail:
# Mocked Audit Info
def set_mocked_audit_info(self):
audit_info = AWS_Audit_Info(
session_config=None,
original_session=None,
audit_session=session.Session(
profile_name=None,
botocore_session=None,
),
audited_account=None,
audited_user_id=None,
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region="us-east-1",
credentials=None,
assumed_role_info=None,
audited_regions=None,
organizations_metadata=None,
audit_resources=None,
)
return audit_info
@mock_iam
def test_policy_full_access_to_cloudtrail(self):
audit_info = self.set_mocked_audit_info()
iam_client = client("iam")
policy_name = "policy_cloudtrail_full"
policy_document_full_access = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "cloudtrail:*", "Resource": "*"},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document_full_access)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_full_access_to_cloudtrail.iam_policy_no_full_access_to_cloudtrail.iam_client",
new=IAM(audit_info),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_full_access_to_cloudtrail.iam_policy_no_full_access_to_cloudtrail import (
iam_policy_no_full_access_to_cloudtrail,
)
check = iam_policy_no_full_access_to_cloudtrail()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Policy {policy_name} allows 'cloudtrail:*' privileges"
)
assert result[0].resource_id == "policy_cloudtrail_full"
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_iam
def test_policy_no_full_access_to_cloudtrail(self):
audit_info = self.set_mocked_audit_info()
iam_client = client("iam")
policy_name = "policy_no_cloudtrail_full"
policy_document_full_access = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "ec2:*", "Resource": "*"},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document_full_access)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_full_access_to_cloudtrail.iam_policy_no_full_access_to_cloudtrail.iam_client",
new=IAM(audit_info),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_full_access_to_cloudtrail.iam_policy_no_full_access_to_cloudtrail import (
iam_policy_no_full_access_to_cloudtrail,
)
check = iam_policy_no_full_access_to_cloudtrail()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Policy {policy_name} does not allow 'cloudtrail:*' privileges"
)
assert result[0].resource_id == "policy_no_cloudtrail_full"
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_iam
def test_policy_mixed(self):
audit_info = self.set_mocked_audit_info()
iam_client = client("iam")
policy_name = "policy_mixed"
policy_document_full_access = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ec2:*", "cloudtrail:*"],
"Resource": "*",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document_full_access)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_full_access_to_cloudtrail.iam_policy_no_full_access_to_cloudtrail.iam_client",
new=IAM(audit_info),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_full_access_to_cloudtrail.iam_policy_no_full_access_to_cloudtrail import (
iam_policy_no_full_access_to_cloudtrail,
)
check = iam_policy_no_full_access_to_cloudtrail()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Policy {policy_name} allows 'cloudtrail:*' privileges"
)
assert result[0].resource_id == "policy_mixed"
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"

View File

@@ -0,0 +1,151 @@
from json import dumps
from unittest import mock
from boto3 import client, session
from moto import mock_iam
from prowler.providers.aws.lib.audit_info.audit_info import AWS_Audit_Info
from prowler.providers.aws.services.iam.iam_service import IAM
class Test_iam_policy_no_full_access_to_kms:
# Mocked Audit Info
def set_mocked_audit_info(self):
audit_info = AWS_Audit_Info(
session_config=None,
original_session=None,
audit_session=session.Session(
profile_name=None,
botocore_session=None,
),
audited_account=None,
audited_user_id=None,
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region="us-east-1",
credentials=None,
assumed_role_info=None,
audited_regions=None,
organizations_metadata=None,
audit_resources=None,
)
return audit_info
@mock_iam
def test_policy_full_access_to_kms(self):
audit_info = self.set_mocked_audit_info()
iam_client = client("iam")
policy_name = "policy_kms_full"
policy_document_full_access = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "kms:*", "Resource": "*"},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document_full_access)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_full_access_to_kms.iam_policy_no_full_access_to_kms.iam_client",
new=IAM(audit_info),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_full_access_to_kms.iam_policy_no_full_access_to_kms import (
iam_policy_no_full_access_to_kms,
)
check = iam_policy_no_full_access_to_kms()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Policy {policy_name} allows 'kms:*' privileges"
)
assert result[0].resource_id == "policy_kms_full"
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_iam
def test_policy_no_full_access_to_kms(self):
audit_info = self.set_mocked_audit_info()
iam_client = client("iam")
policy_name = "policy_no_kms_full"
policy_document_full_access = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "ec2:*", "Resource": "*"},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document_full_access)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_full_access_to_kms.iam_policy_no_full_access_to_kms.iam_client",
new=IAM(audit_info),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_full_access_to_kms.iam_policy_no_full_access_to_kms import (
iam_policy_no_full_access_to_kms,
)
check = iam_policy_no_full_access_to_kms()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Policy {policy_name} does not allow 'kms:*' privileges"
)
assert result[0].resource_id == "policy_no_kms_full"
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"
@mock_iam
def test_policy_mixed(self):
audit_info = self.set_mocked_audit_info()
iam_client = client("iam")
policy_name = "policy_mixed"
policy_document_full_access = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": ["ec2:*", "kms:*"], "Resource": "*"},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document_full_access)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=audit_info,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_full_access_to_kms.iam_policy_no_full_access_to_kms.iam_client",
new=IAM(audit_info),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_full_access_to_kms.iam_policy_no_full_access_to_kms import (
iam_policy_no_full_access_to_kms,
)
check = iam_policy_no_full_access_to_kms()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Policy {policy_name} allows 'kms:*' privileges"
)
assert result[0].resource_id == "policy_mixed"
assert result[0].resource_arn == arn
assert result[0].region == "us-east-1"