aws / aws/aws-cdk

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

Open
#35,628 1 comment 0 reactions 0 assignees View on GitHub
@aws-cdk/aws-sqs effort/medium feature-request p2
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
1d 19h
Merged PRs (30d)
74

Description

### 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:::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:::event-bus/EventBus",
"aws:SourceAccount": "",
"kms:EncryptionContext:aws:events:event-bus:arn": "arn:aws:events:::event-bus/EventBus"
}
}
},
{
"Effect": "Allow",
"Principal": {
"Service": "events.amazonaws.com"
},
"Action": [
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey*",
"kms:ReEncrypt*"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:SourceAccount": ""
}
}
}
]
}
```

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:::root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Principal": {
"Service": "events.amazonaws.com"
},
"Action": [
"kms:Decrypt",
"kms:DescribeKey",
"kms:GenerateDataKey"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "",
"aws:SourceArn": "arn:aws:events:::event-bus/EventBus",
"kms:EncryptionContext:aws:events:event-bus:arn": "arn:aws:events:::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": ""
}
}
}
```

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_

Contributor guide

Open the contributing guide

Research direction

Start by synthesizing the supplied TypeScript reproduction and diff the KMS policies for direct versus imported keys. Trace the SQS/EventBridge target and imported-key handling in the CDK source; done when both forms emit the missing events.amazonaws.com permissions and a regression test covers the behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, typescript
Domain
cloud, infrastructure, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.