Skip to main content

Ddb Customer KMS Keys Remediation

Triage and Remediation

Remediation

Using Console

Below are step‑by‑step console instructions to ensure DynamoDB tables use a customer‑managed KMS key (CMK) instead of the default AWS owned key.


1. Create (or Identify) a KMS CMK

  1. Sign in to the AWS Management Console.
  2. Go to AWS Key Management Service (KMS):
    • In the search bar, type KMS and open Key Management Service.
  3. In the left pane, choose Customer managed keys.
  4. Click Create key.
  5. Configure the key:
    • Key type: Symmetric.
    • Key usage: Encrypt and decrypt.
    • Click Next.
  6. Set Alias (e.g., alias/dynamodb-table-kms-key), optional description.
  7. Configure Key administrators and Key users:
    • Make sure the IAM roles/users that manage/operate DynamoDB have permission to use this key.
  8. Finish the wizard:
    • Review and click Finish (or Create key).

You now have a CMK that can be used by DynamoDB.


2. Update an Existing DynamoDB Table to Use the CMK

  1. Go to the DynamoDB console.
  2. In the left pane, click Tables.
  3. Click on the table you want to remediate.
  4. In the table’s page, choose the Additional settings / Encryption tab (exact label may vary slightly).
  5. Under Encryption at rest:
    • If it shows AWS owned CMK, click Edit.
  6. Select Customer managed key.
  7. In the dropdown, choose your CMK (e.g., alias/dynamodb-table-kms-key).
  8. Click Save changes / Update.

The table’s encryption at rest will now use your CMK. Repeat for each non‑compliant table.


3. Ensure New Tables Use CMKs by Default

There’s no single global default per service via console, so enforce this via process or templates:

  • When creating any new table in the DynamoDB console:

    1. Click Create table.
    2. In the Table settings / Additional settings section, find Encryption at rest.
    3. Choose Customer managed key.
    4. Select the CMK you created.
    5. Complete table creation.
  • Optionally, enforce via:

    • Standard CloudFormation/Terraform templates that specify SSESpecification with a KMS key.
    • IAM policies that restrict use of AWS owned keys for DynamoDB (advanced/optional).

These steps will remediate the finding by ensuring DynamoDB tables use a KMS CMK for encryption at rest.

Using CLI

Below are the CLI steps to ensure a DynamoDB table uses a customer-managed KMS key (CMK) for encryption.

1. Identify the table and region

Decide which table(s) to fix and the AWS Region (e.g., us-east-1).

TABLE_NAME="my-dynamodb-table"
REGION="us-east-1"

2. Create (or choose) a KMS CMK

2.1 Create a new CMK (if you don’t already have one)

aws kms create-key \
--description "CMK for DynamoDB encryption" \
--key-usage ENCRYPT_DECRYPT \
--origin AWS_KMS \
--region $REGION

Note the KeyId in the output.

Optionally, give it an alias:

CMK_KEY_ID="<KeyId-from-previous-command>"

aws kms create-alias \
--alias-name "alias/dynamodb-cmk" \
--target-key-id "$CMK_KEY_ID" \
--region $REGION

You can also reuse an existing CMK or alias, e.g. alias/dynamodb-cmk.


3. Update the DynamoDB table to use the CMK

Use update-table and specify server-side encryption with KMS and your CMK (either KeyId or alias):

aws dynamodb update-table \
--table-name "$TABLE_NAME" \
--region "$REGION" \
--sse-specification Enabled=true,SSEType=KMS,KMSMasterKeyId="alias/dynamodb-cmk"

4. Verify encryption settings

aws dynamodb describe-table \
--table-name "$TABLE_NAME" \
--region "$REGION" \
--query "Table.SSEDescription"

You should see something like:

  • Status: ENABLED
  • SSEType: KMS
  • KMSMasterKeyArn: your CMK ARN

Repeat steps 3–4 for each non-compliant table.

Using Python

Below is a practical, step‑by‑step approach using Python (boto3) to ensure DynamoDB tables are encrypted with a customer-managed KMS key (CMK).


1. Prerequisites

  • Python 3.x
  • boto3 installed:
    pip install boto3
  • IAM permissions for:
    • dynamodb:DescribeTable, dynamodb:UpdateTable
    • kms:DescribeKey
  • A KMS CMK ARN you want to use (or create one in KMS console and copy its ARN).

2. Understand What You Need to Change

You want each DynamoDB table to have:

  • SSEEnabled = True
  • SSEType = "KMS"
  • KMSMasterKeyId = "<your-cmk-arn>"

This is done with UpdateTable and the SSESpecification parameter.


3. Python: Update a Single Table to Use CMK

import boto3
from botocore.exceptions import ClientError

region = "us-east-1" # change as needed
table_name = "your-table-name" # change as needed
cmk_arn = "arn:aws:kms:us-east-1:123456789012:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

dynamodb = boto3.client("dynamodb", region_name=region)

def enable_kms_cmk_encryption(table_name, cmk_arn):
try:
# Describe current SSE settings
resp = dynamodb.describe_table(TableName=table_name)
sse_desc = resp["Table"].get("SSEDescription", {})

already_ok = (
sse_desc.get("Status") in ("ENABLED", "UPDATING") and
sse_desc.get("SSEType") == "KMS" and
sse_desc.get("KMSMasterKeyArn") == cmk_arn
)
if already_ok:
print(f"Table {table_name} already uses CMK: {cmk_arn}")
return

print(f"Updating table {table_name} to use CMK: {cmk_arn}")

# Update SSE specification
dynamodb.update_table(
TableName=table_name,
SSESpecification={
"Enabled": True,
"SSEType": "KMS",
"KMSMasterKeyId": cmk_arn,
},
)

print(f"Update initiated for {table_name}. Encryption change is asynchronous.")

except ClientError as e:
print(f"Error updating {table_name}: {e}")

if __name__ == "__main__":
enable_kms_cmk_encryption(table_name, cmk_arn)

4. Python: Bulk Remediation for All Tables in a Region

import boto3
from botocore.exceptions import ClientError

region = "us-east-1" # change as needed
cmk_arn = "arn:aws:kms:us-east-1:123456789012:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

dynamodb = boto3.client("dynamodb", region_name=region)

def list_all_tables():
tables = []
paginator = dynamodb.get_paginator("list_tables")
for page in paginator.paginate():
tables.extend(page.get("TableNames", []))
return tables

def remediate_tables_with_cmk(cmk_arn):
for table_name in list_all_tables():
try:
resp = dynamodb.describe_table(TableName=table_name)
sse_desc = resp["Table"].get("SSEDescription", {})
status = sse_desc.get("Status")
sse_type = sse_desc.get("SSEType")
current_key = sse_desc.get("KMSMasterKeyArn")

# Skip if already using this CMK
if status in ("ENABLED", "UPDATING") and sse_type == "KMS" and current_key == cmk_arn:
print(f"[SKIP] {table_name} already uses CMK: {cmk_arn}")
continue

print(f"[UPDATE] {table_name} -> CMK: {cmk_arn}")
dynamodb.update_table(
TableName=table_name,
SSESpecification={
"Enabled": True,
"SSEType": "KMS",
"KMSMasterKeyId": cmk_arn,
},
)
except ClientError as e:
print(f"[ERROR] {table_name}: {e}")

if __name__ == "__main__":
remediate_tables_with_cmk(cmk_arn)

5. KMS Key Policy Considerations

Ensure the CMK key policy allows DynamoDB to use it. A minimal example statement (add to CMK key policy):

{
"Sid": "AllowDynamoDBUseOfTheKey",
"Effect": "Allow",
"Principal": {
"Service": "dynamodb.amazonaws.com"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
}

Also ensure your IAM principal (user/role running the script) has kms:DescribeKey and, if needed, kms:ListAliases.


If you share any specific error you hit while running this, I can adjust the code or permissions for your case.

Using Terraform
resource "aws_kms_key" "dynamodb_cmk" {
description = "CMK for DynamoDB table encryption"
deletion_window_in_days = 30

# Optional: restrict usage via key policy, tags, etc.
# See AWS KMS docs for hardening guidance.
}

resource "aws_dynamodb_table" "this" {
name = "YOUR_TABLE_NAME" # replace with your table name
billing_mode = "PAY_PER_REQUEST"

hash_key = "PARTITION_KEY_NAME" # replace with your partition key
attribute {
name = "PARTITION_KEY_NAME" # replace with your partition key
type = "S" # adjust type as needed
}

server_side_encryption {
enabled = true
kms_key_arn = aws_kms_key.dynamodb_cmk.arn
}

# ...any other existing arguments (tags, range key, GSIs, LSIs, etc.)
}

Changing server_side_encryption to use a CMK is an in-place update for existing DynamoDB tables and does not force replacement.

To verify, terraform plan should show an in-place update (~) on the aws_dynamodb_table resource adding/updating the server_side_encryption block with enabled = true and kms_key_arn = arn:aws:kms:....