-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
feat(rds): default DatabaseCluser and DatabaseInstance storageEncrypted
to true
(under feature flag)
#32695
base: main
Are you sure you want to change the base?
Conversation
… storageEncrypted
@@ -35,6 +35,8 @@ class TestStack extends cdk.Stack { | |||
vpc, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change affected a lot of integration tests. I opted to use the legacy behaviors for these tests because re-running the creation of all these databases would be extremely slow and expensive.
If someone at AWS with a "free"/"expensed" account would like to rerun all these failing tests, we could go that route, too.
I added explicit new tests for this behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think manually adding "StorageEncrypted": true
to the templates would make those tests pass without the need to deploy the related resources (we expect the flag to work given that you added the new test).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the past, I've received PR feedback that you should never manually update snapshot files. I just confirmed that I could add this property manually to the snapshots and that would be an alternative solution to updating the stack definitions.
I'd like a +1 from an approver that this would be OK before I spend the time doing it, based on previous feedback that snapshots should never be manually edited.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've received PR feedback that you should never manually update snapshot files
👍 Sounds good!
About the isStorageLegacyUnencrypted
comment.
Thanks for clarifying, I'm not sure why CFN defaults to unencrypted clusters and instances.
However, I left some comments on the implementation for small adjustments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pull request linter has failed. See the aws-cdk-automation comment below for failure reasons. If you believe this pull request should receive an exemption, please comment and provide a justification.
A comment requesting an exemption should contain the text Exemption Request
. Additionally, if clarification is needed add Clarification Request
to a comment.
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #32695 +/- ##
=======================================
Coverage 82.21% 82.21%
=======================================
Files 119 119
Lines 6876 6876
Branches 1162 1162
=======================================
Hits 5653 5653
Misses 1120 1120
Partials 103 103
Flags with carried forward coverage won't be shown. Click here to find out more.
|
✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To provide backward compatibility, this PR also introduces a new property, isStorageLegacyUnencrypted. This allows leaving the StorageEncrypted property unset in the CloudFormation template, to avoid replacing Database instances/clusters.
Shouldn't the default behavior be applied when @aws-cdk/aws-rds:enableEncryptionAtRestByDefault: false
? 🤔
The extra variable seems to add unnecessary complexity here.
@@ -35,6 +35,8 @@ class TestStack extends cdk.Stack { | |||
vpc, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think manually adding "StorageEncrypted": true
to the templates would make those tests pass without the need to deploy the related resources (we expect the flag to work given that you added the new test).
@lpizzinidev - thanks for the review. I commented on your inline comment, and below is a comment that you requested changed for (it's annoying that GitHub won't let you respond inline to those). Suggestion to reuse feature flag vs
|
…ryption-by-default
@lpizzinidev - thanks for the thorough review and great comments. This is ready for re-review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks 🚀 Left comments for some final adjustments
@lpizzinidev - thanks! should be good to go. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks 👍
…ryption-by-default
14795d0
to
bddf5b9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @blimmer for raising this issue and for this contribution! I have left few comments to confirm my understanding. I also suggested few modifications around the feature flag. Looking forward to your thoughts on the suggested changes!
// If a KMS key is provided, enable encryption at rest | ||
if (storageEncryptionKey) { | ||
return true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, when storageEncryptionKey
is provided, the code forces storageEncrypted
to true
, regardless of what value the user explicitly set for storageEncrypted
. This means even if a user specifically sets storageEncrypted: false
, the encryption is still enabled, which seems to contradict the purpose of the storageEncrypted
property.
In that case do we still need the same behaviour in the new approach? Would it be better to validate and throw error with clear message (when feature flag is enabled). Something like:
// Validation: Cannot disable encryption while providing an encryption key
if (storageEncryptionKey && storageEncrypted === false) {
throw new Error(
'Cannot set storageEncrypted to false when storageEncryptionKey is provided. ' +
'Either enable encryption or remove the storageEncryptionKey.'
);
}
Please correct me if my understanding is wrong.
if (storageEncryptedLegacyDefaultValue) { | ||
if (storageEncrypted) { | ||
throw new ValidationError('Cannot set `storageEncrypted` to `true` when `storageEncryptedLegacyDefaultValue` is `true`.', scope); | ||
} | ||
|
||
return undefined; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't fully understood the usage of this new property. Could you help me to explain further with different scenarios?
* @default - If the `@aws-cdk/aws-rds:enableEncryptionAtRestByDefault` feature flag is set, true by default or if storageEncryptionKey is provided. If the flag is not set, undefined by default unless storageEncryptionKey is provided. | ||
*/ | ||
readonly storageEncrypted?: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'm not quite clear on the default behaviour based on reading the doc string. Would be nice to rephrase it.
* | ||
* The legacy behavior left the `StorageEncrypted` CloudFormation property unset by default. This resulted in | ||
* cluster storage being unencrypted. The new behavior always sets the `StorageEncrypted` property to `true` or | ||
* `false` for clarity. However, CloudFormation will replace the Cluster if you change `StorageEncrypted` from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new behavior always sets the
StorageEncrypted
property totrue
orfalse
for clarity.
I don't think it is true based on the method implementation. I could see it may return undefined
if the storageEncryptedLegacyDefaultValue
is true
and storageEncrypted
is undefined
or false
.
export function getStorageEncryptedProperty( | ||
scope: IConstruct, | ||
storageEncrypted?: boolean, | ||
storageEncryptedLegacyDefaultValue?: boolean, | ||
storageEncryptionKey?: kms.IKey, | ||
): boolean | undefined { | ||
const featureFlagEnabled = FeatureFlags.of(scope).isEnabled(cxapi.RDS_ENABLE_ENCRYPTION_AT_REST_BY_DEFAULT); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please correct me if I understood the requirement wrongly. We want to make storageEncrypted
default to true which will enable encryption at rest. If storageEncryptionKey
is provided, then will use the provided kms key; if not, will use the default AWS managed key for encryption. If customer explicitly set storageEncrypted
as false
, then encryption will be disabled.
- If feature flag is false (existing behaviour):
Existing CDK projects will have default feature flag as false. This preserves backward compatibility:
// No explicit settings - remains undefined
new DatabaseCluster(this, 'Cluster', {});
// With encryption key - forces true
new DatabaseCluster(this, 'Cluster', {
storageEncryptionKey: key
});
// With encryption key - forces true, ignores storageEncrypted value provided by customer
new DatabaseCluster(this, 'Cluster', {
storageEncrypted: false,
storageEncryptionKey: key
});
//Explicit false
new DatabaseCluster(this, 'Cluster', {
storageEncrypted: false
});
// If storageEncrypted is true and no encryption key specified, uses default aws managed key
new DatabaseCluster(this, 'Cluster', {
storageEncrypted: true
});
- If feature flag is true (new behaviour)
New CDK projects will have the feature flag enabled by default, making storageEncrypted: true
the default:
// No explicit settings - defaults to true
new DatabaseCluster(this, 'Cluster', {}); // storageEncrypted: true and uses default AWS managed key for encryption
// Explicit false is respected
new DatabaseCluster(this, 'Cluster', {
storageEncrypted: false
}); // storageEncrypted: false
// Uses provided encryption key
new DatabaseCluster(this, 'Cluster2', {
storageEncryptionKey: key // default storageEncrypted is true and takes the user provided key for encryption
});
// Explicit encryption configuration
new DatabaseCluster(this, 'Cluster1', {
storageEncrypted: true, //explicit true
storageEncryptionKey: key // Enables encryption with user provided key
});
// Invalid configuration - will throw error
new DatabaseCluster(this, 'Cluster', {
storageEncrypted: false,
storageEncryptionKey: key
}); // Error: Cannot set storageEncrypted to false when storageEncryptionKey is provided
If the above requirements are true, i think the following code might work without the need for additional property.
export function getStorageEncrypted(
scope: IConstruct,
storageEncrypted?: boolean,
storageEncryptionKey?: kms.IKey,
): boolean | undefined {
const featureFlagEnabled = FeatureFlags.of(scope).isEnabled(cxapi.RDS_ENABLE_ENCRYPTION_AT_REST_BY_DEFAULT);
// Feature flag is OFF - maintain existing behavior
if (!featureFlagEnabled) {
return storageEncryptionKey ? true : storageEncrypted;
}
// Feature flag is ON - new behavior
// Validation: Cannot disable encryption while providing an encryption key
if (storageEncryptionKey && storageEncrypted === false) {
throw new Error(
'Cannot set storageEncrypted to false when storageEncryptionKey is provided. ' +
'Either enable encryption or remove the storageEncryptionKey.'
);
}
if (storageEncrypted !== undefined) {
return storageEncrypted;
}
// Default to true when feature flag is ON and no explicit value provided
return true;
}
Let me know if this makes sense or if I've misunderstood any of the requirements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @godwingrs22 - the extra property is required when CDK v3 is released and the feature flag is removed. That's because the legacy default behavior leaves the storageEncrypted
property undefined
in the template. In effect, this means "unencrypted".
Without the extra property, when the feature flag is removed, you'd think that users could do this:
new DatabaseCluster(this, 'Cluster', {
storageEncrypted: false
});
However, changing the StorageEncrypted
property from undefined
to false
reports that the cluster/instance will be replaced. We do not want to force our users to replace their cluster/instance when the feature flag is removed.
Therefore, I think we have to provide a way to retain the legacy behavior of not setting the StorageEncryption
property at all essentially forever to prevent replacing existing databases that used the old pre-feature-flag behavior.
Does that make sense why I think we need this extra property?
See also #32695 (comment) where I discussed this above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @blimmer , Thanks for the clarification. Yes it makes sense to have the new property to preserve the legacy behaviour if the feature flag is removed in future.
…ryption-by-default
AWS CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @blimmer, Thanks for the clarification. LGTM. I'll internally check with RDS service team once to see if there is any feedback from them on enabling the encryption at rest by default.
export function getStorageEncryptedProperty( | ||
scope: IConstruct, | ||
storageEncrypted?: boolean, | ||
storageEncryptedLegacyDefaultValue?: boolean, | ||
storageEncryptionKey?: kms.IKey, | ||
): boolean | undefined { | ||
const featureFlagEnabled = FeatureFlags.of(scope).isEnabled(cxapi.RDS_ENABLE_ENCRYPTION_AT_REST_BY_DEFAULT); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @blimmer , Thanks for the clarification. Yes it makes sense to have the new property to preserve the legacy behaviour if the feature flag is removed in future.
OK, I will pause for your response before fixing up the PR to prep for final review / merge. |
Issue # (if applicable)
Closes #32398
Reason for this change
Today, if you don't remember to pass
storageEncrypted: true
to yourDatabaseInstance
orDatabaseCluster
, your database's storage will be unencrypted.In almost all cases, your database storage should be encrypted. Here are reasons I think we should use AWS-managed keys by default:
Description of changes
This PR introduces a new feature flag,
@aws-cdk/aws-rds:enableEncryptionAtRestByDefault
. When set totrue
,storageEncrypted
will default totrue
instead ofundefined
(which means "false").To provide backward compatibility, this PR also introduces a new property,
storageEncryptedLegacyDefaultValue
. This allows leaving theStorageEncrypted
property unset in the CloudFormation template, to avoid replacing Database instances/clusters.Describe any new or updated permissions being added
N/A
Description of how you validated changes
Unit and integration tests.
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license