Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sns_subscriptions: Breaking change from issue 19796 where an exception was added for code that works #26719

Closed
MurraySpeight opened this issue Aug 11, 2023 · 10 comments · Fixed by #26886
Assignees
Labels
@aws-cdk/aws-sns-subscriptions bug This issue is a bug. effort/small Small work item – less than a day of effort p0

Comments

@MurraySpeight
Copy link

Describe the bug

Merging of change from issue #19796 has caused a breaking change in my CDK project. Creating new issue to create attention to the problem and quoting comment I put on the closed issue below:

@NGL321 @bmoffatt it looks like this change is preventing my originally working CDK on v2.88 to no longer synth on v2.89.

I am currently using an SSE enabled queue subscribed to a topic and with the necessary IAM privileges mentioned here https://docs.aws.amazon.com/sns/latest/dg/sns-key-management.html#sns-what-permissions-for-sse the CDK was synthing and deploying in CloudFormation without issue. Now that I have upgraded CDK, the code is refusing to synth with this new exception!

Here is what the code I am using looks like:

import { RemovalPolicy } from "aws-cdk-lib";
import { Queue, QueueEncryption } from "aws-cdk-lib/aws-sqs";
import { Construct } from "constructs";
import { Topic } from "aws-cdk-lib/aws-sns";
import { SqsSubscription } from "aws-cdk-lib/aws-sns-subscriptions";
import { Key } from "aws-cdk-lib/aws-kms";
import { Effect, PolicyStatement, ServicePrincipal } from "aws-cdk-lib/aws-iam";
import { StageProps } from "@ros-aws-coop/shared-constructs";

interface SubscriptionQueueProps {
  stageName: string;
}

export class SubscriptionQueue extends Construct {
  public readonly dlq;
  public readonly subDlq;
  public readonly queue;

  constructor(
    scope: Construct,
    id: string,
    props: StageProps<SubscriptionQueueProps>
  ) {
    super(scope, id);

    const key = new Key(this, "Key");
    const keyAlias = key.addAlias(`my-sqs-${props.stageName}`);
    keyAlias.applyRemovalPolicy(RemovalPolicy.DESTROY);

    this.dlq = new Queue(scope, "SqsDlq", {
      encryption: QueueEncryption.KMS_MANAGED,
      encryptionMasterKey: keyAlias,
      enforceSSL: true
    });

    this.subDlq = new Queue(scope, "SubDlq", {
      encryption: QueueEncryption.KMS_MANAGED,
      encryptionMasterKey: keyAlias,
      enforceSSL: true
    });

    this.queue = new Queue(scope, "Queue", {
      encryption: QueueEncryption.KMS_MANAGED,
      encryptionMasterKey: keyAlias,
      enforceSSL: true,
      deadLetterQueue: {
        queue: this.dlq,
        maxReceiveCount: 1
      }
    });

    const someTopic = Topic.fromTopicArn(
      this,
      "TopicInAnotherAccount",
      "arn:aws:sns:us-east-1:12345678910:topicinanotheraccount"
    );
    someTopic.addSubscription(
      new SqsSubscription(this.queue, {
        rawMessageDelivery: true,
        deadLetterQueue: this.subDlq
      })
    );

    // Allow SNS topics to write into the queue
    keyAlias.addToResourcePolicy(
      new PolicyStatement({
        sid: "sns-allow",
        effect: Effect.ALLOW,
        resources: [someTopic.topicArn],
        principals: [new ServicePrincipal("sns")],
        actions: ["kms:Decrypt", "kms:GenerateDataKey"]
      })
    );
  }
}

Expected Behavior

Calling addSubscription method on a topic with a subscription to an SSE SQS queue using a customer key to work without exception.

Current Behavior

SQS queue encrypted by AWS managed KMS key cannot be used as SNS subscription exception message thrown when calling addSubscription method on a topic with a subscription to an SSE SQS queue using a customer key.

Reproduction Steps

Code in main description. Specifically:

someTopic.addSubscription(
      new SqsSubscription(this.queue, {
        rawMessageDelivery: true,
        deadLetterQueue: this.subDlq
      })
    );

Possible Solution

Back out change made on issue #19796

Additional Information/Context

No response

CDK CLI Version

2.89

Framework Version

No response

Node.js Version

18

OS

Linux

Language

Typescript

Language Version

No response

Other information

CDK versions before 2.89 do not have this issue.

@MurraySpeight MurraySpeight added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Aug 11, 2023
@peterwoodworth
Copy link
Contributor

Thanks for reporting this @MurraySpeight,

It appears to me that the AWS managed key for SQS has the proper permissions. Here's the policy for the one in my console (since I can't find the docs for this):

{
    "Version": "2012-10-17",
    "Id": "auto-sqs-1",
    "Statement": [
        {
            "Sid": "Allow access through Simple Queue Service (SQS) for all principals in the account that are authorized to use SQS",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:CreateGrant",
                "kms:DescribeKey"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "kms:CallerAccount": "123456789012",
                    "kms:ViaService": "sqs.us-east-1.amazonaws.com"
                }
            }
        },
        {
            "Sid": "Allow direct access to key metadata to the account",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:root"
            },
            "Action": [
                "kms:Describe*",
                "kms:Get*",
                "kms:List*",
                "kms:RevokeGrant"
            ],
            "Resource": "*"
        }
    ]
}

@peterwoodworth peterwoodworth added p0 effort/small Small work item – less than a day of effort and removed needs-triage This issue or PR still needs to be triaged. labels Aug 11, 2023
@peterwoodworth
Copy link
Contributor

peterwoodworth commented Aug 11, 2023

Have you verified that this setup actually works, or just that it deploys? I can get it to deploy, but I'm not sure if the redrive functionality will work (I'm not sure exactly how to force that to occur to test). I see this warning in the console on my subscription

Screenshot 2023-08-11 at 2 59 42 PM

@peterwoodworth peterwoodworth added the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Aug 11, 2023
@github-actions
Copy link

This issue has not received a response in a while. If you want to keep this issue open, please leave a comment below and auto-close will be canceled.

@github-actions github-actions bot added the closing-soon This issue will automatically close in 4 days unless further comments are made. label Aug 14, 2023
@MurraySpeight
Copy link
Author

MurraySpeight commented Aug 14, 2023

Hi @peterwoodworth, yes this deploys and works as expected. My SQS queue is subscribed to the topic and is receiving events. The DLQ redrive functionality also works and I do not get the message in the console you see - but DLQs aren't relevant to this problem because the exception occurs with just an SQS trying to subscribe to a topic without DLQs configured.

@github-actions github-actions bot removed closing-soon This issue will automatically close in 4 days unless further comments are made. response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. labels Aug 14, 2023
@lixx
Copy link

lixx commented Aug 15, 2023

We also see the same issue after upgrading to 2.89.0

import sns = require('aws-cdk-lib/aws-sns');
import sqs = require('aws-cdk-lib/aws-sqs');
// ...
const failureQueue = new sqs.Queue(this, failureQueueName, {
    queueName: failureQueueName,
    encryption: sqs.QueueEncryption.KMS_MANAGED,
    retentionPeriod: Duration.days(14),
    visibilityTimeout: Duration.seconds(60),
});

failureTopic.addSubscription(
    new SqsSubscription(failureQueue, {
        rawMessageDelivery: true,
    })
);

FYI: Before the upgrade, the SQS queue was deployed successfully with SSE enabled using SSE-KMS (with master key alias/aws/sqs).

@mrgrain
Copy link
Contributor

mrgrain commented Aug 24, 2023

Hi @peterwoodworth, yes this deploys and works as expected. My SQS queue is subscribed to the topic and is receiving events. The DLQ redrive functionality also works and I do not get the message in the console you see - but DLQs aren't relevant to this problem because the exception occurs with just an SQS trying to subscribe to a topic without DLQs configured.

Hey @MurraySpeight Thanks for providing the example! When I deploy your code, it does work. However the queue is actually encrypted with a customer managed key.

image

This is because we silently do this check at the moment:

      if (encryption !== QueueEncryption.KMS && props.encryptionMasterKey) {
        encryption = QueueEncryption.KMS; // KMS is implied by specifying an encryption key
      }

Can you please confirm for me:

  • firstly, if you are queues are also encrypted with a Customer Managed Key, or actually with a KMS managed key.
  • secondly, if your app works again if you change the encryption property in your code to:
encryption: QueueEncryption.KMS,

Basically I think being able to specify encryption: QueueEncryption.KMS_MANAGED and a encryptionMasterKey at the same time should error, or at least warn.

We also see the same issue after upgrading to 2.89.0

Hi @lixx Thanks for providing these details. I cannot get your example to work.
Could you double check for me the actual encryption configuration on the queue?

@mrgrain
Copy link
Contributor

mrgrain commented Aug 25, 2023

It appears to me that the AWS managed key for SQS has the proper permissions. Here's the policy for the one in my console (since I can't find the docs for this):

@peterwoodworth I think the policy is maybe missing permissions for SNS. Which explains why @MurraySpeight's code works - it does add permissions to the key for new ServicePrincipal("sns"). The default policy does this weird "kms:ViaService": "sqs.us-east-1.amazonaws.com" thing.

Edit: It tested various policies, and it's the "kms:CallerAccount": "123456789012" condition that breaks it.

@mrgrain mrgrain added the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Aug 25, 2023
@mrgrain
Copy link
Contributor

mrgrain commented Aug 25, 2023

@lixx I did some more resource (see post above) and I don't believe this does work. The code snippet you've provided does synth and deploy, but the subscription does not actually work. If you can provide me with more details, I'm happy to investigate further. Until then, I am assuming you've run into a similar situation as @MurraySpeight and will consider the issue fixed with the change in #26884

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Aug 25, 2023
@MurraySpeight
Copy link
Author

@mrgrain the queues are encrypted with a customer managed key as I believe this is the only way to encrypt when subscribing to a cross-account SNS topic. I have made your suggested change of changing the QueueEncryption from KMS_MANAGED to KMS and upgraded to the latest CDK. This has cleared the exception when synthing and resulted in the same CloudFormation template being generated. I have gone a step further and removed the QueueEncryption parameter altogether because as you have stated, the value of KMS is assumed by the presence of the encryptionMasterKey parameter and CDK sets this automatically to KMS.

So my understanding of this issue from your above description is I was incorrectly configuring the queue but pre-v2.89 CDK was silently fixing my mistake by overriding to the KMS value since I had the encryptionMasterKey set. This silent fixing made me think I had set the correct value because it worked but it only worked because CDK corrected it for me.

I think a warning here would be massively helpful e.g. IF encryptionMasterKey has value AND encryption is not QueueEncryption.KMS THEN throw warning. The exception message is not taking into account that CDK is changing the QueueEncryption for the ignorant developer and is therefore misleading.

Thanks for your help resolving this!

@mergify mergify bot closed this as completed in #26886 Aug 25, 2023
mergify bot pushed a commit that referenced this issue Aug 25, 2023
…vided (#26886)

In #19796 we added additional validation to sns subscription.
For that purpose the Queue `encryptionType` was exposed as a public property.
However the PR forgot to take into account that the provided `encryption` property is automatically changed when a `encryptionMasterKey` is provided.

This PR ensures that the public `encryptionType` has the correct value.

Additionally, adds a warning for an incorrect configuration scenario where `encryptionMasterKey` is provided together with an `encryption` other than QueueEncryption.KMS.
This feature was supposed to allow users to simply provide an encryption key and have the encryption type being selected automatically.
However it now unintentionally allows for wrong configurations that are silently fixed, e.g. setting QueueEncryption.UNENCRYPTED and providing an encryption key.
The warning keeps backwards compatibility, but instructs users to fix their configuration.

Closes #26719

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@github-actions
Copy link

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment