dynamodb and kms

Control AWS DynamoDB encryption by using a Customer managed customer master key (CMK) to encrypt data at rest using server side encryption (SSE).

Customer managed CMKs can be fully controlled (key rotation, key policies, IAM policies, etc). The examples use CloudFormation to create the CMK, DynamoDB table, and test Lambda.

Charges Apply!

Charges apply so be sure to remove all resources created after experimentation.

TL;DR

  1. Use a Customer managed customer master key (CMK)
  2. Add a condition to the CMK key policy to allow DynamoDB to use it
  3. Configure a DynamoDB table with SSE using the CMK
  4. 1 CMK is ~$1.00 per month + $0.03 per 10,000 requests

Keep it secret, keep it safe

DynamoDB is an AWS managed key-value and document database. Server side encryption is enabled by default and uses an AWS owned customer master key (CMK).

There are three types of CMK that can be used with DynamoDB to encrypt data at rest:

Use a Customer managed CMK for the greatest level of control. There are several advantages to using a Customer managed CMK for DynamoDB SSE.

  • Full control over these keys means fine grained control over key rotation, key and IAM policies, etc
  • Reduces blast radius when using one Customer managed CMK per table

CloudFormation

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:

  DynamoDbTableKmsKey:
    Type: AWS::KMS::Key
    Properties:
      EnableKeyRotation: true
      KeyPolicy:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
            Action: kms:*
            Resource: '*'
          - Effect: Allow
            Principal:
              AWS: '*'
            Action:
              - kms:Encrypt
              - kms:Decrypt
            Resource: '*'
            Condition:
              StringEquals:
                kms:CallerAccount: !Ref AWS::AccountId
                kms:ViaService: !Sub dynamodb.${AWS::Region}.amazonaws.com

  DynamoDbTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH
      BillingMode: PAY_PER_REQUEST
      SSESpecification:
        KMSMasterKeyId: !Ref DynamoDbTableKmsKey
        SSEEnabled: true
        SSEType: KMS

  DynamoDbWriteFunction:
    Type: AWS::Serverless::Function
    Properties:
      InlineCode: !Sub
        - |
          def handler(event, context):
            import boto3
            dynamodb = boto3.client('dynamodb')
            id = event['id']
            return dynamodb.put_item(TableName='${TableName}', Item={'id':{'S':f'{id}'}})
        - TableName: !Ref DynamoDbTable
      Handler: index.handler
      Runtime: python3.7
      Policies:
        Statement:
          - Effect: Allow
            Action:
              - dynamodb:PutItem
            Resource: !GetAtt DynamoDbTable.Arn
  • Specify a CMK key policy condition to allow the DynamoDB service to use the CMK for server side encryption and only from the same account
  • Reference the KMS CMK above when defining the DynamoDB table
  • Use the Lambda for inserting data

Deploying to AWS

aws cloudformation deploy --stack-name dynamodb-sse-with-kms-cmk --template-file sam.yaml --capabilities CAPABILITY_IAM

Invoke the test Lambda using the AWS CLI

aws lambda invoke \
--function-name <lambda name goes here> \
--payload '{ "id": "1" }' \
response.json

Delete the CloudFormation Stack

aws cloudformation delete-stack --stack-name dynamodb-sse-with-kms-cmk

About the author

Hi, I'm Karl Kyck a cloud architect specialising in building sustainable serverless architectures on AWS.