Skip to content

sqs/events: Using an SQS queue as the target of an EB Rule leads to an incomplete KMS Key policy if the queue is encrypted with an imported key #35628

@johannesfloriangeiger

Description

@johannesfloriangeiger

Describe the bug

Consider the following minimal CDK snippet that creates a KMS key, an encrypted Event Bus, an encrypted SQS queue, an EB Rule for the Bus that targets the queue and a Lambda that consumes from that Queue:

const key = new kms.Key(this, "Key", {
    removalPolicy: RemovalPolicy.DESTROY,
});

const eventBus = new events.EventBus(this, "EventBus", {
    eventBusName: "EventBus",
    kmsKey: key
});

const queue = new sqs.Queue(this, "Queue", {
    queueName: "Queue",
    encryption: sqs.QueueEncryption.KMS,
    encryptionMasterKey: key, // kms.Key.fromKeyArn(this, "ImportedKey", key.keyArn),
});

new events.Rule(this, "Rule", {
    eventBus: events.EventBus.fromEventBusArn(this, "ImportedEventBus", eventBus.eventBusArn),
    ruleName: "Rule",
    eventPattern: {
        source: ["my.custom.source"]
    },
    targets: [new events_targets.SqsQueue(queue)]
});

const consumer = new lambda.Function(this, "Consumer", {
    functionName: "Consumer",
    logGroup: new LogGroup(this, "LogGroup", {
        logGroupName: "/aws/lambda/Consumer",
        removalPolicy: RemovalPolicy.DESTROY,
    }),
    runtime: lambda.Runtime.NODEJS_LATEST,
    code: lambda.Code.fromInline("exports.handler = async (event) => { console.log(\"Event received:\", event); }"),
    handler: "index.handler",
});
consumer.addEventSource(new lambda_event_sources.SqsEventSource(queue));

Sending events (events.json)

[
  {
    "Source": "my.custom.source",
    "DetailType": "myDetailType",
    "Detail": "{}",
    "EventBusName": "EventBus"
  }
]

to the Bus

aws events put-events --entries file://events.json

Works fine, the Lambda logs the received event.

Specifically take note of the generated KMS key policies:

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": {
				"AWS": "arn:aws:iam::<ACCOUNT>:root"
			},
			"Action": "kms:*",
			"Resource": "*"
		},
		{
			"Effect": "Allow",
			"Principal": {
				"Service": "events.amazonaws.com"
			},
			"Action": [
				"kms:Decrypt",
				"kms:DescribeKey",
				"kms:GenerateDataKey"
			],
			"Resource": "*",
			"Condition": {
				"StringEquals": {
					"aws:SourceArn": "arn:aws:events:<REGION>:<ACCOUNT>:event-bus/EventBus",
					"aws:SourceAccount": "<ACCOUNT>",
					"kms:EncryptionContext:aws:events:event-bus:arn": "arn:aws:events:<REGION>:<ACCOUNT>:event-bus/EventBus"
				}
			}
		},
		{
			"Effect": "Allow",
			"Principal": {
				"Service": "events.amazonaws.com"
			},
			"Action": [
				"kms:Decrypt",
				"kms:Encrypt",
				"kms:GenerateDataKey*",
				"kms:ReEncrypt*"
			],
			"Resource": "*",
			"Condition": {
				"StringEquals": {
					"aws:SourceAccount": "<ACCOUNT>"
				}
			}
		}
	]
}

Now, instead of using the Key directly for SQS, change the construct to

const queue = new sqs.Queue(this, "Queue", {
    queueName: "Queue",
    encryption: sqs.QueueEncryption.KMS,
    encryptionMasterKey: kms.Key.fromKeyArn(this, "ImportedKey", key.keyArn),
});

I.e., we "simulate" that the Key is imported, the CLI above stops working and the KMS key policies look like

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": {
				"AWS": "arn:aws:iam::<ACCOUNT>:root"
			},
			"Action": "kms:*",
			"Resource": "*"
		},
		{
			"Effect": "Allow",
			"Principal": {
				"Service": "events.amazonaws.com"
			},
			"Action": [
				"kms:Decrypt",
				"kms:DescribeKey",
				"kms:GenerateDataKey"
			],
			"Resource": "*",
			"Condition": {
				"StringEquals": {
					"aws:SourceAccount": "<ACCOUNT>",
					"aws:SourceArn": "arn:aws:events:<REGION>:<ACCOUNT>:event-bus/EventBus",
					"kms:EncryptionContext:aws:events:event-bus:arn": "arn:aws:events:<REGION>:<ACCOUNT>:event-bus/EventBus"
				}
			}
		}
	]
}

i.e., the last statement

		{
			"Effect": "Allow",
			"Principal": {
				"Service": "events.amazonaws.com"
			},
			"Action": [
				"kms:Decrypt",
				"kms:Encrypt",
				"kms:GenerateDataKey*",
				"kms:ReEncrypt*"
			],
			"Resource": "*",
			"Condition": {
				"StringEquals": {
					"aws:SourceAccount": "<ACCOUNT>"
				}
			}
		}

is missing altogether.

A workaround is to manually update the KMS key policy by adding the last statement.

Regression Issue

  • Select this option if this issue appears to be a regression.

Last Known Working CDK Library Version

No response

Expected Behavior

Both CDK constructs should lead to the same KMS key policy.

Current Behavior

See above, the KMS key policy is missing a statement.

Reproduction Steps

See above.

Possible Solution

No response

Additional Information/Context

No response

AWS CDK Library version (aws-cdk-lib)

2.214.0

AWS CDK CLI version

2.1029.1 (build b45b1ab)

Node.js Version

v24.3.0

OS

macOS Sequoia 15.6

Language

TypeScript

Language Version

No response

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    @aws-cdk/aws-sqsRelated to Amazon Simple Queue Serviceeffort/mediumMedium work item – several days of effortfeature-requestA feature should be added or improved.p2

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions