From fbee69be13320d835ac8752abab72f388c407c90 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Thu, 1 Aug 2024 13:55:30 +0700 Subject: [PATCH] feat(checks): improve S3 server logging access detection for AVD-AWS-0089 Signed-off-by: Nikita Pivkin --- avd_docs/aws/s3/AVD-AWS-0089/docs.md | 2 + checks/cloud/aws/s3/enable_logging.rego | 69 +++++++++++++++++--- checks/cloud/aws/s3/enable_logging_test.rego | 52 +++++++++++++-- 3 files changed, 110 insertions(+), 13 deletions(-) diff --git a/avd_docs/aws/s3/AVD-AWS-0089/docs.md b/avd_docs/aws/s3/AVD-AWS-0089/docs.md index fe0e14d8..94dc5bc4 100644 --- a/avd_docs/aws/s3/AVD-AWS-0089/docs.md +++ b/avd_docs/aws/s3/AVD-AWS-0089/docs.md @@ -10,4 +10,6 @@ Ensures S3 bucket logging is enabled for S3 buckets ### Links - https://docs.aws.amazon.com/AmazonS3/latest/userguide/ServerLogs.html +- https://docs.aws.amazon.com/AmazonS3/latest/userguide/enable-server-access-logging.html + diff --git a/checks/cloud/aws/s3/enable_logging.rego b/checks/cloud/aws/s3/enable_logging.rego index 87177772..4b1ba430 100644 --- a/checks/cloud/aws/s3/enable_logging.rego +++ b/checks/cloud/aws/s3/enable_logging.rego @@ -1,11 +1,12 @@ # METADATA -# title: "S3 Bucket Logging" -# description: "Ensures S3 bucket logging is enabled for S3 buckets" +# title: S3 Bucket Logging +# description: Ensures S3 bucket logging is enabled for S3 buckets # scope: package # schemas: # - input: schema["cloud"] # related_resources: # - https://docs.aws.amazon.com/AmazonS3/latest/userguide/ServerLogs.html +# - https://docs.aws.amazon.com/AmazonS3/latest/userguide/enable-server-access-logging.html # custom: # id: AVD-AWS-0089 # avd_id: AVD-AWS-0089 @@ -15,7 +16,7 @@ # short_code: enable-logging # aliases: # - s3-bucket-logging -# recommended_action: "Add a logging block to the resource to enable access logging" +# recommended_action: Add a logging block to the resource to enable access logging # input: # selector: # - type: cloud @@ -23,15 +24,65 @@ # - service: s3 # provider: aws # terraform: -# good_examples: "checks/cloud/aws/s3/enable_bucket_logging.tf.go" -# links: "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket" +# good_examples: checks/cloud/aws/s3/enable_bucket_logging.tf.go +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket # cloud_formation: -# good_examples: "checks/cloud/aws/s3/enable_bucket_logging.cf.go" +# good_examples: checks/cloud/aws/s3/enable_bucket_logging.cf.go package builtin.aws.s3.aws0089 +import future.keywords.if +import future.keywords.in + deny[res] { bucket := input.aws.s3.buckets[_] - not bucket.acl.value == "log-delivery-write" - not bucket.logging.enabled.value - res := result.new("Bucket has logging disabled", bucket.logging.enabled) + not bucket_has_server_logging_access(bucket) + not is_logging_enabled(bucket) + res := result.new( + "Bucket has logging disabled", + object.get(bucket, ["logging", "enabled"], bucket), + ) +} + +is_logging_enabled(bucket) if bucket.logging.enabled.value + +# Canned ACL +# The LogDelivery group gets WRITE and READ_ACP permissions on the bucket. +bucket_has_server_logging_access(bucket) if { + bucket.acl.value == "log-delivery-write" +} + +bucket_has_server_logging_access(bucket) if { + some grant in bucket.grants + grantee_is_log_delivery_group(grant) + has_write_and_read_acp_permissions(grant.permissions) +} + +# Grant permissions to the log delivery group by using a bucket AC +has_write_and_read_acp_permissions(permissions) if { + has_permission(permissions, "FULL_CONTROL") +} + +has_write_and_read_acp_permissions(permissions) if { + has_permission(permissions, "WRITE") + has_permission(permissions, "READ_ACP") +} + +has_permission(permissions, v) if { + some permission in permissions + permission.value == v +} + +log_group := "http://acs.amazonaws.com/groups/s3/LogDelivery" + +grantee_is_log_delivery_group(grant) if grant.grantee.uri.value == log_group + +# Grant permissions to the logging service principal by using a bucket policy +bucket_has_server_logging_access(bucket) if { + policy := bucket.bucketpolicies[_] + doc := json.unmarshal(policy.document.value) + statement := doc.Statement[_] + statement.Effect == "Allow" + "s3:PutObject" in statement.Action + "logging.s3.amazonaws.com" in statement.Principal.Service } diff --git a/checks/cloud/aws/s3/enable_logging_test.rego b/checks/cloud/aws/s3/enable_logging_test.rego index c0482503..0e80a08b 100644 --- a/checks/cloud/aws/s3/enable_logging_test.rego +++ b/checks/cloud/aws/s3/enable_logging_test.rego @@ -1,16 +1,16 @@ package builtin.aws.s3.aws0089 -test_detects_when_disabled { +test_deny_logging_disabled { r := deny with input as {"aws": {"s3": {"buckets": [{"logging": {"enabled": {"value": false}}}]}}} count(r) == 1 } -test_when_enabled { +test_allow_logging_enabled { r := deny with input as {"aws": {"s3": {"buckets": [{"logging": {"enabled": {"value": true}}}]}}} count(r) == 0 } -test_detects_when_disabled_but_acl_is_log_write { +test_allow_logging_disabled_but_bucket_has_server_logging_access_acl { r := deny with input as {"aws": {"s3": {"buckets": [{ "logging": {"enabled": {"value": false}}, "acl": {"value": "log-delivery-write"}, @@ -18,10 +18,54 @@ test_detects_when_disabled_but_acl_is_log_write { count(r) == 0 } -test_detects_when_enabled_and_acl_is_not_log_write { +test_deny_logging_disabled_and_bucket_does_not_have_server_access_logging { r := deny with input as {"aws": {"s3": {"buckets": [{ "logging": {"enabled": {"value": false}}, "acl": {"value": "private"}, }]}}} count(r) == 1 } + +test_allow_logging_disabled_but_bucket_has_server_logging_access_grant { + r := deny with input as {"aws": {"s3": {"buckets": [{ + "logging": {"enabled": {"value": false}}, + "acl": {"value": "log-delivery-write"}, + "grants": [ + { + "grantee": { + "id": {"value": "111122223333"}, + "type": {"value": "CanonicalUser"}, + }, + "permissions": [{"value": "FULL_CONTROL"}], + }, + { + "grantee": { + "uri": {"value": "http://acs.amazonaws.com/groups/s3/LogDelivery"}, + "type": {"value": "GROUP"}, + }, + "permissions": [ + {"value": "READ_ACP"}, + {"value": "WRITE"}, + ], + }, + ], + }]}}} + count(r) == 0 +} + +test_allow_logging_disabled_but_bucket_has_server_logging_access_policy { + r := deny with input as {"aws": {"s3": {"buckets": [{ + "logging": {"enabled": {"value": false}}, + "bucketpolicies": [{"document": {"value": json.marshal({ + "Version": "2012-10-17", + "Statement": [{ + "Sid": "S3ServerAccessLogsPolicy", + "Effect": "Allow", + "Principal": {"Service": ["logging.s3.amazonaws.com"]}, + "Action": ["s3:PutObject"], + "Resource": "arn:aws:s3:::DOC-EXAMPLE-DESTINATION-BUCKET-logs/*", + }], + })}}], + }]}}} + count(r) == 0 +}