aws / aws/aws-cdk

aws-lambda: Adding an encrypted SQS queue as a Lambda DLQ does not grant Lambda role necessary KMS permissions

Open
#33,153 2 comments 0 reactions 0 assignees View on GitHub
@aws-cdk/aws-lambda bug effort/medium p2
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
2d 3h
Merged PRs (30d)
83

Description

### Describe the bug

When creating a Lambda, you have the option to [specify a DLQ](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Function.html#deadletterqueue). Doing so correctly automatically grants the Lambda's role `sqs:SendMessage` permissions on that SQS queue resource. However, if that SQS queue is encrypted with a customer-managed KMS key in the same stack, the Lambda's role is not automatically granted the required permissions on the KMS key resource. This leads to the Lambda being unable to write to the DLQ and messages being lost.

### Regression Issue

- [ ] Select this option if this issue appears to be a regression.

### Last Known Working CDK Version

_No response_

### Expected Behavior

The Lambda role to be automatically granted the required permissions on the KMS key

### Current Behavior

The Lambda role did not have any permissions on the KMS key

### Reproduction Steps

```TypeScript
import { Capture, Template } from "aws-cdk-lib/assertions";
import { Key } from "aws-cdk-lib/aws-kms";
import { Function, InlineCode, Runtime } from "aws-cdk-lib/aws-lambda";
import { Queue } from "aws-cdk-lib/aws-sqs";
import { App, Stack } from "aws-cdk-lib/core";
import { Construct } from "constructs";

class TestStack extends Stack {
constructor(scope: Construct, id: string) {
super(scope, id);

const lambdaDlqKey = new Key(this, "LambdaDlqKey", {
enableKeyRotation: true,
});

const lambdaDlq = new Queue(this, "LambdaDlq", {
queueName: "LambdaDlq",
encryptionMasterKey: lambdaDlqKey,
});

const lambda = new Function(this, "Lambda", {
code: new InlineCode("throw new Error()"),
handler: "someHandler",
runtime: Runtime.NODEJS_18_X,
deadLetterQueue: lambdaDlq,
});
}
}

describe("Function with customer managed KMS encrypted DLQ", () => {
it("does not grant KMS permissions", () => {
const mockApp = new App();

const testInfraStack = new TestStack(mockApp, "testStack");

const template = Template.fromStack(testInfraStack);

const statementCapture = new Capture();
template.hasResourceProperties("AWS::IAM::Policy", {
PolicyDocument: {
Statement: statementCapture,
},
});
// Succeeds (correctly grants required sqs:SendMessage permission)
expect(statementCapture.asArray().filter((statement) => hasActionMatching(statement, /sqs:SendMessage/))).not.toHaveLength(0);
// Fails: (does not grant required kms permission)
expect(statementCapture.asArray().filter((statement) => hasActionMatching(statement, /kms:.*/))).not.toHaveLength(0);
});
});

function hasActionMatching(statement: { Action: string | string[] }, action: RegExp) {
if (typeof statement.Action === "string") {
return action.test(statement.Action);
}
return statement.Action.filter((statementAction) => action.test(statementAction)).length > 0;
}
```

This can also be confirmed by deploying the TestStack, sending an async message to the Lambda, and validating that it is unable to write the failed message to its DLQ (and a dead letter queue failure metric is published).

### Possible Solution

This can of course be worked around by the user manually granting KMS permissions (e.g. `lambdaDlqKey.grantEncrypt(lambda.role!)`, or even `lambdaDlq.grantSendMessages(lambda)`), however the fact that this is not done automatically by CDK seems like a bug to me, or at very least a poor customer experience that can lead to customer data loss due to accidental misconfiguration.

A fix would be to check if the DLQ is encrypted when the Lambda is initialised in CDK, and grant the necessary KMS permissions if so.

### Additional Information/Context

_No response_

### CDK CLI Version

2.175.1

### Framework Version

_No response_

### Node.js Version

18.20.5

### OS

Ubuntu 24.04.1

### Language

TypeScript

### Language Version

TypeScript (5.7.3)

### Other information

_No response_

Contributor guide

Open the contributing guide

Research direction

Start with the Lambda Function deadLetterQueue handling and reproduce the behavior using the TestStack and assertions shown in the issue. Synthesize the IAM policy for a Lambda using a customer-managed KMS-encrypted SQS queue; done means the Lambda role includes the required KMS permissions in addition to sqs:SendMessage.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, typescript
Domain
cloud, security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 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.