Skip to content

[dynamodb] .grantWriteData() does not grant correct permissions to use PutItem on a KMS CMK encrypted DynamoDB Table. #10010

@mcteo

Description

@mcteo

Hi,

I was trying to give a Lambda PutItem access to a KMS CMK encrypted DynamoDB table, and it was throwing the following error:

[ERROR] ClientError: An error occurred (AccessDeniedException) when calling the PutItem operation: KMS key access denied error: com.amazonaws.services.kms.model.AWSKMSException: The ciphertext refers to a customer master key that does not exist, does not exist in this region, or you are not allowed to access. (Service: AWSKMS; Status Code: 400; Error Code: AccessDeniedException; Request ID: xxxx-xxx-xxx-xxxxxx)

I had a look into the issue, and it seems, the KMS CMK resource policy requires the "kms:Decrypt" permission too, in order to call PutItem successfully. (Only added to the KMS Resource Policy, not added to the IAM Policy attached to the Lambda Execution Role. My fix below adds to both, for simplicity.)

I've attached an example Stack that reproduces the problem. I'd open a PR myself, but the only solution that jumps out to me would be to either just grant "kms:Decrypt" to the role, or split .grantWriteData() into multiple functions.

Thanks

Reproduction Steps

Relevant snippet from package.json

    "@aws-cdk/aws-dynamodb": "^1.61.0",
    "@aws-cdk/aws-iam": "^1.61.0",
    "@aws-cdk/aws-kms": "^1.61.0",
    "@aws-cdk/aws-lambda": "^1.61.0",
    "@aws-cdk/core": "1.61.0",

Example Stack:

import { AttributeType, BillingMode, Table, TableEncryption } from '@aws-cdk/aws-dynamodb'
import { Code, Function, Runtime } from '@aws-cdk/aws-lambda'
import * as cdk from '@aws-cdk/core'
import * as path from 'path'

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

        const table = new Table(this, 'Table', {
            partitionKey: {
                name: 'pk',
                type: AttributeType.STRING,
            },
            billingMode: BillingMode.PAY_PER_REQUEST,
            encryption: TableEncryption.CUSTOMER_MANAGED,
        })

        const lambda = new Function(this, 'Function', {
            runtime: Runtime.PYTHON_3_8,
            code: Code.fromAsset(path.join(__dirname, 'lambda')),
            handler: 'index.main',
            environment: {
                TABLE_NAME: table.tableName,
            },
        })
        table.grantWriteData(lambda)
        
        
        // Without this, is throws an error; with this, it succeeds.
        // if (table.encryptionKey) {
        //     table.encryptionKey.grantDecrypt(lambda)
        // }
    }
}

Lambda code:

import datetime
import os

import boto3


def main(event, context):
    region = os.environ.get("AWS_REGION", "us-west-2")
    table = boto3.resource("dynamodb", region_name=region).Table(os.environ["TABLE_NAME"])

    table.put_item(Item={
        "pk": datetime.datetime.now().isoformat(),
    })

    return {
        "message": "Success!"
    }

What did you expect to happen?

The Lambda should be able to write to the DynamoDB Table.

What actually happened?

Running the Lambda receives the following error:

[ERROR] ClientError: An error occurred (AccessDeniedException) when calling the PutItem operation: KMS key access denied error: com.amazonaws.services.kms.model.AWSKMSException: The ciphertext refers to a customer master key that does not exist, does not exist in this region, or you are not allowed to access. (Service: AWSKMS; Status Code: 400; Error Code: AccessDeniedException; Request ID: xxxx-xxx-xxx-xxxxxx)

Environment

  • CLI Version : 1.61.0 (build 72e6727)
  • Framework Version: 1.61.0
  • Node.js Version: v12.17.0
  • OS : OSX
  • Language (Version): "typescript": "~3.9.7"

This is 🐛 Bug Report

Metadata

Metadata

Assignees

No one assigned

    Labels

    @aws-cdk/aws-dynamodbRelated to Amazon DynamoDBbugThis issue is a bug.effort/mediumMedium work item – several days of effortp1

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions