Ensure Kafka Cluster Encryption at Rest Uses Customer Managed Keys (CMK)
Overview
This check verifies that Amazon MSK (Managed Streaming for Apache Kafka) clusters use customer-managed KMS keys (CMK) for encrypting data at rest, rather than AWS-managed keys.
When you create an MSK cluster, AWS encrypts your data at rest by default. However, using a customer-managed key gives you greater control over the encryption process.
Risk
Using AWS-managed encryption keys (instead of customer-managed keys) limits your control over:
- Key rotation: You cannot control when or how often the key is rotated
- Access policies: You cannot define granular policies for who can use the key
- Audit visibility: You lose detailed CloudTrail logging of key usage
- Incident response: You cannot quickly disable a key to "crypto-shred" data during a security incident
For organizations with compliance requirements (C5, ISO27001, KISA-ISMS-P), customer-managed keys may be required.
Remediation Steps
Prerequisites
- AWS account access with permissions to manage MSK clusters and KMS keys
- An existing customer-managed KMS key, or permissions to create one
Important: MSK encryption settings cannot be changed after cluster creation. You must create a new cluster with the correct encryption settings and migrate your data.
AWS Console Method
Step 1: Create a Customer Managed Key (if needed)
- Go to the AWS KMS Console: https://console.aws.amazon.com/kms
- Make sure you are in the us-east-1 region (or your target region)
- Click Create key
- Choose Symmetric key type and Encrypt and decrypt usage
- Click Next
- Enter an alias (e.g.,
msk-encryption-key) and optional description - Click Next
- Select key administrators who can manage the key
- Click Next
- Select users and roles that can use the key (include the role/user that will create MSK clusters)
- Click Next and then Finish
- Copy the key ARN for later use
Step 2: Create a New MSK Cluster with CMK Encryption
- Go to the Amazon MSK Console: https://console.aws.amazon.com/msk
- Click Create cluster
- Choose Custom create for full configuration control
- Enter a cluster name
- Select your Apache Kafka version
- Choose broker instance type and number of brokers
- Configure networking (VPC, subnets, security groups)
- In the Encryption section:
- For Encryption at rest, select Use a customer-managed key
- Choose your CMK from the dropdown (or paste the key ARN)
- Do not select the
alias/aws/kafkakey (this is AWS-managed)
- Configure other settings as needed
- Click Create cluster
Step 3: Migrate from Existing Cluster (if applicable)
If you have an existing cluster using AWS-managed encryption:
- Set up Apache Kafka MirrorMaker or use MSK Replicator to copy data
- Update your applications to point to the new cluster
- Verify data integrity
- Delete the old cluster once migration is complete
AWS CLI (optional)
Create a KMS Key
# Create a customer-managed KMS key
aws kms create-key \
--description "KMS key for MSK cluster encryption" \
--region us-east-1
# Create an alias for easier reference
aws kms create-alias \
--alias-name alias/msk-encryption-key \
--target-key-id <key-id-from-previous-command> \
--region us-east-1
Create MSK Cluster with CMK
# Create the MSK cluster with CMK encryption
aws kafka create-cluster \
--cluster-name "my-secure-cluster" \
--kafka-version "3.5.1" \
--number-of-broker-nodes 3 \
--broker-node-group-info '{
"InstanceType": "kafka.m5.large",
"ClientSubnets": [
"subnet-xxxxxxxxx1",
"subnet-xxxxxxxxx2",
"subnet-xxxxxxxxx3"
],
"SecurityGroups": ["sg-xxxxxxxxx"],
"StorageInfo": {
"EbsStorageInfo": {
"VolumeSize": 100
}
}
}' \
--encryption-info '{
"EncryptionAtRest": {
"DataVolumeKMSKeyId": "arn:aws:kms:us-east-1:123456789012:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
"EncryptionInTransit": {
"ClientBroker": "TLS",
"InCluster": true
}
}' \
--region us-east-1
Replace the placeholder values:
subnet-xxxxxxxxx1,subnet-xxxxxxxxx2,subnet-xxxxxxxxx3: Your VPC subnet IDssg-xxxxxxxxx: Your security group IDarn:aws:kms:...: Your customer-managed KMS key ARN
List Existing Clusters
# List all MSK clusters
aws kafka list-clusters --region us-east-1
Check Cluster Encryption Settings
# Get cluster details including encryption configuration
aws kafka describe-cluster \
--cluster-arn "arn:aws:kafka:us-east-1:123456789012:cluster/my-cluster/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
--query 'ClusterInfo.EncryptionInfo' \
--region us-east-1
CloudFormation (optional)
AWSTemplateFormatVersion: '2010-09-09'
Description: MSK Cluster with Customer Managed Key Encryption
Parameters:
ClusterName:
Type: String
Description: Name of the MSK cluster
KMSKeyArn:
Type: String
Description: ARN of the Customer Managed KMS key
KafkaVersion:
Type: String
Default: '3.5.1'
Description: Apache Kafka version
InstanceType:
Type: String
Default: kafka.m5.large
Description: Instance type for broker nodes
NumberOfBrokerNodes:
Type: Number
Default: 3
Description: Number of broker nodes
Subnet1:
Type: AWS::EC2::Subnet::Id
Description: First subnet for MSK brokers
Subnet2:
Type: AWS::EC2::Subnet::Id
Description: Second subnet for MSK brokers
Subnet3:
Type: AWS::EC2::Subnet::Id
Description: Third subnet for MSK brokers
SecurityGroupId:
Type: AWS::EC2::SecurityGroup::Id
Description: Security group for MSK brokers
Resources:
MSKCluster:
Type: AWS::MSK::Cluster
Properties:
ClusterName: !Ref ClusterName
KafkaVersion: !Ref KafkaVersion
NumberOfBrokerNodes: !Ref NumberOfBrokerNodes
BrokerNodeGroupInfo:
InstanceType: !Ref InstanceType
ClientSubnets:
- !Ref Subnet1
- !Ref Subnet2
- !Ref Subnet3
SecurityGroups:
- !Ref SecurityGroupId
StorageInfo:
EBSStorageInfo:
VolumeSize: 100
EncryptionInfo:
EncryptionAtRest:
DataVolumeKMSKeyId: !Ref KMSKeyArn
EncryptionInTransit:
ClientBroker: TLS
InCluster: true
Outputs:
ClusterArn:
Description: ARN of the MSK cluster
Value: !Ref MSKCluster
Deploy the stack:
aws cloudformation create-stack \
--stack-name msk-secure-cluster \
--template-body file://msk-cluster.yaml \
--parameters \
ParameterKey=ClusterName,ParameterValue=my-secure-cluster \
ParameterKey=KMSKeyArn,ParameterValue=arn:aws:kms:us-east-1:123456789012:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
ParameterKey=Subnet1,ParameterValue=subnet-xxxxxxxxx1 \
ParameterKey=Subnet2,ParameterValue=subnet-xxxxxxxxx2 \
ParameterKey=Subnet3,ParameterValue=subnet-xxxxxxxxx3 \
ParameterKey=SecurityGroupId,ParameterValue=sg-xxxxxxxxx \
--region us-east-1
Terraform (optional)
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
variable "cluster_name" {
description = "Name of the MSK cluster"
type = string
}
variable "kms_key_arn" {
description = "ARN of the Customer Managed KMS key for encryption at rest"
type = string
}
variable "kafka_version" {
description = "Apache Kafka version"
type = string
default = "3.5.1"
}
variable "instance_type" {
description = "Instance type for broker nodes"
type = string
default = "kafka.m5.large"
}
variable "number_of_broker_nodes" {
description = "Number of broker nodes (must be multiple of AZs)"
type = number
default = 3
}
variable "subnet_ids" {
description = "List of subnet IDs for broker nodes"
type = list(string)
}
variable "security_group_ids" {
description = "List of security group IDs for broker nodes"
type = list(string)
}
resource "aws_msk_cluster" "main" {
cluster_name = var.cluster_name
kafka_version = var.kafka_version
number_of_broker_nodes = var.number_of_broker_nodes
broker_node_group_info {
instance_type = var.instance_type
client_subnets = var.subnet_ids
security_groups = var.security_group_ids
storage_info {
ebs_storage_info {
volume_size = 100
}
}
}
encryption_info {
encryption_at_rest_kms_key_arn = var.kms_key_arn
encryption_in_transit {
client_broker = "TLS"
in_cluster = true
}
}
tags = {
Environment = "production"
}
}
output "cluster_arn" {
description = "ARN of the MSK cluster"
value = aws_msk_cluster.main.arn
}
output "bootstrap_brokers_tls" {
description = "TLS connection host:port pairs"
value = aws_msk_cluster.main.bootstrap_brokers_tls
}
Example terraform.tfvars:
cluster_name = "my-secure-cluster"
kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
subnet_ids = ["subnet-xxxxxxxxx1", "subnet-xxxxxxxxx2", "subnet-xxxxxxxxx3"]
security_group_ids = ["sg-xxxxxxxxx"]
Deploy:
terraform init
terraform plan
terraform apply
Verification
After creating your cluster, verify it uses a customer-managed key:
- Go to the Amazon MSK Console
- Click on your cluster name
- In the Security section, check Encryption at rest
- Confirm it shows your customer-managed key ARN (not
alias/aws/kafka)
CLI verification
# Verify encryption configuration
aws kafka describe-cluster \
--cluster-arn "arn:aws:kafka:us-east-1:123456789012:cluster/my-secure-cluster/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
--query 'ClusterInfo.EncryptionInfo.EncryptionAtRest.DataVolumeKMSKeyId' \
--output text \
--region us-east-1
The output should show your customer-managed KMS key ARN, not the AWS-managed key.
Run Prowler to verify:
prowler aws --checks kafka_cluster_encryption_at_rest_uses_cmk
Additional Resources
Notes
- Encryption settings are immutable: You cannot change the encryption key after cluster creation. Plan accordingly.
- MSK Serverless: Serverless clusters automatically pass this check as they use AWS-managed encryption appropriately for the serverless model.
- Key policy requirements: Ensure your KMS key policy grants MSK the necessary permissions (
kms:Encrypt,kms:Decrypt,kms:GenerateDataKey*, etc.) - Cross-account keys: If using a KMS key from another account, additional key policy configuration is required.
- Key rotation: Enable automatic key rotation on your CMK for enhanced security (KMS handles this transparently).
- Cost consideration: Customer-managed KMS keys incur additional charges ($1/month per key plus API usage).