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
- Use a Customer managed customer master key (CMK)
- Add a condition to the CMK key policy to allow DynamoDB to use it
- Configure a DynamoDB table with SSE using the CMK
- 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:
- AWS owned CMK (shared across multiple accounts, cannot be managed, charges do not apply)
- AWS managed CMK (shared across a single account, cannot be managed, charges apply)
- Customer managed CMK (can be fully managed, charges apply)
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