I am trying to create a trail in Cloudtrail logging to S3 bucket but I am getting following error:
Error: Error creating CloudTrail: InsufficientEncryptionPolicyException: Insufficient permissions to access S3 bucket $BUCKET_NAME or KMS key arn:aws:kms:eu-west-1:$ACCOUNT_ID:key/1234567890.
Terraform is able to create all resources except trail. For some reason I am able to build this manually in the console. Unfortunately even if I copy all permissions generated by AWS it doesn’t work with Terraform. I found other people having the same issues but no proper solution was given.
resource "aws_cloudtrail" "TRAIL" {
name = "TRAIL"
cloud_watch_logs_role_arn = aws_iam_role.cloudtrail-cloudwatch-events-role.arn
cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.loggroup.arn}:*"
enable_log_file_validation = "false"
enable_logging = "true"
is_multi_region_trail = "false"
kms_key_id = aws_kms_key.cloudtrail-logs-kms-key.arn
s3_bucket_name = aws_s3_bucket.BUCKET_NAME.id
}
cloudtrail-cloudwatch-events-role role:
resource "aws_iam_role" "cloudtrail-cloudwatch-events-role" {
name = "cloudtrail-cloudwatch-events-role"
path = "/"
assume_role_policy = data.aws_iam_policy_document.assume-policy.json
}
data "aws_iam_policy_document" "assume-policy" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["cloudtrail.amazonaws.com"]
}
}
}
S3 bucket configuration:
resource "aws_s3_bucket" "BUCKET_NAME" {
bucket = "BUCKET_NAME"
acl = "private"
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
kms_master_key_id = aws_kms_key.cloudtrail-logs-kms-key.id
sse_algorithm = "aws:kms"
}
}
}
versioning {
enabled = false
mfa_delete = false
}
}
resource "aws_s3_bucket_public_access_block" "BUCKET_NAME-access" {
bucket = aws_s3_bucket.BUCKET_NAME.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = false
restrict_public_buckets = false
}
resource "aws_s3_bucket_policy" "logs" {
bucket = aws_s3_bucket.BUCKET_NAME.id
policy = file("${path.module}/cloudtrail-s3-policy.json")
}
cloudtrail-s3-policy.json:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Allow Cloudtrail to access S3 bucket",
"Effect": "Allow",
"Principal": {
"Service": [
"cloudtrail.amazonaws.com",
"config.amazonaws.com"
]
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::BUCKET_NAME"
},
{
"Sid": "AWS cloudtrail global",
"Effect": "Allow",
"Principal": {
"Service": [
"cloudtrail.amazonaws.com",
"config.amazonaws.com"
]
},
"Action": "s3:*",
"Resource": "arn:aws:s3:::BUCKET_NAME/*"
},
{
"Sid": "Allow Cloudtrail to write in S3 bucket",
"Effect": "Allow",
"Principal": {
"Service": [
"cloudtrail.amazonaws.com",
"config.amazonaws.com"
]
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::BUCKET_NAME/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control",
"AWS:SourceArn": "arn:aws:cloudtrail:eu-west-1:ACCOUNT_ID:trail/TRAIL"
}
}
}
]
}
KMS configuration:
resource "aws_kms_key" "cloudtrail-logs-kms-key" {
key_usage = "ENCRYPT_DECRYPT"
enable_key_rotation = false
policy = templatefile("${path.module}/cloudtrail-logs-kms-key.json",{ account_id = data.aws_caller_identity.current.account_id })
}
resource "aws_kms_alias" "kms-alias-logs" {
name = "alias/logs"
target_key_id = aws_kms_key.cloudtrail-logs-kms-key.id
}
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${account_id}:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow CloudTrail to encrypt logs",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "kms:GenerateDataKey*",
"Resource": "*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudtrail:eu-west-1:${account_id}:trail/*"
},
"StringLike": {
"kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:${account_id}:trail/*"
}
}
},
{
"Sid": "Allow CloudTrail to describe key",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "kms:DescribeKey",
"Resource": "*"
},
{
"Sid": "Allow principals in the account to decrypt log files",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"kms:Decrypt",
"kms:ReEncryptFrom"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:CallerAccount": "${account_id}"
},
"StringLike": {
"kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:${account_id}:trail/*"
}
}
},
{
"Sid": "Allow alias creation during setup",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "kms:CreateAlias",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:ViaService": "ec2.eu-west-1.amazonaws.com",
"kms:CallerAccount": "${account_id}"
}
}
},
{
"Sid": "Enable cross account log decryption",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"kms:Decrypt",
"kms:ReEncryptFrom"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:CallerAccount": "${account_id}"
},
"StringLike": {
"kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:${account_id}:trail/*"
}
}
},
{
"Sid": "Access Logs to decrypt logs",
"Effect": "Allow",
"Principal": {
"Service": "logs.eu-west-1.amazonaws.com"
},
"Action": [
"kms:Encrypt*",
"kms:Decrypt*",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:Describe*"
],
"Resource": "*"
}
]
}
2
Answers
Try change from
to
I’ve just been battling the same issue with a similar approach. Following the Terraform CloudTrail example, a target path is defined for the S3 bucket: either
AWSLogs
or<prefix>/AWSLogs
, if you defines3_key_prefix
in the cloudtrail resource.My generated S3 bucket was empty; so I tried adding an AWSLogs folder there encrypted by the same specified KMS key (I hadn’t set a prefix, but that would need to be the top level directory name if you do). The Terraform apply passed for me after that. I hope that helps!
Edit to add:
There seems to be an order of operations issue too. The first Terraform apply to our production environment failed, but then passed on re-running without additional changes.
Adding the folder with TF: