This check flags Kinesis Firehose delivery streams that encrypt data with the default AWS-managed KMS key instead of a customer-managed key (CMK). Switch to a CMK so you control the key policy, rotation, and audit trail. The fix is one start-delivery-stream-encryption call.
Kinesis Data Firehose moves streaming data into destinations like S3, Redshift, OpenSearch, and Splunk. When server-side encryption is enabled on a delivery stream, Firehose encrypts records buffered in the stream before they are delivered. The question this check asks is simple: which key is doing that encryption?
If the answer is the default AWS-managed key (aws/kinesis), Lensix raises this finding. It is not a critical, data-is-exposed emergency, but it does represent a loss of control that matters for compliance, key governance, and incident response.
What this check detects
The kinesis_firehose_defaultkms check inspects each Firehose delivery stream's server-side encryption configuration and reports any stream whose KeyType is set to AWS_OWNED_CMK rather than CUSTOMER_MANAGED_CMK.
In practice there are three states a delivery stream can be in:
- No encryption — server-side encryption is disabled (a separate, more serious problem).
- Default AWS-managed key — encryption is on, but using
aws/kinesis. This is what the check flags. - Customer-managed key (CMK) — encryption uses a KMS key you create and control. This is the passing state.
Note: Firehose server-side encryption only applies to data buffered inside the stream itself. It does not encrypt the data at the destination. S3, Redshift, and OpenSearch each have their own encryption settings that you configure separately. Don't assume a CMK on Firehose covers the whole pipeline.
Why it matters
The default aws/kinesis key gets the job done for encryption at rest, so why bother? Because the key policy and lifecycle are owned by AWS, not you. That difference shows up in several real situations.
You can't restrict who uses the key
With an AWS-managed key, you cannot edit the key policy. Any principal in your account with the right Kinesis and KMS permissions can interact with data protected by that key, and you have no key-level control to narrow that down. A customer-managed key lets you write a key policy that grants decrypt rights only to the specific roles that need them, giving you a second layer of access control beyond IAM alone.
Rotation is on AWS's schedule, not yours
AWS-managed keys rotate automatically every year, and you can't change that cadence. Many compliance frameworks and internal standards expect you to demonstrate ownership of rotation policy. With a CMK you choose annual automatic rotation, an external rotation process, or on-demand rotation after a suspected exposure.
Audit and forensics are harder
CloudTrail records KMS API calls for both key types, but customer-managed keys give you a cleaner, scoped view. When you investigate an incident, being able to point CloudTrail and key grants at a single CMK dedicated to your Firehose pipeline makes the access story far easier to reconstruct than sifting through shared usage of an account-wide managed key.
Warning: Customer-managed keys cost roughly $1 per key per month plus per-request charges for encrypt and decrypt operations. For a high-throughput Firehose stream this is usually negligible, but if you create a separate CMK per stream across hundreds of streams, the request charges add up. Budget for it and consider sharing one CMK across streams that belong to the same trust boundary.
Compliance frameworks expect CMKs
Standards such as PCI DSS, HIPAA, and many internal data classification policies require that encryption keys protecting regulated data be under your direct administrative control. An auditor who sees aws/kinesis on a stream carrying cardholder or health data will flag it, and you'll be remediating under deadline pressure instead of on your own schedule.
How to fix it
Remediation has two parts: create a customer-managed KMS key (if you don't already have a suitable one), then point the delivery stream at it.
Step 1: Create a customer-managed key
aws kms create-key \
--description "CMK for Kinesis Firehose encryption" \
--tags TagKey=purpose,TagValue=firehose-sse \
--query 'KeyMetadata.KeyId' \
--output text
Give it a friendly alias so you can reference it easily later:
aws kms create-alias \
--alias-name alias/firehose-sse \
--target-key-id <key-id-from-previous-step>
Step 2: Scope the key policy
Attach a key policy that allows only the Firehose service and your administrators to use the key. The principal that matters here is the role Firehose assumes, plus the firehose.amazonaws.com service principal for the stream itself.
{
"Version": "2012-10-17",
"Id": "firehose-cmk-policy",
"Statement": [
{
"Sid": "AllowKeyAdministration",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:role/kms-admins" },
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowFirehoseUse",
"Effect": "Allow",
"Principal": { "Service": "firehose.amazonaws.com" },
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "*"
}
]
}
Step 3: Enable encryption with the CMK
Warning: Changing the encryption key triggers a short transition where the stream's encryption status moves to ENABLING. Records continue to flow, but configuration changes are temporarily locked. Run this during a maintenance window if your pipeline is latency-sensitive.
aws firehose start-delivery-stream-encryption \
--delivery-stream-name my-firehose-stream \
--delivery-stream-encryption-configuration-input \
KeyType=CUSTOMER_MANAGED_CMK,KeyARN=arn:aws:kms:us-east-1:111122223333:key/<key-id>
Verify the change took effect:
aws firehose describe-delivery-stream \
--delivery-stream-name my-firehose-stream \
--query 'DeliveryStreamDescription.DeliveryStreamEncryptionConfiguration'
You should see "Status": "ENABLED" and "KeyType": "CUSTOMER_MANAGED_CMK" with your key ARN.
Note: Server-side encryption with a CMK is only supported for Direct PUT delivery streams (where producers call PutRecord or PutRecordBatch directly). If the stream's source is a Kinesis Data Stream, you encrypt the data stream instead, and Firehose inherits that protection.
Fix it in Terraform
If you manage Firehose with infrastructure as code, set the encryption block so the configuration is enforced on every apply.
resource "aws_kms_key" "firehose" {
description = "CMK for Kinesis Firehose"
deletion_window_in_days = 30
enable_key_rotation = true
}
resource "aws_kinesis_firehose_delivery_stream" "this" {
name = "my-firehose-stream"
destination = "extended_s3"
server_side_encryption {
enabled = true
key_type = "CUSTOMER_MANAGED_CMK"
key_arn = aws_kms_key.firehose.arn
}
extended_s3_configuration {
role_arn = aws_iam_role.firehose.arn
bucket_arn = aws_s3_bucket.destination.arn
}
}
Tip: Set enable_key_rotation = true on the KMS key so AWS rotates the backing key material annually without any work from you. You get rotation guarantees and ownership in one line.
How to prevent it from happening again
One-off remediation drifts back over time as new streams get created. Bake the requirement into the path that creates infrastructure.
Block it in CI with policy-as-code
If you use Terraform, a Checkov or OPA policy can fail the pipeline before a non-compliant stream is ever provisioned. Here is a Conftest/OPA rule against a Terraform plan:
package firehose
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_kinesis_firehose_delivery_stream"
sse := resource.change.after.server_side_encryption[_]
sse.enabled == true
sse.key_type != "CUSTOMER_MANAGED_CMK"
msg := sprintf("Firehose stream %q must use a customer-managed KMS key", [resource.name])
}
Detect drift at runtime
Pipelines only cover resources created through pipelines. For everything else, run a scheduled scan. Lensix evaluates kinesis_firehose_defaultkms continuously across your accounts, so a stream created by hand in the console or by a quick fix during an incident gets flagged the same day.
Tip: Pair the scan with an AWS Config custom rule or an EventBridge rule on CreateDeliveryStream calls so you get notified the moment a stream is created without a CMK, instead of waiting for the next audit cycle.
Best practices
- Always enable server-side encryption on Direct PUT Firehose streams. A CMK is better than the default key, but the default key is far better than no encryption at all.
- Use a dedicated CMK per trust boundary. Don't reuse one application key everywhere, and don't create one key per stream blindly. Group streams that share the same data sensitivity and access requirements.
- Scope the key policy tightly. Grant
kms:Decryptandkms:GenerateDataKeyonly to the Firehose service principal and the specific consumer roles that read the delivered data. - Turn on automatic key rotation so you meet rotation requirements without manual effort.
- Encrypt the destination too. Firehose SSE protects buffered data only. Confirm S3 bucket encryption, Redshift encryption, or OpenSearch encryption at rest are also configured.
- Tag your keys with owner and purpose so cost allocation and cleanup stay manageable as the number of CMKs grows.
Danger: Never schedule deletion of a CMK that is still in use by an active delivery stream. Once a key is deleted, any data encrypted with it becomes permanently unrecoverable, and the stream will fail to deliver. Disable the key first, watch for errors, and only schedule deletion after you've confirmed nothing depends on it.
Moving Firehose off the default KMS key is a low-effort, high-clarity improvement. You gain a key policy you control, rotation on your terms, and a cleaner audit trail, all without rearchitecting the pipeline. Make the CMK the default in your IaC modules and this finding stops appearing for good.

