KMS: Add a way to get an `IKey` reference from an `Alias` `ARN`
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
Description
### Describe the feature
Currently, there is no way to get a proper `IKey` reference from an `Alias` `ARN`, that would produce a proper **IAM** `PolicyStatement`.
This would be especially useful for cross-account situations where the current available options like `Key.fromLookup` and `Alias.fromAliasName` are not sufficient.
### Use Case
My current use case is creating a **Lambda** `ESM` from an encrypted cross-account **Kinesis** `Stream`.
From the external account, granting the permissions to the `Stream` and `Key` was pretty easy thanks to the existing **CDK** constructs.
On the reader account, however, we originally thought that we could centralize all the cross-account stuff in our custom **Kinesis** L3 `Stream` _construct_, and that the **Lambda** one would just create a `KinesisEventSource` without caring about the `IStream` origin. Sadly, that was not the case since there was no way to get a reference to the external account **KMS** `Alias` that would create a correct **IAM** `PolicyStatement`.
We tried the following **CDK** code:
```typescript
const streamArn = Stack.of(this).formatArn({
account: externalAccountId,
service: 'kinesis',
resource: 'stream',
resourceName: streamName
})
const keyArn = Stack.of(this).formatArn({
account: externalAccountId,
service: 'kms',
resource: 'alias',
resourceName: `${streamName}-key`
})
const stream = Stream.fromStreamAttributes(this, 'external-stream', {
streamArn,
// Sadly, this doesn't produce the correct policy, and we have to explicitly modify the lambda policy.
encryptionKey: Key.fromKeyArn(this, 'external-stream-key', keyArn)
})
```
But that created this **IAM** `PolicyStatement` _(which is wrong)_:
```json
{
"Action": "kms:Decrypt",
"Resource": "arn:aws:kms:::alias/-key",
"Effect": "Allow"
}
```
Thus, we had to expose the cross-account information to the **Lambda** module, and explicitly grant the correct **IAM** `PolicyStatement`.
```typescript
// Workaround for lack of cross-account KMS alias lookup.
lambda.addToRolePolicy(
new PolicyStatement({
actions: ['kms:Decrypt'],
resources: [
Stack.of(this).formatArn({
account: externalAccountId,
service: 'kms',
resource: 'key',
resourceName: '*'
})
],
conditions: {
'ForAnyValue:StringEquals': {
'kms:ResourceAliases': `alias/${streamName}-key`
}
}
})
)
```
-----
Of course, another option would have been to use the external `Key` `ARN` instead of the `Alias` one. However, since `Key`'s `ARN` values are not predictable, and we deploy this same `Stack` in multiple regions in multiple environments, we would need to have a hardcoded map of values. And we believe that is a worse solution since it is brittle and error-prone.
### Proposed Solution
A `static` method like `Key.fromAliasArn` or `Alias.fromArn` that would return an `IKey` reference pointing to that `Alias`, and whose `grant` methods would produce a proper `PolicyStatement` with a `kms:ResourceAliases` condition.
### Other Information
This feels related to #28284 & #8822
And, it is likely to be a recurring problem, since the official **AWS** recommendation is to use multiple accounts, and when sharing data, for example, using **Kinesis** `Streams`, it is a security best practice to encrypt it using **KMS** `Keys`.
### Acknowledgements
- [ ] I may be able to implement this feature request
- [ ] This feature might incur a breaking change
### AWS CDK Library version (aws-cdk-lib)
2.243.0
### AWS CDK CLI version
2.1111.0
### Environment details (OS name and version, etc.)
Any
Contributor guide
Research direction
Start from the KMS entry points Key.fromKeyArn and Alias.fromAliasName, then trace the grant methods used by Stream.fromStreamAttributes and KinesisEventSource. Compare the generated IAM statement with the issue's examples; done means an alias ARN can produce an IKey reference whose grant includes the kms:ResourceAliases condition for the cross-account Kinesis and Lambda case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, typescript
- Domain
- cloud, security
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100