如何使用AWS CDK解决S3、SQS与KMS间的循环依赖问题
I ran into this exact same circular dependency issue a while back! The problem stems from how CDK handles implicit permissions when you link encrypted resources together. Here's the breakdown:
- Your S3 Bucket and SQS Queue both depend on the shared KMS key
- When you add the S3 event notification to the SQS Queue, CDK tries to automatically add two things:
- A permission for the S3 Bucket to send messages to the SQS Queue
- Permissions on the KMS key to let the S3 service principal encrypt messages for the encrypted queue
- This creates a loop: the KMS key needs to reference the S3 Bucket/Service Principal for permissions, but the S3 Bucket already depends on the KMS key for encryption.
Here's how to fix it:
1. Avoid defining the full KMS policy upfront
Instead of passing a pre-built policy when creating the KMS key, use addToResourcePolicy after creating your resources to add the necessary permissions. This breaks the circular dependency by separating the key creation from the permission additions.
2. Explicitly grant the required permissions
You need to grant two key sets of permissions:
- Let the S3 Bucket send messages to the SQS Queue
- Let the S3 service principal use the KMS key to encrypt messages for the queue
Modified Code Example
from aws_cdk import aws_kms as kms from aws_cdk import aws_s3 as s3 from aws_cdk import aws_sqs as sqs from aws_cdk import aws_s3_notifications as s3notif from aws_cdk import aws_iam as iam # Create KMS key WITHOUT passing an initial policy (or pass a base policy without S3-related permissions) kms_key = kms.Key( self, 'ssl_s3_sqs_kms_key', alias='sslS3SqsKmsKey', description='This is kms key', enabled=True, enable_key_rotation=True ) # Create the S3 bucket bucket = s3.Bucket( self, "ssl_s3_bucket_raw_kms", bucket_name="ssl-s3-bucket-kms-raw", encryption=s3.BucketEncryption.KMS, encryption_key=kms_key, ) # Create the SQS queue queue = sqs.Queue( self, "ssl_sqs_event_queue", queue_name="ssl-sqs-kms-event-queue", encryption=sqs.QueueEncryption.KMS, encryption_master_key=kms_key, ) # 1. Grant S3 permission to send messages to the queue queue.grant_send_messages(bucket) # 2. Grant S3 service principal permission to use the KMS key for queue encryption kms_key.add_to_resource_policy( iam.PolicyStatement( actions=[ "kms:GenerateDataKey", "kms:Encrypt" ], principals=[iam.ServicePrincipal("s3.amazonaws.com")], resources=["*"], conditions={ "ArnEquals": { "aws:SourceArn": bucket.bucket_arn } } ) ) # Add the event notification to the bucket notification = s3notif.SqsDestination(queue) filter1 = s3.NotificationKeyFilter(prefix="home/") bucket.add_event_notification(s3.EventType.OBJECT_CREATED, notification, filter1)
Why this works:
- By creating the KMS key first without the S3-related permissions, we avoid the initial circular reference between the key and the bucket.
- Using
queue.grant_send_messages(bucket)explicitly grants the S3 bucket permission to send messages to the queue, which is cleaner than relying on CDK's implicit permissions. - The
addToResourcePolicycall adds the necessary KMS permissions after the bucket and queue are created, breaking the loop since the key already exists and the bucket/queue depend on it, but the permission addition doesn't create a reverse dependency.
If you had a base policy you wanted to use for the KMS key, you can still define it, just leave out the S3-specific permissions and add them later with addToResourcePolicy.
内容的提问来源于stack exchange,提问作者Sachin Suresh

