From 479ad1c62b75433fb8f43a81189d88f16e124cdd Mon Sep 17 00:00:00 2001 From: shikha372 Date: Wed, 29 May 2024 16:53:25 -0700 Subject: [PATCH] revert: "fix(ses-actions): permissions too wide for S3 action" (#30375) ### Issue # (if applicable) Closes #[30143](https://github.com/aws/aws-cdk/issues/30143). ### Reason for this change Fix the below deployment failure Deployment fails with a Could not write to bucket error: 1:36:13 PM | CREATE_FAILED | AWS::SES::ReceiptRule | TestRuleSetStoreToBucketRule3E41D5CF Could not write to bucket: reprosess3rulestack-testemailstoref58b593c-dxh45g1m3y6b (Service: AmazonSimpleEmailService; Status Code: 400; Error Code: InvalidS3Configuration; Request ID: 817f5520-748b-4bae-b347-ec68df52b675; Proxy: null) This PR reverts the changes introduced in PR https://github.com/aws/aws-cdk/pull/29833 ### Description of changes This PR reverts the change that was made in CDK v2.139.0 to reduce overly broad permissions allocated to SES for the S3 receipt rule action. This resulted in deployment failure where SES is unable to write to s3 bucket. ### Description of how you validated changes Dry-run for integration tests ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-cdk-ses-receipt.assets.json | 4 +- .../aws-cdk-ses-receipt.template.json | 30 +-------- .../integ.actions.js.snapshot/manifest.json | 12 +--- .../test/integ.actions.js.snapshot/tree.json | 29 +-------- .../aws-cdk-lib/aws-ses-actions/lib/s3.ts | 61 +++++++------------ .../aws-ses-actions/test/actions.test.ts | 19 +----- .../aws-ses/lib/receipt-rule-action.ts | 8 --- .../aws-cdk-lib/aws-ses/lib/receipt-rule.ts | 17 ++---- 8 files changed, 35 insertions(+), 145 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.assets.json index 78acf6dcc87d2..e1f4e21c3afc9 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.assets.json @@ -14,7 +14,7 @@ } } }, - "e6588125503215c64027666d36868f7ca8e305ebf39630158558d6379dcc5fcb": { + "e75d52ecdaf0f3588db5bc3c10fdcd3a347911e7ec4edd2058d2cd142329a9c9": { "source": { "path": "aws-cdk-ses-receipt.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "e6588125503215c64027666d36868f7ca8e305ebf39630158558d6379dcc5fcb.json", + "objectKey": "e75d52ecdaf0f3588db5bc3c10fdcd3a347911e7ec4edd2058d2cd142329a9c9.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.template.json index 9c28697b45e93..c83ff34cb83ec 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.template.json @@ -86,35 +86,8 @@ "Action": "s3:PutObject", "Condition": { "StringEquals": { - "aws:SourceAccount": { + "aws:Referer": { "Ref": "AWS::AccountId" - }, - "aws:SourceArn": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ses:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":receipt-rule-set/", - { - "Ref": "RuleSetE30C6C48" - }, - ":receipt-rule/", - { - "Ref": "RuleSetFirstRule0A27C8CC" - } - ] - ] } } }, @@ -313,6 +286,7 @@ } }, "DependsOn": [ + "BucketPolicyE9A3008A", "FunctionAllowSes1829904A" ] }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/manifest.json index 5d007e6251fac..aae99fcf97f24 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/manifest.json @@ -18,7 +18,7 @@ "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}/e6588125503215c64027666d36868f7ca8e305ebf39630158558d6379dcc5fcb.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/e75d52ecdaf0f3588db5bc3c10fdcd3a347911e7ec4edd2058d2cd142329a9c9.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -91,19 +91,13 @@ "/aws-cdk-ses-receipt/RuleSet/FirstRule/Resource": [ { "type": "aws:cdk:logicalId", - "data": "RuleSetFirstRule0A27C8CC", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" - ] + "data": "RuleSetFirstRule0A27C8CC" } ], "/aws-cdk-ses-receipt/RuleSet/SecondRule/Resource": [ { "type": "aws:cdk:logicalId", - "data": "RuleSetSecondRule03178AD4", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" - ] + "data": "RuleSetSecondRule03178AD4" } ], "/aws-cdk-ses-receipt/SingletonLambda224e77f9a32e4b4dac32983477abba16/ServiceRole/Resource": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/tree.json index 4976261e07769..9ff4582ab6924 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/tree.json @@ -180,35 +180,8 @@ "Action": "s3:PutObject", "Condition": { "StringEquals": { - "aws:SourceAccount": { + "aws:Referer": { "Ref": "AWS::AccountId" - }, - "aws:SourceArn": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ses:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":receipt-rule-set/", - { - "Ref": "RuleSetE30C6C48" - }, - ":receipt-rule/", - { - "Ref": "RuleSetFirstRule0A27C8CC" - } - ] - ] } } }, diff --git a/packages/aws-cdk-lib/aws-ses-actions/lib/s3.ts b/packages/aws-cdk-lib/aws-ses-actions/lib/s3.ts index 156a50813f298..bc19658117120 100644 --- a/packages/aws-cdk-lib/aws-ses-actions/lib/s3.ts +++ b/packages/aws-cdk-lib/aws-ses-actions/lib/s3.ts @@ -42,12 +42,32 @@ export interface S3Props { * a notification to Amazon SNS. */ export class S3 implements ses.IReceiptRuleAction { - private rule?: ses.IReceiptRule; + constructor(private readonly props: S3Props) { } public bind(rule: ses.IReceiptRule): ses.ReceiptRuleActionConfig { - this.rule = rule; + // Allow SES to write to S3 bucket + // See https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html#receiving-email-permissions-s3 + const keyPattern = this.props.objectKeyPrefix || ''; + const s3Statement = new iam.PolicyStatement({ + actions: ['s3:PutObject'], + principals: [new iam.ServicePrincipal('ses.amazonaws.com')], + resources: [this.props.bucket.arnForObjects(`${keyPattern}*`)], + conditions: { + StringEquals: { + 'aws:Referer': cdk.Aws.ACCOUNT_ID, + }, + }, + }); + this.props.bucket.addToResourcePolicy(s3Statement); + + const policy = this.props.bucket.node.tryFindChild('Policy') as s3.BucketPolicy; + if (policy) { // The bucket could be imported + rule.node.addDependency(policy); + } else { + cdk.Annotations.of(rule).addWarningV2('@aws-cdk/s3:AddBucketPermissions', 'This rule is using a S3 action with an imported bucket. Ensure permission is given to SES to write to that bucket.'); + } // Allow SES to use KMS master key // See https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html#receiving-email-permissions-kms @@ -79,41 +99,4 @@ export class S3 implements ses.IReceiptRuleAction { }, }; } - - /** - * Generate and apply the receipt rule action statement - * - * @param ruleSet The rule set the rule is being added to - * @internal - */ - public _applyPolicyStatement(receiptRuleSet: ses.IReceiptRuleSet): void { - if (!this.rule) { - throw new Error('Cannot apply policy statement before binding the action to a receipt rule'); - } - - // Allow SES to write to S3 bucket - // See https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html#receiving-email-permissions-s3 - const keyPattern = this.props.objectKeyPrefix || ''; - const s3Statement = new iam.PolicyStatement({ - actions: ['s3:PutObject'], - principals: [new iam.ServicePrincipal('ses.amazonaws.com')], - resources: [this.props.bucket.arnForObjects(`${keyPattern}*`)], - conditions: { - StringEquals: { - 'aws:SourceAccount': cdk.Aws.ACCOUNT_ID, - 'aws:SourceArn': cdk.Arn.format({ - partition: cdk.Aws.PARTITION, - service: 'ses', - region: cdk.Aws.REGION, - account: cdk.Aws.ACCOUNT_ID, - resource: [ - `receipt-rule-set/${receiptRuleSet.receiptRuleSetName}`, - `receipt-rule/${this.rule.receiptRuleName}`, - ].join(':'), - }), - }, - }, - }); - this.props.bucket.addToResourcePolicy(s3Statement); - } } diff --git a/packages/aws-cdk-lib/aws-ses-actions/test/actions.test.ts b/packages/aws-cdk-lib/aws-ses-actions/test/actions.test.ts index dc2fa57d124f3..9769df6bcb4ab 100644 --- a/packages/aws-cdk-lib/aws-ses-actions/test/actions.test.ts +++ b/packages/aws-cdk-lib/aws-ses-actions/test/actions.test.ts @@ -190,26 +190,9 @@ test('add s3 action', () => { Action: 's3:PutObject', Condition: { StringEquals: { - 'aws:SourceAccount': { + 'aws:Referer': { Ref: 'AWS::AccountId', }, - 'aws:SourceArn': { - 'Fn::Join': [ - '', - [ - 'arn:', - { Ref: 'AWS::Partition' }, - ':ses:', - { Ref: 'AWS::Region' }, - ':', - { Ref: 'AWS::AccountId' }, - ':receipt-rule-set/', - { Ref: 'RuleSetE30C6C48' }, - ':receipt-rule/', - { Ref: 'RuleSetRule0B1D6BCA' }, - ], - ], - }, }, }, Effect: 'Allow', diff --git a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule-action.ts b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule-action.ts index 8e95fb714c19c..c63fd2f4eef85 100644 --- a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule-action.ts +++ b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule-action.ts @@ -1,5 +1,4 @@ import { IReceiptRule } from './receipt-rule'; -import { IReceiptRuleSet } from './receipt-rule-set'; /** * An abstract action for a receipt rule. @@ -10,13 +9,6 @@ export interface IReceiptRuleAction { */ bind(receiptRule: IReceiptRule): ReceiptRuleActionConfig; - /** - * Generate and apply the receipt rule action statement - * - * @param ruleSet The rule set the rule is being added to - * @internal - */ - _applyPolicyStatement?(ruleSet: IReceiptRuleSet): void; } /** diff --git a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts index 5b9dbe89f8c4a..ae662eb3f18d4 100644 --- a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts +++ b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts @@ -112,10 +112,7 @@ export class ReceiptRule extends Resource implements IReceiptRule { } public readonly receiptRuleName: string; - - private readonly ruleSet: IReceiptRuleSet; - private readonly actions: IReceiptRuleAction[] = []; - private readonly actionProperties: CfnReceiptRule.ActionProperty[] = []; + private readonly actions = new Array(); constructor(scope: Construct, id: string, props: ReceiptRuleProps) { super(scope, id, { @@ -136,7 +133,6 @@ export class ReceiptRule extends Resource implements IReceiptRule { }); this.receiptRuleName = resource.ref; - this.ruleSet = props.ruleSet; for (const action of props.actions || []) { this.addAction(action); @@ -147,20 +143,15 @@ export class ReceiptRule extends Resource implements IReceiptRule { * Adds an action to this receipt rule. */ public addAction(action: IReceiptRuleAction) { - this.actions.push(action); - this.actionProperties.push(action.bind(this)); + this.actions.push(action.bind(this)); } private renderActions() { - if (this.actionProperties.length === 0) { + if (this.actions.length === 0) { return undefined; } - for (const action of this.actions) { - action._applyPolicyStatement?.(this.ruleSet); - } - - return this.actionProperties; + return this.actions; } }