Skip to content

feat(s3-tables): add KMS support for TableBucket L2 construct #34281

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

Merged
merged 10 commits into from
May 14, 2025

Conversation

xuxey
Copy link
Contributor

@xuxey xuxey commented Apr 28, 2025

Reason for this change

This adds support for encryption settings for TableBucket including providing KMS keys for server side encryption.

Description of changes

L1 reference: CfnTableBucket#encryptionConfiguration

Backwards compatible changes were made to the TableBucket construct in the following places:

  • TableBucketProps now include optional fields encryption and encryptionKey
  • grant methods now provide permissions to the bucket encryptionKey, if applicable
  • A new KMS key is created if the user provides KMS encryptionType but no key
  • Updated README with rosetta support

Usage

// Provide a user defined KMS Key:
const key = new kms.Key(scope, 'UserKey', {});
const encryptedBucket = new TableBucket(scope, 'EncryptedTableBucket', {
    tableBucketName: 'table-bucket-1',
    encryption: TableBucketEncryption.KMS,
    encryptionKey: key,
});
// This account principal will also receive kms:Decrypt access to the KMS key
encryptedBucket.grantRead(new iam.AccountPrincipal('123456789012'), '*');

// If no key is provided, one will be created automatically
const encryptedBucketAuto = new TableBucket(scope, 'EncryptedTableBucketAuto', {
    tableBucketName: 'table-bucket-2',
    encryption: TableBucketEncryption.KMS,
});

// Use S3 managed server side encryption (default)
const encryptedBucketDefault = new TableBucket(scope, 'EncryptedTableBucketDefault', {
    tableBucketName: 'table-bucket-3',
    encryption: TableBucketEncryption.S3_MANAGED,
});

Describe any new or updated permissions being added

These permissions were added for KMS support:

export const KEY_READ_ACCESS = [
  'kms:Decrypt',
];

export const KEY_WRITE_ACCESS = [
  'kms:Decrypt',
  'kms:GenerateDataKey*',
];

When grant methods are used, these policies are applied to the principal for the TableBucket's encryption key. For example, giving read access to an encrypted bucket without giving decrypt permissions to the bucket key will not be sufficient permissions for the principal to read the bucket data.

Description of how you validated changes

  • Added unit test coverage for all possible scenarios of bucket encryption config, as well as all grant methods for each valid encryption config.
  • Added integration tests with snapshot and assertions. The assertions are currently disabled due to the aws-sdk version not supporting GetTableBucketEncryptionCommand but will be included once resolved.

Checklist


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@github-actions github-actions bot added p2 beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK labels Apr 28, 2025
@aws-cdk-automation aws-cdk-automation requested a review from a team April 28, 2025 18:54
@xuxey xuxey marked this pull request as ready for review April 28, 2025 20:10
@godwingrs22 godwingrs22 self-assigned this Apr 28, 2025
@aws-cdk-automation aws-cdk-automation added the pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. label Apr 30, 2025
Copy link
Member

@godwingrs22 godwingrs22 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @xuxey for adding KMS encryption support to the S3 TableBucket L2 construct. I have left some thoughts for your considerations. Also since we are adding the encryption support, this would required a security review. I'll initiate the security review in parallel.

Comment on lines +92 to +96
// If no key is provided, one will be created automatically
const encryptedBucketAuto = new TableBucket(scope, 'EncryptedTableBucketAuto', {
tableBucketName: 'table-bucket-2',
encryption: TableBucketEncryption.KMS,
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since S3 TableBuckets already have S3-managed SSE enabled by default (which I assume is free and secure for most use cases), having an auto-generated KMS key option might lead to unexpected costs for users ($1/month per key plus API call costs - here). Organizations that require KMS encryption typically either have existing KMS keys or want explicit control over key management to align with their security policies.

I suggest we simplify the API to just support customer-provided KMS keys while keeping S3-managed SSE as the default. This would eliminate the need for the TableBucketEncryption enum since the only encryption option a user would need to specify is their KMS key. This approach would reduce complexity, avoid unexpected costs, and better align with real-world usage patterns.

What are your thoughts on this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having an auto-generated KMS key option might lead to unexpected costs for users

This could be true for some users, however there are advantages for customers with auto-generated keys

  1. This is consistent with the general purpose S3 Bucket construct, which auto-generates KMS keys
  2. Customers can leverage autogenerated keys by not having to explicitly create keys, and these keys will also contain all of the necessary access policies (for example allowlisting S3 Tables Maintenance)

I suggest we simplify the API to just support customer-provided KMS keys while keeping S3-managed SSE as the default

Since both encryptionKey and encryption are optional parameters, this behavior already exists. We would like to keep the interface with TableBucketEncryption since it is more in-sync with the S3 Tables API and CloudFormation resource definitions, and can easily extend to other encryption formats that may be supported in the future.

/**
* Use S3 managed encryption keys with AES256 encryption
*/
S3_MANAGED = 'AES256',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to explicity pass AES256 algorithm to enable the default encryption (S3 managed keys - SSE-S3)? Based on this doc, seems by default SSE encryption with S3 managed keys will be already with AES256 algorithm to encrypt the data. So I assume CDK users not required to pass this explicitly.

If we don't need to explicitly set this in CloudFormation (as it's already the default behavior), then perhaps we don't need the TableBucketEncryption enum at all? The API could be simpler - if a user wants to use KMS encryption, they just provide a KMS key, otherwise it defaults to S3-managed encryption (AES-256).

The construct could look something like:

interface TableBucketProps {
  tableBucketName: string;
  encryptionKey?: kms.IKey;  // If provided, uses KMS encryption
  // ... other props
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by default SSE encryption with S3 managed keys will be already with AES256 algorithm to encrypt the data. So I assume CDK users not required to pass this explicitly.

That's correct, this is not explicitly needed since AES256 is the default.

The API could be simpler - if a user wants to use KMS encryption, they just provide a KMS key, otherwise it defaults to S3-managed encryption (AES-256).

Addressed this in the other comment -- we would like to keep our interface extensible for the future if we add more encryption support, and keep the interface in-line with the Tables API & resource defs.

That said, the behavior you described (use kms key if provided) is still supported by the current interface

@godwingrs22 godwingrs22 added p1 and removed p2 labels May 2, 2025
@aws-cdk-automation aws-cdk-automation added pr/needs-maintainer-review This PR needs a review from a Core Team Member and removed pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. labels May 2, 2025
Copy link
Contributor Author

@xuxey xuxey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, Godwin! I spoke with the team about the interface decisions and have left some follow-ups in the replies. Let me know what you think :)

Comment on lines +92 to +96
// If no key is provided, one will be created automatically
const encryptedBucketAuto = new TableBucket(scope, 'EncryptedTableBucketAuto', {
tableBucketName: 'table-bucket-2',
encryption: TableBucketEncryption.KMS,
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having an auto-generated KMS key option might lead to unexpected costs for users

This could be true for some users, however there are advantages for customers with auto-generated keys

  1. This is consistent with the general purpose S3 Bucket construct, which auto-generates KMS keys
  2. Customers can leverage autogenerated keys by not having to explicitly create keys, and these keys will also contain all of the necessary access policies (for example allowlisting S3 Tables Maintenance)

I suggest we simplify the API to just support customer-provided KMS keys while keeping S3-managed SSE as the default

Since both encryptionKey and encryption are optional parameters, this behavior already exists. We would like to keep the interface with TableBucketEncryption since it is more in-sync with the S3 Tables API and CloudFormation resource definitions, and can easily extend to other encryption formats that may be supported in the future.

/**
* Use S3 managed encryption keys with AES256 encryption
*/
S3_MANAGED = 'AES256',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by default SSE encryption with S3 managed keys will be already with AES256 algorithm to encrypt the data. So I assume CDK users not required to pass this explicitly.

That's correct, this is not explicitly needed since AES256 is the default.

The API could be simpler - if a user wants to use KMS encryption, they just provide a KMS key, otherwise it defaults to S3-managed encryption (AES-256).

Addressed this in the other comment -- we would like to keep our interface extensible for the future if we add more encryption support, and keep the interface in-line with the Tables API & resource defs.

That said, the behavior you described (use kms key if provided) is still supported by the current interface

Comment on lines +92 to +96
// If no key is provided, one will be created automatically
const encryptedBucketAuto = new TableBucket(scope, 'EncryptedTableBucketAuto', {
tableBucketName: 'table-bucket-2',
encryption: TableBucketEncryption.KMS,
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to elaborate this in the readme about CDK creating the new KMS key if it is not provided when KMS encryption type is selected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify what additional information we can include in the README?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For eg:

When using KMS encryption (`TableBucketEncryption.KMS`), if no encryption key is provided, CDK will automatically create a new KMS key for the table bucket with necessary permissions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elaborated in the latest commit. Thanks for the suggestion!

Comment on lines 675 to 686
encryptionKey.addToResourcePolicy(new iam.PolicyStatement({
sid: 'AllowS3TablesMaintenanceAccess',
effect: iam.Effect.ALLOW,
principals: [
new iam.ServicePrincipal('maintenance.s3tables.amazonaws.com'),
],
actions: [
'kms:GenerateDataKey',
'kms:Decrypt',
],
resources: ['*'],
}));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Can we scope down the resource to specific table arn instead of using wildcard?
  2. Can we able to use grant method to provide access to the encryption key?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Unfortunately this is not possible because of circular dependencies. The TableBucket construct depends on the creation of the KMS key to be passed in as the encryptionKey parameter. If we include a policy to scope down to the table bucket ARN, the KMS key will take a dependency on the table bucket which has not been created at this point.
  2. The KMS key grant methods don't have an option to provide this combination of policies (See here), so it made more sense to create the IAM policy here directly for the specific actions that are needed.

@godwingrs22 godwingrs22 added the needs-security-review Related to feature or issues that needs security review label May 7, 2025
godwingrs22
godwingrs22 previously approved these changes May 14, 2025
Copy link
Member

@godwingrs22 godwingrs22 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @xuxey for the contribution. LGTM.

Copy link
Contributor

mergify bot commented May 14, 2025

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

Copy link
Contributor

mergify bot commented May 14, 2025

This pull request has been removed from the queue for the following reason: pull request branch update failed.

The pull request can't be updated.

You should update or rebase your pull request manually. If you do, this pull request will automatically be requeued once the queue conditions match again.
If you think this was a flaky issue, you can requeue the pull request, without updating it, by posting a @mergifyio requeue comment.

@mergify mergify bot dismissed godwingrs22’s stale review May 14, 2025 20:47

Pull request has been modified.

@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildv2Project1C6BFA3F-wQm2hXv2jqQv
  • Commit ID: eb1fddf
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

Copy link
Contributor

mergify bot commented May 14, 2025

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@mergify mergify bot merged commit ac0e4ca into aws:main May 14, 2025
17 of 19 checks passed
Copy link
Contributor

Comments on closed issues and PRs are hard for our team to see.
If you need help, please open a new issue that references this one.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 14, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK needs-security-review Related to feature or issues that needs security review p1 pr/needs-maintainer-review This PR needs a review from a Core Team Member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants