Compare commits

...

4 Commits

Author SHA1 Message Date
HugoPBrito
2681acf996 feat: added testing 2024-11-04 11:47:51 +01:00
HugoPBrito
062a53b2ad feat: added check logic 2024-11-04 11:36:43 +01:00
HugoPBrito
7fcee422c8 feat: added check metadata 2024-11-04 10:24:37 +01:00
HugoPBrito
f6f204fd6b chore: enhanced kms service 2024-11-04 09:37:14 +01:00
5 changed files with 431 additions and 10 deletions

View File

@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "iam_policy_no_kms_decryption_actions",
"CheckTitle": "Check if IAM customer managed policies",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices/CIS AWS Foundations Benchmark"
],
"ServiceName": "iam",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:iam::{account-id}:{resource-type}/{resource-id}",
"Severity": "medium",
"ResourceType": "AwsIamPolicy",
"Description": "Ensure IAM customer managed policies do not allow broad decryption permissions (kms:Decrypt or kms:ReEncryptFrom) on all AWS KMS keys",
"Risk": "Allowing broad decryption permissions (kms:Decrypt or kms:ReEncryptFrom) on all AWS KMS keys can lead to unauthorized data access, especially if users inadvertently or maliciously decrypt sensitive information.",
"RelatedUrl": "https://docs.aws.amazon.com/kms/latest/developerguide/iam-policies.html",
"Remediation": {
"Code": {
"CLI": "aws iam update-policy --policy-arn <policy-arn> --policy-document '{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Action\": [ \"kms:Decrypt\", \"kms:ReEncryptFrom\" ], \"Resource\": \"arn:aws:kms:<region>:<account-id>:key/<key-id>\" } ] }'",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/kms-controls.html#kms-1",
"Terraform": ""
},
"Recommendation": {
"Text": "Restrict decryption actions in IAM customer managed policies to specific KMS keys rather than allowing permissions on all keys. By specifying particular key ARNs in the Resource field, you limit the keys that identities can decrypt, enhancing control and compliance with best practices in data security.",
"Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage-edit.html#edit-managed-policy-console"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}

View File

@@ -0,0 +1,52 @@
from typing import List
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_kms_decryption_actions(Check):
"""Check IAM policies for KMS decryption actions.
This class verifies that IAM policies do not allow KMS decryption actions (kms:Decrypt or kms:ReEncryptFrom) on all resources.
"""
def execute(self) -> List[Check_Report_AWS]:
"""Execute the No KMS Decryption Actions check.
Iterates over all IAM policies and checks if any of them allow KMS decryption actions.
Returns:
List[Check_Report_AWS]: A list of reports for each IAM policy that allows KMS decryption actions.
"""
findings = []
for policy in iam_client.policies:
# Check only custom policies
if policy.type == "Custom":
report = Check_Report_AWS(self.metadata())
report.region = iam_client.region
report.resource_arn = policy.arn
report.resource_id = policy.name
report.resource_tags = policy.tags
report.status = "PASS"
report.status_extended = f"Custom Policy {policy.name} does not allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
if policy.document:
for statement in policy.document.get("Statement", []):
if (
statement.get("Effect") == "Allow"
and "Action" in statement
and (
"kms:Decrypt" in statement["Action"]
or "kms:ReEncryptFrom" in statement["Action"]
)
and "Resource" in statement
and "*" in statement["Resource"]
):
report.status = "FAIL"
report.status_extended = f"Custom Policy {policy.name} does allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
findings.append(report)
return findings

View File

@@ -1,14 +1,13 @@
import json
from typing import Optional
from typing import Dict, List, Optional
from pydantic import BaseModel
from pydantic import BaseModel, Field
from prowler.lib.logger import logger
from prowler.lib.scan_filters.scan_filters import is_resource_filtered
from prowler.providers.aws.lib.service.service import AWSService
################## KMS
class KMS(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
@@ -115,11 +114,11 @@ class KMS(AWSService):
class Key(BaseModel):
id: str
arn: str
state: Optional[str]
origin: Optional[str]
manager: Optional[str]
rotation_enabled: Optional[bool]
policy: Optional[dict]
spec: Optional[str]
state: Optional[str] = None
origin: Optional[str] = None
manager: Optional[str] = None
rotation_enabled: Optional[bool] = None
policy: Optional[Dict] = None
spec: Optional[str] = None
region: str
tags: Optional[list] = []
tags: List[Dict[str, str]] = Field(default_factory=list)

View File

@@ -0,0 +1,338 @@
from json import dumps
from unittest import mock
from boto3 import client
from moto import mock_aws
from prowler.providers.aws.services.iam.iam_service import IAM
from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider
class Test_iam_policy_no_kms_decryption_actions:
@mock_aws
def test_no_policies(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
new=IAM(aws_provider),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
iam_policy_no_kms_decryption_actions,
)
check = iam_policy_no_kms_decryption_actions()
result = check.execute()
assert len(result) == 0
@mock_aws
def test_iam_policy_no_kms_actions(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "no_kms_actions"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Deny", "Action": "kms:Decrypt", "Resource": "*"},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
new=IAM(aws_provider),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
iam_policy_no_kms_decryption_actions,
)
check = iam_policy_no_kms_decryption_actions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} does not allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == []
@mock_aws
def test_iam_policy_allows_kms_decrypt(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "allows_kms_decrypt"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "kms:Decrypt", "Resource": "*"},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
new=IAM(aws_provider),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
iam_policy_no_kms_decryption_actions,
)
check = iam_policy_no_kms_decryption_actions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} does allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == []
@mock_aws
def test_iam_policy_allows_kms_reencrypt_from(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "allows_kms_reencrypt_from"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "kms:ReEncryptFrom", "Resource": "*"},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
new=IAM(aws_provider),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
iam_policy_no_kms_decryption_actions,
)
check = iam_policy_no_kms_decryption_actions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} does allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == []
@mock_aws
def test_iam_policy_allows_kms_decrypt_on_specific_resource(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "allows_kms_decrypt_specific_resource"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "arn:aws:kms:us-east-1:123456789012:key/1234abcd",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
new=IAM(aws_provider),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
iam_policy_no_kms_decryption_actions,
)
check = iam_policy_no_kms_decryption_actions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} does not allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == []
@mock_aws
def test_iam_policy_allows_kms_reencrypt_from_on_specific_resource(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "allows_kms_decrypt_specific_resource"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "kms:ReEncryptFrom",
"Resource": "arn:aws:kms:us-east-1:123456789012:key/1234abcd",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
new=IAM(aws_provider),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
iam_policy_no_kms_decryption_actions,
)
check = iam_policy_no_kms_decryption_actions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} does not allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == []
@mock_aws
def test_iam_policy_allows_both_kms_actions(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "allows_both_kms_actions"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["kms:Decrypt", "kms:ReEncryptFrom"],
"Resource": "*",
},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
new=IAM(aws_provider),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
iam_policy_no_kms_decryption_actions,
)
check = iam_policy_no_kms_decryption_actions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} does allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == []
@mock_aws
def test_iam_policy_unrelated_actions_policy(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam")
policy_name = "unrelated_actions_policy"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "s3:ListBucket", "Resource": "*"},
],
}
arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
new=IAM(aws_provider),
):
# Test Check
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
iam_policy_no_kms_decryption_actions,
)
check = iam_policy_no_kms_decryption_actions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Custom Policy {policy_name} does not allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == []