mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
fix(kms): detect public access for any KMS action, not just kms:* (#10071)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jfagoagas <16007882+jfagoagas@users.noreply.github.com> Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
This commit is contained in:
@@ -48,6 +48,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
### 🐞 Fixed
|
||||
|
||||
- `pip install prowler` failing on systems without C compiler due to `netifaces` transitive dependency from `openstacksdk` [(#10055)](https://github.com/prowler-cloud/prowler/pull/10055)
|
||||
- `kms_key_not_publicly_accessible` false negative for specific KMS actions (e.g., `kms:DescribeKey`, `kms:Decrypt`) with unrestricted principals [(#10071)](https://github.com/prowler-cloud/prowler/pull/10071)
|
||||
|
||||
---
|
||||
|
||||
|
||||
+2
-2
@@ -24,9 +24,9 @@
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws kms put-key-policy --key-id <example_resource_id> --policy-name default --policy '{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::<account_id>:root\"},\"Action\":\"kms:*\",\"Resource\":\"*\"}]}'",
|
||||
"CLI": "aws kms put-key-policy --key-id <example_resource_id> --policy-name default --policy '{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::<account_id>:root\"},\"Action\":\"kms:\\*\",\"Resource\":\"\\*\"}]}'",
|
||||
"NativeIaC": "```yaml\n# CloudFormation: restrict KMS key policy to account root (removes any public access)\nResources:\n <example_resource_name>:\n Type: AWS::KMS::Key\n Properties:\n KeyPolicy:\n Version: '2012-10-17'\n Statement:\n - Effect: Allow\n Principal:\n AWS: arn:aws:iam::<account_id>:root # Critical: only account root can access; prevents public \"*\" principals\n Action: kms:*\n Resource: '*'\n```",
|
||||
"Other": "1. Open AWS Console > Key Management Service (KMS)\n2. Select the affected key and go to the Key policy tab\n3. Click Edit and remove any statement with Principal set to \"*\" (or AWS: \"*\")\n4. Ensure a statement exists that allows only arn:aws:iam::<account_id>:root\n5. Save changes",
|
||||
"Other": "1. Open AWS Console > Key Management Service (KMS)\n2. Select the affected key and go to the Key policy tab\n3. Click Edit and remove any statement with Principal set to \"\\*\" (or AWS: \"\\*\")\n4. Ensure a statement exists that allows only arn:aws:iam::<account_id>:root\n5. Save changes",
|
||||
"Terraform": "```hcl\n# Restrict KMS key policy to the account root to avoid any public (\"*\") principals\ndata \"aws_caller_identity\" \"current\" {}\n\nresource \"aws_kms_key\" \"<example_resource_name>\" {\n policy = jsonencode({\n Version = \"2012-10-17\"\n Statement = [\n {\n Effect = \"Allow\"\n Principal = { AWS = \"arn:aws:iam::${data.aws_caller_identity.current.account_id}:root\" } # Critical: limit to account root to remove public access\n Action = \"kms:*\"\n Resource = \"*\"\n }\n ]\n })\n}\n```"
|
||||
},
|
||||
"Recommendation": {
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ class kms_key_not_publicly_accessible(Check):
|
||||
if is_policy_public(
|
||||
key.policy,
|
||||
kms_client.audited_account,
|
||||
not_allowed_actions=["kms:*"],
|
||||
not_allowed_actions=[],
|
||||
):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
|
||||
+110
@@ -129,6 +129,116 @@ class Test_kms_key_not_publicly_accessible:
|
||||
assert result[0].resource_id == key["KeyId"]
|
||||
assert result[0].resource_arn == key["Arn"]
|
||||
|
||||
@mock_aws
|
||||
def test_kms_key_public_accessible_with_describe_key(self):
|
||||
# Generate KMS Client
|
||||
kms_client = client("kms", region_name=AWS_REGION_US_EAST_1)
|
||||
# Create KMS key with public policy allowing kms:DescribeKey
|
||||
key = kms_client.create_key(
|
||||
MultiRegion=False,
|
||||
Policy=json.dumps(
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Id": "key-default-1",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowDescribeKeyPermissionForClusterOperator",
|
||||
"Effect": "Allow",
|
||||
"Principal": {"AWS": "*"},
|
||||
"Action": "kms:DescribeKey",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
}
|
||||
),
|
||||
)["KeyMetadata"]
|
||||
|
||||
from prowler.providers.aws.services.kms.kms_service import KMS
|
||||
|
||||
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,
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.kms.kms_key_not_publicly_accessible.kms_key_not_publicly_accessible.kms_client",
|
||||
new=KMS(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.kms.kms_key_not_publicly_accessible.kms_key_not_publicly_accessible import (
|
||||
kms_key_not_publicly_accessible,
|
||||
)
|
||||
|
||||
check = kms_key_not_publicly_accessible()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"KMS key {key['KeyId']} may be publicly accessible."
|
||||
)
|
||||
assert result[0].resource_id == key["KeyId"]
|
||||
assert result[0].resource_arn == key["Arn"]
|
||||
|
||||
@mock_aws
|
||||
def test_kms_key_public_accessible_with_decrypt(self):
|
||||
# Generate KMS Client
|
||||
kms_client = client("kms", region_name=AWS_REGION_US_EAST_1)
|
||||
# Create KMS key with public policy allowing kms:Decrypt
|
||||
key = kms_client.create_key(
|
||||
MultiRegion=False,
|
||||
Policy=json.dumps(
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Id": "key-default-1",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowDecryptPermissionPublicly",
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "kms:Decrypt",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
}
|
||||
),
|
||||
)["KeyMetadata"]
|
||||
|
||||
from prowler.providers.aws.services.kms.kms_service import KMS
|
||||
|
||||
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,
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.kms.kms_key_not_publicly_accessible.kms_key_not_publicly_accessible.kms_client",
|
||||
new=KMS(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.kms.kms_key_not_publicly_accessible.kms_key_not_publicly_accessible import (
|
||||
kms_key_not_publicly_accessible,
|
||||
)
|
||||
|
||||
check = kms_key_not_publicly_accessible()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"KMS key {key['KeyId']} may be publicly accessible."
|
||||
)
|
||||
assert result[0].resource_id == key["KeyId"]
|
||||
assert result[0].resource_arn == key["Arn"]
|
||||
|
||||
@mock_aws
|
||||
def test_kms_key_empty_principal(self):
|
||||
# Generate KMS Client
|
||||
|
||||
Reference in New Issue
Block a user