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:....