DataFirehose Delivery Streams Should Be Encrypted at Rest
Overview
This check verifies that Amazon Data Firehose delivery streams have server-side encryption enabled. Firehose is a streaming data service that captures, transforms, and delivers data to destinations like S3, Redshift, or OpenSearch. Encrypting data at rest protects it from unauthorized access if the underlying storage is ever compromised.
Risk
Without encryption at rest, data in your Firehose delivery streams is vulnerable to unauthorized access. If an attacker gains access to the storage layer (through disk access, snapshots, or misconfigured destinations), they could read sensitive information. This increases the risk of:
- Data breaches exposing confidential information
- Non-compliance with security regulations (SOC 2, ISO 27001, etc.)
- Loss of customer trust
- Reduced auditability and key management control
Remediation Steps
Prerequisites
You need permission to modify Firehose delivery streams in your AWS account. For customer-managed encryption keys, you also need permission to create or use KMS keys.
Required IAM permissions
To enable encryption, your IAM user or role needs these permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"firehose:StartDeliveryStreamEncryption",
"firehose:DescribeDeliveryStream",
"firehose:UpdateDestination"
],
"Resource": "arn:aws:firehose:*:*:deliverystream/*"
},
{
"Effect": "Allow",
"Action": [
"kms:CreateGrant",
"kms:DescribeKey"
],
"Resource": "arn:aws:kms:*:*:key/*"
}
]
}
AWS Console Method
- Open the Amazon Data Firehose console
- Select the delivery stream you want to encrypt
- Click Edit
- Scroll to the Server-side encryption section
- Toggle encryption to Enabled
- Choose your encryption key:
- AWS owned key - Simplest option, no additional setup required
- Customer managed key - Recommended for production; provides more control over key rotation and access
- Click Save changes
Important: Server-side encryption can only be enabled on streams that use DirectPut as their source. If your stream sources data from Kinesis Data Streams or MSK, you must encrypt the source instead.
AWS CLI (optional)
Enable encryption with AWS-owned key (simplest)
aws firehose start-delivery-stream-encryption \
--delivery-stream-name <your-stream-name> \
--delivery-stream-encryption-configuration-input KeyType=AWS_OWNED_CMK \
--region us-east-1
Enable encryption with customer-managed key (recommended for production)
First, ensure your KMS key policy allows Firehose to use it:
aws firehose start-delivery-stream-encryption \
--delivery-stream-name <your-stream-name> \
--delivery-stream-encryption-configuration-input \
KeyType=CUSTOMER_MANAGED_CMK,KeyARN=arn:aws:kms:us-east-1:123456789012:key/your-key-id \
--region us-east-1
Check encryption status
aws firehose describe-delivery-stream \
--delivery-stream-name <your-stream-name> \
--region us-east-1 \
--query 'DeliveryStreamDescription.DeliveryStreamEncryptionConfiguration'
CloudFormation (optional)
AWSTemplateFormatVersion: '2010-09-09'
Description: Firehose delivery stream with server-side encryption
Parameters:
DeliveryStreamName:
Type: String
Description: Name for the Firehose delivery stream
Default: encrypted-firehose-stream
DestinationBucketArn:
Type: String
Description: ARN of the S3 bucket destination
Resources:
# KMS key for customer-managed encryption (recommended)
FirehoseKMSKey:
Type: AWS::KMS::Key
Properties:
Description: KMS key for Firehose delivery stream encryption
EnableKeyRotation: true
KeyPolicy:
Version: '2012-10-17'
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Action: 'kms:*'
Resource: '*'
- Sid: Allow Firehose to use the key
Effect: Allow
Principal:
Service: firehose.amazonaws.com
Action:
- kms:Encrypt
- kms:Decrypt
- kms:GenerateDataKey
Resource: '*'
FirehoseKMSKeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: !Sub 'alias/${DeliveryStreamName}-key'
TargetKeyId: !Ref FirehoseKMSKey
# IAM role for Firehose
FirehoseDeliveryRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: firehose.amazonaws.com
Action: 'sts:AssumeRole'
Policies:
- PolicyName: FirehoseDeliveryPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:PutObject
- s3:GetBucketLocation
- s3:ListBucket
Resource:
- !Ref DestinationBucketArn
- !Sub '${DestinationBucketArn}/*'
- Effect: Allow
Action:
- kms:Encrypt
- kms:Decrypt
- kms:GenerateDataKey
Resource: !GetAtt FirehoseKMSKey.Arn
# Firehose delivery stream with encryption
EncryptedFirehoseStream:
Type: AWS::KinesisFirehose::DeliveryStream
Properties:
DeliveryStreamName: !Ref DeliveryStreamName
DeliveryStreamType: DirectPut
DeliveryStreamEncryptionConfigurationInput:
KeyType: CUSTOMER_MANAGED_CMK
KeyARN: !GetAtt FirehoseKMSKey.Arn
ExtendedS3DestinationConfiguration:
BucketARN: !Ref DestinationBucketArn
RoleARN: !GetAtt FirehoseDeliveryRole.Arn
BufferingHints:
IntervalInSeconds: 300
SizeInMBs: 5
Outputs:
DeliveryStreamArn:
Description: ARN of the encrypted Firehose delivery stream
Value: !GetAtt EncryptedFirehoseStream.Arn
KMSKeyArn:
Description: ARN of the KMS key used for encryption
Value: !GetAtt FirehoseKMSKey.Arn
Terraform (optional)
Basic encryption with AWS-owned key
resource "aws_kinesis_firehose_delivery_stream" "example" {
name = "example-firehose-stream"
destination = "extended_s3"
server_side_encryption {
enabled = true
# Uses AWS-owned CMK by default
}
extended_s3_configuration {
role_arn = aws_iam_role.firehose_role.arn
bucket_arn = aws_s3_bucket.destination.arn
}
}
Customer-managed key encryption (recommended for production)
resource "aws_kinesis_firehose_delivery_stream" "example" {
name = "example-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_role.arn
bucket_arn = aws_s3_bucket.destination.arn
}
}
resource "aws_kms_key" "firehose" {
description = "KMS key for Firehose encryption"
deletion_window_in_days = 7
enable_key_rotation = true
}
resource "aws_iam_role" "firehose_role" {
name = "firehose-delivery-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "firehose.amazonaws.com"
}
}
]
})
}
Verification
After enabling encryption, verify the change was applied:
- In the Firehose console, select your delivery stream
- Check the Server-side encryption section shows Enabled
- Confirm the encryption key type matches what you configured
CLI verification
aws firehose describe-delivery-stream \
--delivery-stream-name <your-stream-name> \
--region us-east-1 \
--query 'DeliveryStreamDescription.DeliveryStreamEncryptionConfiguration'
Expected output for an encrypted stream:
{
"KeyType": "AWS_OWNED_CMK",
"Status": "ENABLED"
}
Or for customer-managed encryption:
{
"KeyARN": "arn:aws:kms:us-east-1:123456789012:key/your-key-id",
"KeyType": "CUSTOMER_MANAGED_CMK",
"Status": "ENABLED"
}
Additional Resources
Notes
- DirectPut only: Server-side encryption can only be enabled on delivery streams that use DirectPut as their source. Streams sourcing from Kinesis Data Streams or MSK must be encrypted at the source.
- Encryption is asynchronous: After enabling encryption, it may take up to 5 seconds before all records are encrypted. You can check the
Encryptedfield in PutRecord responses to verify. - API limits: You can call
StartDeliveryStreamEncryptionandStopDeliveryStreamEncryptiona combined maximum of 25 times per stream per 24-hour period. - Customer-managed keys: If using a customer-managed CMK, ensure the KMS key policy grants Firehose the
kms:Encrypt,kms:Decrypt, andkms:GenerateDataKeypermissions. Only symmetric KMS keys are supported. - No downtime: Enabling encryption does not interrupt data delivery. You can continue reading and writing to the stream while encryption is being enabled.