Aws-cdk: [@aws-cdk/aws-secretsmanager] Grant decrypt for CMK of imported secret

Created on 3 Sep 2020  路  3Comments  路  Source: aws/aws-cdk

Permissions to decrypt key of imported secret encrypted with CMK are not granted. If creating the key within the stack permissions are granted. Permissions are granted in the resource policy for Secrets Manager and the role and in role's attached policies. In this case, there are no policies created that give the role access.

Reproduction Steps

import * as iam from '@aws-cdk/aws-iam';
import * as kms from '@aws-cdk/aws-kms';
import * as sm from '@aws-cdk/aws-secretsmanager';
import * as cdk from '@aws-cdk/core';

class Stack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const key = kms.Key.fromKeyArn(
      this,
      'MyKey',
      'arn:aws:kms:ap-southeast-2:123412341234:key/1234abcd-12ab-34cd-56ef-1234567890ab',
    );
    const secret = sm.Secret.fromSecretAttributes(this, 'MySecret', {
      secretArn: 'arn:aws:secretsmanager:ap-southeast-2:123412341234:secret:mysecret-ASDzxc',
      encryptionKey: key,
    });

    const role = new iam.Role(this, 'MyRole', {
      assumedBy: new iam.AccountPrincipal('123412341234'),
    });
    secret.grantRead(role);
  }
}

const app = new cdk.App();
new Stack(app, 'MyStack');

What did you expect to happen?

Permissions to decrypt associated encryption key are still granted to the role.

What actually happened?

Permission to decrypt is not granted. Allowed actions are only to get secret value.

Environment

  • CLI Version : 1.59.0
  • Framework Version: 1.61.0
  • Node.js Version: v14.9.0
  • OS : macOS 10.13.3
  • Language (Version): TypeScript (3.8.3)

Other


This is :bug: Bug Report

@aws-cdaws-secretsmanager bug efformedium p2

Most helpful comment

Thanks for the work-around @mitch-dm!

The issue here is that the ViaServicePrincipal doesn't support the addToPrincipalPolicy method.

https://github.com/aws/aws-cdk/blob/1260d5215d474d6edc2460ffe9658552d17ab239/packages/@aws-cdk/aws-kms/lib/via-service-principal.ts#L6

The secret.grantRead attempts to grant decrypt permissions to the role via the service principal. When the key is not imported, this ends up adding the permissions to the key policy. When the key is imported, that's impossible and the only option is to add the decrypt permissions to the principal, which in this case is the ViaServicePrincipal. The ViaServicePrincipal _could_ delegate to the underlying principal (Role) and add a condition to the statement; a quick test proves this out. However, this has some fairly broad implications that need to be investigated and addressed before we can adopt it as a solution. This would likely change the permissions of many existing stacks, so care needs to be taken to ensure this is an acceptable and backwards-compatible change, or if another less intrusive solution is available.

All 3 comments

Here is a simple workaround:

secret.grantRead(role);
secret.encryptionKey?.grantDecrypt(role);

I see that when using your workaround that calls grantDecrypt(), the role gets an additional policy statement with the kms:Decrypt permission in the synthesized cloudformation output. In some initial testing in the AWS console I found that I was still able to read the secret values (even when assuming a role without the kms:Decrypt permission) if the secret was encrypted with a default key, but if I encrypted with a CMK then I could not read the secret value - this matches up with the AWS documentation here.

So I'm inclined to agree that the grantRead API should be adding the kms:Decrypt permission in order to be consistent with the rest of the construct's usage and CDK Secrets Manager README. (Either that, or the CDK docs should be updated).

Thanks for the work-around @mitch-dm!

The issue here is that the ViaServicePrincipal doesn't support the addToPrincipalPolicy method.

https://github.com/aws/aws-cdk/blob/1260d5215d474d6edc2460ffe9658552d17ab239/packages/@aws-cdk/aws-kms/lib/via-service-principal.ts#L6

The secret.grantRead attempts to grant decrypt permissions to the role via the service principal. When the key is not imported, this ends up adding the permissions to the key policy. When the key is imported, that's impossible and the only option is to add the decrypt permissions to the principal, which in this case is the ViaServicePrincipal. The ViaServicePrincipal _could_ delegate to the underlying principal (Role) and add a condition to the statement; a quick test proves this out. However, this has some fairly broad implications that need to be investigated and addressed before we can adopt it as a solution. This would likely change the permissions of many existing stacks, so care needs to be taken to ensure this is an acceptable and backwards-compatible change, or if another less intrusive solution is available.

Was this page helpful?
0 / 5 - 0 ratings