From 6c5b67ed3174bfd27a473e1468dc18917c3d7bba Mon Sep 17 00:00:00 2001 From: zdf Date: Wed, 24 May 2023 09:13:36 -0700 Subject: [PATCH] fix(s3): KMS encryption works fine for server access logging target buckets (#25350) The previous changes(https://github.com/aws/aws-cdk/pull/23514 & https://github.com/aws/aws-cdk/pull/23385) about failing early when certain encryption type being used is not correct. In fact, KMS encryption works fine for server access logging target buckets with proper permission being setup. So this change is removing the condition failing for the SSE-KMS with customized encryption key case. However, it is not possible to know which encryption type for the server access logging bucket, so the only checking can be applied after this change merged is failing when logging to self case using BucketEncryption.KMS_MANAGED. After this fix, the only condition would be failed pre-checking is __Log to self and using KMS_MANAGED encryption type__ This change only fix the checking condition, so this change won't affect snapshot at all. Hence, Exemption Request. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...efaultTestDeployAssertB937C102.assets.json | 19 + ...aultTestDeployAssertB937C102.template.json | 36 ++ ...-s3-server-access-logs-sse-kms.assets.json | 32 ++ ...3-server-access-logs-sse-kms.template.json | 395 +++++++++++++++++ .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 153 +++++++ .../tree.json | 404 ++++++++++++++++++ ...integ.bucket-server-access-logs-sse-kms.ts | 24 ++ packages/aws-cdk-lib/aws-s3/lib/bucket.ts | 19 +- .../aws-cdk-lib/aws-s3/test/bucket.test.ts | 30 +- 11 files changed, 1099 insertions(+), 26 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/aws-cdk-s3-server-access-logs-sse-kms.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/aws-cdk-s3-server-access-logs-sse-kms.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.assets.json new file mode 100644 index 0000000000000..f66cdffe3270a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.assets.json @@ -0,0 +1,19 @@ +{ + "version": "31.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/aws-cdk-s3-server-access-logs-sse-kms.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/aws-cdk-s3-server-access-logs-sse-kms.assets.json new file mode 100644 index 0000000000000..1b2f22aa6da67 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/aws-cdk-s3-server-access-logs-sse-kms.assets.json @@ -0,0 +1,32 @@ +{ + "version": "31.0.0", + "files": { + "40aa87cdf43c4095cec18bc443965f22ab2f8c1ace47e482a0ba4e35d83b0cc9": { + "source": { + "path": "asset.40aa87cdf43c4095cec18bc443965f22ab2f8c1ace47e482a0ba4e35d83b0cc9", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "40aa87cdf43c4095cec18bc443965f22ab2f8c1ace47e482a0ba4e35d83b0cc9.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "a2b5b863d9f5b680816a4fb9864f0f6813798b7bbdd9fe1fcf001e106175a199": { + "source": { + "path": "aws-cdk-s3-server-access-logs-sse-kms.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "a2b5b863d9f5b680816a4fb9864f0f6813798b7bbdd9fe1fcf001e106175a199.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/aws-cdk-s3-server-access-logs-sse-kms.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/aws-cdk-s3-server-access-logs-sse-kms.template.json new file mode 100644 index 0000000000000..afb92ede77d02 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/aws-cdk-s3-server-access-logs-sse-kms.template.json @@ -0,0 +1,395 @@ +{ + "Resources": { + "ServerAccessLogsBucketKey95B7E326": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "Description": "Created by aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket" + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "ServerAccessLogsBucket05F29982": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "KMSMasterKeyID": { + "Fn::GetAtt": [ + "ServerAccessLogsBucketKey95B7E326", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + } + } + ] + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "ServerAccessLogsBucketPolicy947BE3EE": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "ServerAccessLogsBucket05F29982" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "ServerAccessLogsBucket05F29982", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ServerAccessLogsBucket05F29982", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ServerAccessLogsBucket05F29982", + "Arn" + ] + }, + "/example*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ServerAccessLogsBucketAutoDeleteObjectsCustomResourceDA32BBFB": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "ServerAccessLogsBucket05F29982" + } + }, + "DependsOn": [ + "ServerAccessLogsBucketPolicy947BE3EE" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "40aa87cdf43c4095cec18bc443965f22ab2f8c1ace47e482a0ba4e35d83b0cc9.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + }, + "Runtime": "nodejs16.x", + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "ServerAccessLogsBucket05F29982" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ] + }, + "Bucket83908E77": { + "Type": "AWS::S3::Bucket", + "Properties": { + "LoggingConfiguration": { + "DestinationBucketName": { + "Ref": "ServerAccessLogsBucket05F29982" + }, + "LogFilePrefix": "example" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Mappings": { + "DefaultCrNodeVersionMap": { + "af-south-1": { + "value": "nodejs16.x" + }, + "ap-east-1": { + "value": "nodejs16.x" + }, + "ap-northeast-1": { + "value": "nodejs16.x" + }, + "ap-northeast-2": { + "value": "nodejs16.x" + }, + "ap-northeast-3": { + "value": "nodejs16.x" + }, + "ap-south-1": { + "value": "nodejs16.x" + }, + "ap-south-2": { + "value": "nodejs16.x" + }, + "ap-southeast-1": { + "value": "nodejs16.x" + }, + "ap-southeast-2": { + "value": "nodejs16.x" + }, + "ap-southeast-3": { + "value": "nodejs16.x" + }, + "ca-central-1": { + "value": "nodejs16.x" + }, + "cn-north-1": { + "value": "nodejs16.x" + }, + "cn-northwest-1": { + "value": "nodejs16.x" + }, + "eu-central-1": { + "value": "nodejs16.x" + }, + "eu-central-2": { + "value": "nodejs16.x" + }, + "eu-north-1": { + "value": "nodejs16.x" + }, + "eu-south-1": { + "value": "nodejs16.x" + }, + "eu-south-2": { + "value": "nodejs16.x" + }, + "eu-west-1": { + "value": "nodejs16.x" + }, + "eu-west-2": { + "value": "nodejs16.x" + }, + "eu-west-3": { + "value": "nodejs16.x" + }, + "me-central-1": { + "value": "nodejs16.x" + }, + "me-south-1": { + "value": "nodejs16.x" + }, + "sa-east-1": { + "value": "nodejs16.x" + }, + "us-east-1": { + "value": "nodejs16.x" + }, + "us-east-2": { + "value": "nodejs16.x" + }, + "us-gov-east-1": { + "value": "nodejs16.x" + }, + "us-gov-west-1": { + "value": "nodejs16.x" + }, + "us-iso-east-1": { + "value": "nodejs14.x" + }, + "us-iso-west-1": { + "value": "nodejs14.x" + }, + "us-isob-east-1": { + "value": "nodejs14.x" + }, + "us-west-1": { + "value": "nodejs16.x" + }, + "us-west-2": { + "value": "nodejs16.x" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/cdk.out new file mode 100644 index 0000000000000..7925065efbcc4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"31.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/integ.json new file mode 100644 index 0000000000000..13e49a9e69d4c --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "31.0.0", + "testCases": { + "ServerAccessLogsSseKmsTest/DefaultTest": { + "stacks": [ + "aws-cdk-s3-server-access-logs-sse-kms" + ], + "assertionStack": "ServerAccessLogsSseKmsTest/DefaultTest/DeployAssert", + "assertionStackName": "ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/manifest.json new file mode 100644 index 0000000000000..a7977003d35e7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/manifest.json @@ -0,0 +1,153 @@ +{ + "version": "31.0.0", + "artifacts": { + "aws-cdk-s3-server-access-logs-sse-kms.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-s3-server-access-logs-sse-kms.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-s3-server-access-logs-sse-kms": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-s3-server-access-logs-sse-kms.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/a2b5b863d9f5b680816a4fb9864f0f6813798b7bbdd9fe1fcf001e106175a199.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-s3-server-access-logs-sse-kms.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-cdk-s3-server-access-logs-sse-kms.assets" + ], + "metadata": { + "/aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket/Key/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ServerAccessLogsBucketKey95B7E326" + } + ], + "/aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ServerAccessLogsBucket05F29982" + } + ], + "/aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ServerAccessLogsBucketPolicy947BE3EE" + } + ], + "/aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket/AutoDeleteObjectsCustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "ServerAccessLogsBucketAutoDeleteObjectsCustomResourceDA32BBFB" + } + ], + "/aws-cdk-s3-server-access-logs-sse-kms/DefaultCrNodeVersionMap": [ + { + "type": "aws:cdk:logicalId", + "data": "DefaultCrNodeVersionMap" + } + ], + "/aws-cdk-s3-server-access-logs-sse-kms/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + } + ], + "/aws-cdk-s3-server-access-logs-sse-kms/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" + } + ], + "/aws-cdk-s3-server-access-logs-sse-kms/Bucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Bucket83908E77" + } + ], + "/aws-cdk-s3-server-access-logs-sse-kms/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-s3-server-access-logs-sse-kms/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-s3-server-access-logs-sse-kms" + }, + "ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "ServerAccessLogsSseKmsTestDefaultTestDeployAssertB937C102.assets" + ], + "metadata": { + "/ServerAccessLogsSseKmsTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/ServerAccessLogsSseKmsTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "ServerAccessLogsSseKmsTest/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/tree.json new file mode 100644 index 0000000000000..d0a0498e601ec --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.js.snapshot/tree.json @@ -0,0 +1,404 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-s3-server-access-logs-sse-kms": { + "id": "aws-cdk-s3-server-access-logs-sse-kms", + "path": "aws-cdk-s3-server-access-logs-sse-kms", + "children": { + "ServerAccessLogsBucket": { + "id": "ServerAccessLogsBucket", + "path": "aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket", + "children": { + "Key": { + "id": "Key", + "path": "aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket/Key", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket/Key/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Key", + "aws:cdk:cloudformation:props": { + "keyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "description": "Created by aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.CfnKey", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.Key", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "bucketEncryption": { + "serverSideEncryptionConfiguration": [ + { + "serverSideEncryptionByDefault": { + "sseAlgorithm": "aws:kms", + "kmsMasterKeyId": { + "Fn::GetAtt": [ + "ServerAccessLogsBucketKey95B7E326", + "Arn" + ] + } + } + } + ] + }, + "tags": [ + { + "key": "aws-cdk:auto-delete-objects", + "value": "true" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.CfnBucket", + "version": "0.0.0" + } + }, + "Policy": { + "id": "Policy", + "path": "aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket/Policy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket/Policy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", + "aws:cdk:cloudformation:props": { + "bucket": { + "Ref": "ServerAccessLogsBucket05F29982" + }, + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "ServerAccessLogsBucket05F29982", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ServerAccessLogsBucket05F29982", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "s3:PutObject", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + } + }, + "StringEquals": { + "aws:SourceAccount": { + "Ref": "AWS::AccountId" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "logging.s3.amazonaws.com" + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ServerAccessLogsBucket05F29982", + "Arn" + ] + }, + "/example*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.CfnBucketPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketPolicy", + "version": "0.0.0" + } + }, + "AutoDeleteObjectsCustomResource": { + "id": "AutoDeleteObjectsCustomResource", + "path": "aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket/AutoDeleteObjectsCustomResource", + "children": { + "Default": { + "id": "Default", + "path": "aws-cdk-s3-server-access-logs-sse-kms/ServerAccessLogsBucket/AutoDeleteObjectsCustomResource/Default", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.CustomResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.Bucket", + "version": "0.0.0" + } + }, + "DefaultCrNodeVersionMap": { + "id": "DefaultCrNodeVersionMap", + "path": "aws-cdk-s3-server-access-logs-sse-kms/DefaultCrNodeVersionMap", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnMapping", + "version": "0.0.0" + } + }, + "Custom::S3AutoDeleteObjectsCustomResourceProvider": { + "id": "Custom::S3AutoDeleteObjectsCustomResourceProvider", + "path": "aws-cdk-s3-server-access-logs-sse-kms/Custom::S3AutoDeleteObjectsCustomResourceProvider", + "children": { + "Staging": { + "id": "Staging", + "path": "aws-cdk-s3-server-access-logs-sse-kms/Custom::S3AutoDeleteObjectsCustomResourceProvider/Staging", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "aws-cdk-s3-server-access-logs-sse-kms/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "aws-cdk-s3-server-access-logs-sse-kms/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.CustomResourceProvider", + "version": "0.0.0" + } + }, + "Bucket": { + "id": "Bucket", + "path": "aws-cdk-s3-server-access-logs-sse-kms/Bucket", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-s3-server-access-logs-sse-kms/Bucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "loggingConfiguration": { + "destinationBucketName": { + "Ref": "ServerAccessLogsBucket05F29982" + }, + "logFilePrefix": "example" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.CfnBucket", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.Bucket", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-s3-server-access-logs-sse-kms/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-s3-server-access-logs-sse-kms/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "ServerAccessLogsSseKmsTest": { + "id": "ServerAccessLogsSseKmsTest", + "path": "ServerAccessLogsSseKmsTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "ServerAccessLogsSseKmsTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "ServerAccessLogsSseKmsTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.9" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "ServerAccessLogsSseKmsTest/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "ServerAccessLogsSseKmsTest/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "ServerAccessLogsSseKmsTest/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.9" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.ts new file mode 100644 index 0000000000000..30ca9d473f77a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-server-access-logs-sse-kms.ts @@ -0,0 +1,24 @@ +#!/usr/bin/env node +import * as cdk from 'aws-cdk-lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import * as s3 from 'aws-cdk-lib/aws-s3'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-cdk-s3-server-access-logs-sse-kms'); + +const accessLogBucket = new s3.Bucket(stack, 'ServerAccessLogsBucket', { + autoDeleteObjects: true, + removalPolicy: cdk.RemovalPolicy.DESTROY, + encryption: s3.BucketEncryption.KMS, +}); + +new s3.Bucket(stack, 'Bucket', { + serverAccessLogsBucket: accessLogBucket, + serverAccessLogsPrefix: 'example', + removalPolicy: cdk.RemovalPolicy.DESTROY, +}); + +new integ.IntegTest(app, 'ServerAccessLogsSseKmsTest', { + testCases: [stack], +}); diff --git a/packages/aws-cdk-lib/aws-s3/lib/bucket.ts b/packages/aws-cdk-lib/aws-s3/lib/bucket.ts index 9ed54f3c07311..d220e220082bb 100644 --- a/packages/aws-cdk-lib/aws-s3/lib/bucket.ts +++ b/packages/aws-cdk-lib/aws-s3/lib/bucket.ts @@ -2123,17 +2123,14 @@ export class Bucket extends BucketBase { return undefined; } - if ( - // KMS can't be used for logging since the logging service can't use the key - logs don't write - // KMS_MANAGED can't be used for logging since the account can't access the logging service key - account can't read logs - (!props.serverAccessLogsBucket && ( - props.encryptionKey || - props.encryption === BucketEncryption.KMS_MANAGED || - props.encryption === BucketEncryption.KMS )) || - // Another bucket is being used that is configured for default SSE-KMS - props.serverAccessLogsBucket?.encryptionKey - ) { - throw new Error('SSE-S3 is the only supported default bucket encryption for Server Access Logging target buckets'); + // KMS_MANAGED can't be used for logging since the account can't access the logging service key - account can't read logs + if (!props.serverAccessLogsBucket && props.encryption === BucketEncryption.KMS_MANAGED) { + throw new Error('Default bucket encryption with KMS managed key is not supported for Server Access Logging target buckets'); + } + + // When there is an encryption key exists for the server access logs bucket, grant permission to the S3 logging SP. + if (props.serverAccessLogsBucket?.encryptionKey) { + props.serverAccessLogsBucket.encryptionKey.grantEncryptDecrypt(new iam.ServicePrincipal('logging.s3.amazonaws.com')); } return { diff --git a/packages/aws-cdk-lib/aws-s3/test/bucket.test.ts b/packages/aws-cdk-lib/aws-s3/test/bucket.test.ts index bf81401c0849c..b59ec5f1d8160 100644 --- a/packages/aws-cdk-lib/aws-s3/test/bucket.test.ts +++ b/packages/aws-cdk-lib/aws-s3/test/bucket.test.ts @@ -383,30 +383,30 @@ describe('bucket', () => { const stack = new cdk.Stack(); expect(() => { new s3.Bucket(stack, 'MyBucket', { encryption: s3.BucketEncryption.KMS_MANAGED, serverAccessLogsPrefix: 'test' }); - }).toThrow(/SSE-S3 is the only supported default bucket encryption for Server Access Logging target buckets/); + }).toThrow(/Default bucket encryption with KMS managed key is not supported for Server Access Logging target buckets/); }); - test('logs to self, KMS encryption without key throws error', () => { + test('logs to self, KMS encryption without key does not throw error', () => { const stack = new cdk.Stack(); expect(() => { new s3.Bucket(stack, 'MyBucket', { encryption: s3.BucketEncryption.KMS, serverAccessLogsPrefix: 'test' }); - }).toThrow(/SSE-S3 is the only supported default bucket encryption for Server Access Logging target buckets/); + }).not.toThrowError(); }); - test('logs to self, KMS encryption with key throws error', () => { + test('logs to self, KMS encryption with key does not throw error', () => { const stack = new cdk.Stack(); const key = new kms.Key(stack, 'TestKey'); expect(() => { new s3.Bucket(stack, 'MyBucket', { encryptionKey: key, encryption: s3.BucketEncryption.KMS, serverAccessLogsPrefix: 'test' }); - }).toThrow(/SSE-S3 is the only supported default bucket encryption for Server Access Logging target buckets/); + }).not.toThrowError(); }); - test('logs to self, KMS key with no specific encryption specified throws error', () => { + test('logs to self, KMS key with no specific encryption specified does not throw error', () => { const stack = new cdk.Stack(); const key = new kms.Key(stack, 'TestKey'); expect(() => { new s3.Bucket(stack, 'MyBucket', { encryptionKey: key, serverAccessLogsPrefix: 'test' }); - }).toThrow(/SSE-S3 is the only supported default bucket encryption for Server Access Logging target buckets/); + }).not.toThrowError(); }); testDeprecated('logs to separate bucket, UNENCRYPTED does not throw error', () => { @@ -426,40 +426,40 @@ describe('bucket', () => { }); // When provided an external bucket (as an IBucket), we cannot detect KMS_MANAGED encryption. Since this - // check is impossible, we skip thist test. + // check is impossible, we skip this test. // eslint-disable-next-line jest/no-disabled-tests test.skip('logs to separate bucket, KMS_MANAGED encryption throws error', () => { const stack = new cdk.Stack(); const logBucket = new s3.Bucket(stack, 'testLogBucket', { encryption: s3.BucketEncryption.KMS_MANAGED }); expect(() => { new s3.Bucket(stack, 'MyBucket', { serverAccessLogsBucket: logBucket }); - }).toThrow(/SSE-S3 is the only supported default bucket encryption for Server Access Logging target buckets/); + }).toThrow(/Default bucket encryption with KMS managed key is not supported for Server Access Logging target buckets/); }); - test('logs to separate bucket, KMS encryption without key throws error', () => { + test('logs to separate bucket, KMS encryption without key does not throw error', () => { const stack = new cdk.Stack(); const logBucket = new s3.Bucket(stack, 'testLogBucket', { encryption: s3.BucketEncryption.KMS }); expect(() => { new s3.Bucket(stack, 'MyBucket', { serverAccessLogsBucket: logBucket }); - }).toThrow(/SSE-S3 is the only supported default bucket encryption for Server Access Logging target buckets/); + }).not.toThrowError(); }); - test('logs to separate bucket, KMS encryption with key throws error', () => { + test('logs to separate bucket, KMS encryption with key does not throw error', () => { const stack = new cdk.Stack(); const key = new kms.Key(stack, 'TestKey'); const logBucket = new s3.Bucket(stack, 'testLogBucket', { encryptionKey: key, encryption: s3.BucketEncryption.KMS }); expect(() => { new s3.Bucket(stack, 'MyBucket', { serverAccessLogsBucket: logBucket }); - }).toThrow(/SSE-S3 is the only supported default bucket encryption for Server Access Logging target buckets/); + }).not.toThrowError(); }); - test('logs to separate bucket, KMS key with no specific encryption specified throws error', () => { + test('logs to separate bucket, KMS key with no specific encryption specified does not throw error', () => { const stack = new cdk.Stack(); const key = new kms.Key(stack, 'TestKey'); const logBucket = new s3.Bucket(stack, 'testLogBucket', { encryptionKey: key }); expect(() => { new s3.Bucket(stack, 'MyBucket', { serverAccessLogsBucket: logBucket }); - }).toThrow(/SSE-S3 is the only supported default bucket encryption for Server Access Logging target buckets/); + }).not.toThrowError(); }); test('bucket with versioning turned on', () => {