-
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
aws-cdk-lib: Replica Secrets with Replica KMS keys #29819
Comments
Thank you for bringing this up. This is a very great use case. I guess the code should be like: stack.ts export class PrimaryRegionStack extends Stack {
readonly primaryKmsKeyArn: string;
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);
// create the kms key
const defaultKmsKey = new kms.CfnKey(
this,
'defaultProjectSecretsKMSKey',
{
description: 'KMS key for the secret secret',
multiRegion: true,
keyPolicy: generateKeyPolicy(this),
},
)
this.primaryKmsKeyArn = defaultKmsKey.attrArn;
new secrets.Secret(this, 'defaultProjectSecrets', {
// random secretName,
// secretName: 'mysecretsecret',
encryptionKey: kms.Key.fromKeyArn(
this,
'defaultKmsKeyAsKey',
defaultKmsKey.attrArn,
),
replicaRegions: [{ region: 'us-west-2' }],
})
}
}
export interface SecondaryRegionStackProps extends StackProps {
readonly primaryKmsKeyArn: string;
}
export class SecondaryRegionStack extends Stack {
constructor(scope: Construct, id: string, props: SecondaryRegionStackProps) {
super(scope, id, props);
const replicaKey = new kms.CfnReplicaKey(this, 'ReplicaKey', {
description: 'Replicated KMS key for the secret secret',
primaryKeyArn: props.primaryKmsKeyArn,
keyPolicy: generateKeyPolicy(this),
})
}
} app.ts const app = new App();
const env = { region: process.env.CDK_DEFAULT_REGION, account: process.env.CDK_DEFAULT_ACCOUNT };
const uw2env = { region: 'us-west-2', account: process.env.CDK_DEFAULT_ACCOUNT };
const primaryStack = new PrimaryRegionStack(app, 'primary-stack', { env });
new SecondaryRegionStack(app, 'secondary-stack', {
env: uw2env,
primaryKmsKeyArn: primaryStack.primaryKmsKeyArn,
crossRegionReferences: true,
}); But I didn't make it to deploy as some permissions are still missing but I believe this should be a correct pattern. I'll update here when I figure it out. |
yeah @pahud the primary issue I was having was the need to present an If there was a method on |
Though I do see you're passing in |
Check my provided app.ts above:
This implicitly ensure the dependencies that primary stack would always be deployed first followed by the secondary. You can just deploy them with |
ISecret could be a reference of an existing Secret created outsides cloudformation and CDK/CFN generally can't modify the existing resource created out of CFN so I would doubt ISecret can attach the KMS key. Are you able to deploy it using my sample above? |
I'm not sure, I can try to add |
The function generateKeyPolicy(scope: Construct) {
// return a dummy policy(do not use in production)
const masterRegion = 'us-east-1';
const secondaryRegion = 'us-west-2';
const cfnExecRoleMaster = iam.Role.fromRoleName(scope, 'cfn-exec-role-m', `cdk-hnb659fds-cfn-exec-role-${Aws.ACCOUNT_ID}-${masterRegion}`);
const cfnExecRoleSecondary = iam.Role.fromRoleName(scope, 'cfn-exec-role-s', `cdk-hnb659fds-cfn-exec-role-${Aws.ACCOUNT_ID}-${secondaryRegion}`);
const adminRole = iam.Role.fromRoleName(scope, 'AdminRole', 'AdminRole');
return new iam.PolicyDocument({
statements: [
// see https://docs.aws.amazon.com/secretsmanager/latest/userguide/security-encryption.html#security-encryption-authz
// basic read-only permissions for the account
new iam.PolicyStatement({
principals: [ new iam.AccountRootPrincipal() ],
actions: [
'kms:Describe*',
'kms:Get*',
],
resources: ['*'],
}),
// for cfn-exec-role only
new iam.PolicyStatement({
principals: [
cfnExecRoleMaster,
cfnExecRoleSecondary,
],
actions: [
'kms:Describe*',
'kms:GenerateDataKey',
'kms:ReplicateKey',
'kms:Decrypt',
],
resources: ['*'],
}),
// secondary region should have replicateKey permission
new iam.PolicyStatement({
principals: [
cfnExecRoleSecondary,
],
actions: [
'kms:ReplicateKey',
],
resources: ['*'],
}),
// required for admin permission
new iam.PolicyStatement({
principals: [
adminRole,
cfnExecRoleMaster,
cfnExecRoleSecondary,
],
actions: [
'kms:Create*',
'kms:Describe*',
'kms:Enable*',
'kms:List*',
'kms:Put*',
'kms:Update*',
'kms:Revoke*',
'kms:Disable*',
'kms:Get*',
'kms:Delete*',
'kms:TagResource',
'kms:UntagResource',
'kms:ScheduleKeyDeletion',
'kms:CancelKeyDeletion'
],
resources: ['*'],
})
]
})
} |
But i'm not sure how I get the KMS replica key back from us-west-2 into us-east-1 to attach to the
|
Oh if you don't use |
If I have this:
Don't I still need to somehow export the KMS key arn for the CfnReplicaKey out of the "west stack" and import it back into the east stack? This would put a dependency that the West stack must be deployed before the east stack, correct? But if my secrets AND kms keys are created in this same stack that would never work? I feel like without something really hacky there's just no way you can make this work in cdk just simply due to the disparity of how KMS and Secrets are replicated in different fashions (one being replicated from primary into secondary and one being created in secondary referencing an ARN from primary). |
If you are deploying with CloudFormation deployment actions from the pipeline then you can control the deployment sequence from the pipeline. For example
The challenge is how to pass the keyArn between the two deployments. Off the top of my head we have two options:
If you do not Another alternative option is using CodeBuild as your deploy action from the pipeline and run |
Not sure if you are on cdk.dev slack. Feel free to ping me on that slack if you need more ad-hoc discussion with me. |
I can handle it that way, but this means you MUST break your stacks apart and deploy KMS keys before secrets in order for the replica key to be present at the time when replica regions are populated. Is that correct? |
Will crossRegionReferences: true allow me to import a KMS key via ARN from another region? I'm still confused as to how you get the IKey object to provide in replica regions |
I am facing the same issue: I managed to create a secret replicated in two regions and the associated KMS key in the corresponding regions. However, the secret replicas are encrypted using the default encryption key, having the alias 'aws/secretsmanager'. In order to update the encryption key, would it work to create an |
Unfortunatelly it seems that the update secret call has to be performed in the primary region. Trying in the secondary region it gives the following error: The only other option that I see is to create an AWS custom resource in the primary region that performs a |
The second approach works! I had to perform two deployments:
|
@dvd-88 this is an option, but a custom resource for this seems a bit absurd. Especially just for the added complexity for an already complex scenario. Likely this is just going to take some collaboration between the service teams for KMS/SecretsManager. There really shouldn't be this disparity between services imo |
@justin-masse I agree with you that it should be easier to create replicated secrets encrypted with customer-managed keys. However, it is really hard for me to find a "simple" solution to the circular dependency issue in CDK: the main stack containing the secret to be replicated depends on the secondary stacks that contain the customer-managed key replicas and of course, these stacks depend on the main one since the customer-managed key it has to exists before you create the replicas. |
@dvd-88 yeah I don't know of a simple solution for this. The idea that some AWS services replicate from primary -> secondary and others replicate secondary -> from primary just really makes this frustrating. This just happens to be the one weird scenario where this difference in replication collides due to these intermingling with KMS + Secrets. I'm just trying to save time for future engineers (as i'm aware of this limitation now and it won't suck days of my time away anymore). |
Describe the bug
Unable to attach CfnReplicaKey to replicated secrets in the ReplicaRegion[] because you cannot get an IKey for this resource across regions. This means replicated secrets that use KMS keys have to manually be updated in console after replica region deploys in order to attach the replica KMS key (or any KMS key I believe).
This is an inconsistency because of the way SecretsManager has decided to run all of it's logic from the "primary region" whereas KMS runs it's replication logic from the "secondary regions". This inconsistency between services is what leads to this issue.
Expected Behavior
Be able to attach a KMS key (specifically a replica key) when it is created in secondary regions to a secret replicated into that region.
Current Behavior
Sharing secrets across accounts, created a secret in us-east-1 with a defined KMS key and then replicated it into us-west-2. I replicated my KMS key as well but have no way to attach this back to the ReplicaRegions[] in my code.
Example of secret created in us-east-1: (defaultKmsKey obj
In my secondary regions I am doing this:
The problem here is that I cannot even attach this CfnReplicaKey back to my secret in replica regions because when I synth/deploy in that region the
fromKeyArn()
would fail since it's in a different region.Reproduction Steps
ReplicaRegion[] is expecting an IKey for encryptionKey prop but you cannot get an IKey because the KMS key you want to attach exists in another region and would fail to import via
fromKeyArn()
and even if it worked would be dirty.Possible Solution
Allow a call on the Secret class from "replicated regions" that will allow you to update/attach a KMS key. Instead of declaring this @ create time.
Additional Information/Context
No response
CDK CLI Version
2.126
Framework Version
No response
Node.js Version
18
OS
macos
Language
TypeScript
Language Version
No response
Other information
No response
The text was updated successfully, but these errors were encountered: