aws-secretsmanager: grantRead should add Decrypt Policy for kms key
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
Description
### Describe the bug
When I use `fromSecretAttributes` and add a encryptionKey, cdk should automatically add a decrypt policy to the roles default policy when I grant a read access to a role. This is working for example on dynamodb Tables.
```TS
export interface TestStack2Props extends StackProps {
secretArn: string,
secretKeyArn: string,
}
export class TestStack2 extends Stack {
public constructor(scope: Construct, id: string, props: TestStack2Props) {
super(scope,id,props);
const key = Key.fromKeyArn(this, 'key', props.secretKeyArn);
const secret = Secret.fromSecretAttributes(this,'secret',{
secretCompleteArn: props.secretArn,
encryptionKey: key
})
const role = new Role(this,'role',{
assumedBy: new AccountPrincipal('111122223333')
})
secret.grantRead(role);
key.grantDecrypt(role); // <-- should not be necessary
}
}
```
This solution with dynamo db is working well:
```TS
export interface TestStack2Props extends StackProps {
tableName: string,
tableKeyArn: string,
}
export class TestStack2 extends Stack {
public constructor(scope: Construct, id: string, props: TestStack2Props) {
super(scope, id, props);
const key = Key.fromKeyArn(this, 'key', props.tableKeyArn);
const table = Table.fromTableAttributes(this, 'table', {
encryptionKey: key,
tableName: props.tableName
})
const role = new Role(this, 'role', {
assumedBy: new AccountPrincipal('111122223333')
})
table.grantReadData(role);
// key.grantDecrypt(role); // <-- is not necessary
}
}
```
### Expected Behavior
This policy statement should exist in the template
```JSON
{
"Action": "kms:Decrypt",
"Effect": "Allow",
"Resource": {
"Fn::ImportValue": "stack1:ExportsOutputFnGetAttkeyFEDD6EC0Arn6AD68239"
}
}
```
Or other statements depending on the grant action.
### Current Behavior
it does not add a policy statement to allow decryption of the secret using the given kms encryptionKey.
### Reproduction Steps
```TS
import {App, Stack, StackProps} from "aws-cdk-lib";
import {Construct} from "constructs";
import {Secret} from "aws-cdk-lib/aws-secretsmanager";
import {AccountPrincipal, Role} from "aws-cdk-lib/aws-iam";
import {Key} from "aws-cdk-lib/aws-kms";
export class TestStack1 extends Stack {
public secretArn: string;
public secretKeyArn: string;
public constructor(scope?: Construct, id?: string, props?: StackProps) {
super(scope, id, props);
const key = new Key(this, 'key')
this.secretKeyArn = key.keyArn;
const secret = new Secret(this, 'secret', {
encryptionKey: key,
})
this.secretArn = secret.secretArn
}
}
export interface TestStack2Props extends StackProps {
secretArn: string,
secretKeyArn: string,
}
export class TestStack2 extends Stack {
public constructor(scope: Construct, id: string, props: TestStack2Props) {
super(scope, id, props);
const key = Key.fromKeyArn(this, 'key', props.secretKeyArn);
const secret = Secret.fromSecretAttributes(this, 'secret', {
secretCompleteArn: props.secretArn,
encryptionKey: key
})
const role = new Role(this, 'role', {
assumedBy: new AccountPrincipal('111122223333')
})
secret.grantRead(role);
// key.grantDecrypt(role); // <-- should not be necessary
}
}
const app = new App({
outdir: 'cdk.test.out'
})
const stack1 = new TestStack1(app, 'stack1');
new TestStack2(app, 'stack2', {
secretArn: stack1.secretArn,
secretKeyArn: stack1.secretKeyArn,
})
app.synth();
```
### Possible Solution
_No response_
### Additional Information/Context
_No response_
### CDK CLI Version
2.21.1 (build a6ee543)
### Framework Version
_No response_
### Node.js Version
v14.17.6
### OS
Linux Ubuntu 21.10
### Language
Typescript
### Language Version
TypeScript (4.6.3)
### Other information
_No response_
Contributor guide
Research direction
Start by locating the aws-secretsmanager implementation of fromSecretAttributes and grantRead, then compare its behavior with the DynamoDB encryptionKey grant path. Run the provided two-stack TypeScript reproduction and inspect the synthesized template; done means the imported secret's encryption key permissions appear for the granted role without an explicit key.grantDecrypt(role).
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, typescript
- Domain
- cloud, infrastructure, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100