Skip to main content

CloudTrail KMS Encryption Enabled

Overview

This check verifies that your AWS CloudTrail trails use SSE-KMS encryption with customer-managed keys for log files stored in S3. By default, CloudTrail encrypts logs with Amazon S3-managed keys (SSE-S3), but using your own KMS key gives you more control over who can read your audit logs.

Risk

Without customer-managed KMS encryption, your CloudTrail logs rely solely on S3 bucket permissions for protection. This creates security gaps:

  • Weaker access control: Anyone with S3 read permissions can view logs, even if they should not have audit access
  • No key-level auditing: You cannot track who decrypted or accessed log files through KMS
  • Limited separation of duties: Bucket administrators automatically have log access
  • Reduced forensic integrity: Compromised credentials or bucket misconfigurations could expose sensitive API activity
  • Compliance gaps: Many frameworks (CIS, PCI-DSS, HIPAA) recommend or require customer-managed encryption keys

Remediation Steps

Prerequisites

You need:

  • AWS Console access with permissions to modify CloudTrail and create/manage KMS keys
  • An existing CloudTrail trail (or permission to create one)
Required IAM permissions (for administrators)

Your IAM user or role needs these permissions:

  • cloudtrail:UpdateTrail
  • cloudtrail:DescribeTrails
  • kms:CreateKey
  • kms:CreateAlias
  • kms:DescribeKey
  • kms:PutKeyPolicy
  • kms:ListKeys

AWS Console Method

Step 1: Create a KMS Key (if you do not have one)

  1. Open KMS in the AWS Console

  2. Create a new key

    • Click Customer managed keys in the left sidebar
    • Click Create key
    • Choose Symmetric for key type
    • Choose Encrypt and decrypt for key usage
    • Click Next
  3. Add labels

    • Enter an alias like cloudtrail-encryption-key
    • Add a description like "KMS key for CloudTrail log encryption"
    • Click Next
  4. Define key administrators

    • Select the IAM users or roles who can manage (but not use) this key
    • Click Next
  5. Define key usage permissions

    • Select the IAM users or roles who can use this key to encrypt/decrypt
    • Click Next
  6. Review and add CloudTrail permissions

    • Before clicking Finish, you need to add CloudTrail service permissions to the key policy
    • In the key policy JSON, add this statement to the Statement array:
    {
    "Sid": "Allow CloudTrail to encrypt logs",
    "Effect": "Allow",
    "Principal": {
    "Service": "cloudtrail.amazonaws.com"
    },
    "Action": [
    "kms:GenerateDataKey*",
    "kms:DescribeKey"
    ],
    "Resource": "*",
    "Condition": {
    "StringLike": {
    "kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:<your-account-id>:trail/*"
    }
    }
    }
    • Replace <your-account-id> with your 12-digit AWS account ID
    • Click Finish

Step 2: Enable KMS Encryption on Your Trail

  1. Open CloudTrail in the AWS Console

  2. Select your trail

    • Click Trails in the left sidebar
    • Click on the trail name you want to configure
  3. Edit the trail

    • Click Edit in the General details section
  4. Enable SSE-KMS encryption

    • Scroll to Log file SSE-KMS encryption
    • Check the box for Enabled
    • Under Customer managed AWS KMS key, choose Existing
    • Select your KMS key alias (e.g., cloudtrail-encryption-key)
  5. Save your changes

    • Click Save changes
AWS CLI (optional)

Step 1: Create a KMS key with the correct policy

Create a key policy file that grants CloudTrail permission to use the key:

cat > /tmp/cloudtrail-kms-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<your-account-id>:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow CloudTrail to encrypt logs",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": [
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*",
"Condition": {
"StringLike": {
"kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:<your-account-id>:trail/*"
}
}
},
{
"Sid": "Allow CloudTrail to describe key",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "kms:DescribeKey",
"Resource": "*"
}
]
}
EOF

Replace <your-account-id> with your 12-digit AWS account ID.

Create the KMS key:

aws kms create-key \
--description "KMS key for CloudTrail log encryption" \
--policy file:///tmp/cloudtrail-kms-policy.json \
--region us-east-1

Note the KeyId from the output.

Create an alias for easier reference:

aws kms create-alias \
--alias-name alias/cloudtrail-encryption-key \
--target-key-id <key-id> \
--region us-east-1

Step 2: Update the CloudTrail trail

aws cloudtrail update-trail \
--name <trail-name> \
--kms-key-id alias/cloudtrail-encryption-key \
--region us-east-1
CloudFormation (optional)

This template creates a KMS key with the correct policy and a CloudTrail trail that uses it:

AWSTemplateFormatVersion: '2010-09-09'
Description: CloudTrail with KMS encryption

Parameters:
TrailName:
Type: String
Description: Name of the CloudTrail trail
Default: my-encrypted-trail

S3BucketName:
Type: String
Description: S3 bucket for CloudTrail logs

Resources:
CloudTrailKMSKey:
Type: AWS::KMS::Key
Properties:
Description: KMS key for CloudTrail log 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 CloudTrail to encrypt logs
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action:
- kms:GenerateDataKey*
- kms:DescribeKey
Resource: '*'
Condition:
StringLike:
kms:EncryptionContext:aws:cloudtrail:arn: !Sub arn:aws:cloudtrail:*:${AWS::AccountId}:trail/*

CloudTrailKMSKeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: alias/cloudtrail-encryption-key
TargetKeyId: !Ref CloudTrailKMSKey

CloudTrailWithKMS:
Type: AWS::CloudTrail::Trail
Properties:
TrailName: !Ref TrailName
S3BucketName: !Ref S3BucketName
IsLogging: true
KMSKeyId: !GetAtt CloudTrailKMSKey.Arn

Outputs:
KMSKeyArn:
Description: ARN of the KMS key used for CloudTrail encryption
Value: !GetAtt CloudTrailKMSKey.Arn

TrailArn:
Description: ARN of the CloudTrail trail
Value: !GetAtt CloudTrailWithKMS.Arn

Deploy with:

aws cloudformation deploy \
--template-file cloudtrail-kms.yaml \
--stack-name cloudtrail-kms-encryption \
--parameter-overrides TrailName=my-encrypted-trail S3BucketName=my-cloudtrail-bucket \
--region us-east-1
Terraform (optional)
# Variables
variable "trail_name" {
description = "Name of the CloudTrail trail"
type = string
default = "my-encrypted-trail"
}

variable "s3_bucket_name" {
description = "S3 bucket for CloudTrail logs"
type = string
}

# Data source for current account
data "aws_caller_identity" "current" {}

# KMS key for CloudTrail encryption
resource "aws_kms_key" "cloudtrail" {
description = "KMS key for CloudTrail log encryption"
deletion_window_in_days = 30
enable_key_rotation = true

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "Enable IAM User Permissions"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = "kms:*"
Resource = "*"
},
{
Sid = "Allow CloudTrail to encrypt logs"
Effect = "Allow"
Principal = {
Service = "cloudtrail.amazonaws.com"
}
Action = [
"kms:GenerateDataKey*",
"kms:DescribeKey"
]
Resource = "*"
Condition = {
StringLike = {
"kms:EncryptionContext:aws:cloudtrail:arn" = "arn:aws:cloudtrail:*:${data.aws_caller_identity.current.account_id}:trail/*"
}
}
}
]
})
}

# KMS key alias
resource "aws_kms_alias" "cloudtrail" {
name = "alias/cloudtrail-encryption-key"
target_key_id = aws_kms_key.cloudtrail.key_id
}

# CloudTrail trail with KMS encryption
resource "aws_cloudtrail" "main" {
name = var.trail_name
s3_bucket_name = var.s3_bucket_name
kms_key_id = aws_kms_key.cloudtrail.arn
}

# Outputs
output "kms_key_arn" {
description = "ARN of the KMS key used for CloudTrail encryption"
value = aws_kms_key.cloudtrail.arn
}

output "trail_arn" {
description = "ARN of the CloudTrail trail"
value = aws_cloudtrail.main.arn
}

Deploy with:

terraform init
terraform plan -var="trail_name=my-encrypted-trail" -var="s3_bucket_name=my-cloudtrail-bucket"
terraform apply -var="trail_name=my-encrypted-trail" -var="s3_bucket_name=my-cloudtrail-bucket"

Verification

After making changes, verify KMS encryption is enabled:

  1. In the AWS Console:

    • Go to CloudTrail > Trails and select your trail
    • In the General details section, check that Log file SSE-KMS encryption shows Enabled
    • Verify the correct KMS key alias or ARN is displayed
  2. Check a log file:

    • Go to your S3 bucket containing CloudTrail logs
    • Select any log file and view its properties
    • Under Server-side encryption settings, it should show AWS KMS with your key
CLI verification commands

Check trail encryption configuration:

aws cloudtrail describe-trails \
--trail-name-list <trail-name> \
--region us-east-1 \
--query 'trailList[0].{TrailName:Name,KmsKeyId:KmsKeyId}'

The output should show your KMS key ARN in the KmsKeyId field:

{
"TrailName": "my-encrypted-trail",
"KmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

If KmsKeyId is null or missing, KMS encryption is not enabled.

Additional Resources

Notes

  • Existing logs: Enabling KMS encryption only affects new log files. Existing logs remain encrypted with their original method (SSE-S3 or existing KMS key).
  • Key policy is critical: If the KMS key policy does not grant CloudTrail permission to use the key, log delivery will fail. Always include the CloudTrail service principal in your key policy.
  • Cross-account trails: If your trail delivers logs to an S3 bucket in another account, the KMS key policy must also allow the CloudTrail service in the source account.
  • Key rotation: Enable automatic key rotation on your KMS key for better security. AWS rotates the key material annually while keeping the key ID the same.
  • Costs: Customer-managed KMS keys incur charges ($1/month per key plus usage charges). Factor this into your cost planning.
  • Log readers need decrypt permission: Users who need to read CloudTrail logs must have kms:Decrypt permission on the KMS key, in addition to S3 read access.