skip to Main Content

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


  1. Try change from

            {
                "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/*"
                    }
                }
            },
    

    to

            {
                "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/TRAIL"
                    },
                    "StringLike": {
                        "kms:EncryptionContext:aws:cloudtrail:arn": "arn:aws:cloudtrail:*:${account_id}:trail/*"
                    }
                }
            },
    
    Login or Signup to reply.
  2. 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 define s3_key_prefix in the cloudtrail resource.

            {
            "Sid": "AWSCloudTrailWrite",
            "Effect": "Allow",
            "Principal": {
              "Service": "cloudtrail.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": "${aws_s3_bucket.foo.arn}/prefix/AWSLogs/${data.aws_caller_identity.current.account_id}/*",
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control"
                }
            }
        
    

    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:

    resource "aws_s3_bucket_object" "base_log_folder" {
      bucket       = aws_s3_bucket.BUCKET.id
      acl          = "private"
      key          = "AWSLogs/"
      content_type = "application/x-directory"
      kms_key_id   = aws_kms_key.KEY.arn
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search