Encrypt CloudTrail logs with multi-region Key with Terraform
Sujay Pillai
Posted on March 23, 2022
Who did what, where and when? in my AWS account(s) through the AWS Management Console, AWS Command Line Interface, and AWS SDKs and APIs is what AWS CloudTrail answerable to you as account owner. It enables auditing, security monitoring, operational troubleshooting, records user activity and API usage across AWS services as Events. These events can be viewed from the Event History
page in the AWS CloudTrail console and are available for up to 90 days after they occur.
CloudTrail records three types of events:
- Management events capturing control plane actions on resources such as creating or deleting Amazon Simple Storage Service (Amazon S3) buckets.
- Data events capturing data plane actions within a resource, such as reading or writing an Amazon S3 object.
- Insight events shows unusual API activities in your AWS account compared to historical API usage.
By default, CloudTrail event log files are encrypted using Amazon S3 server-side encryption (SSE). You can also choose to encrypt your log files with an AWS KMS key. In the previous blog we saw how to build a multi-region key using terraform. We will make use of the same MRK to encrypt the CloudTrail log files and store it in an S3 bucket here.
Resource: aws_cloudtrail
is used to create a trail for your account/organization.
resource "aws_cloudtrail" "default" {
name = var.trailName
s3_bucket_name = var.trailBucket
is_organization_trail = true
is_multi_region_trail = true
include_global_service_events = true
kms_key_id = aws_kms_key.primary.arn
depends_on = [
aws_s3_bucket.cloudtrailbucket,
aws_kms_key.primary
]
}
As you can see we are enabling the trail for multi-region and at organization level, which you can do it for a single-region and single account too.
For the CloudTrail to write the log-files it needs an S3 bucket and for the same reason we have depends_on
added into the resource block to create the S3 bucket first.
Resource: aws_s3_bucket
is used to create an S3 bucket using terraform.
resource "aws_s3_bucket" "cloudtrailbucket" {
bucket = var.trailBucket
depends_on = [
aws_kms_key.primary
]
force_destroy = true
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
kms_master_key_id = aws_kms_key.primary.id
sse_algorithm = "aws:kms"
}
bucket_key_enabled = "false"
}
}
object_lock_configuration {
object_lock_enabled = "Enabled"
}
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": [
"arn:aws:s3:::${var.trailBucket}"
]
},
{
"Sid": "AWSCloudTrailWriteAccount",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::${var.trailBucket}/AWSLogs/${data.aws_caller_identity.current.account_id}/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control",
"AWS:SourceArn" : "arn:aws:cloudtrail:ap-southeast-1:${data.aws_caller_identity.current.account_id}:trail/${var.trailName}"
}
}
},
{
"Sid" : "AWSCloudTrailWriteOrganization",
"Effect" : "Allow",
"Principal" : {
"Service" : "cloudtrail.amazonaws.com"
},
"Action" : "s3:PutObject",
"Resource" : "arn:aws:s3:::${var.trailBucket}/AWSLogs/${data.aws_organizations_organization.myorg.id}/*",
"Condition" : {
"StringEquals" : {
"s3:x-amz-acl" : "bucket-owner-full-control",
"AWS:SourceArn" : "arn:aws:cloudtrail:ap-southeast-1:${data.aws_caller_identity.current.account_id}:trail/${var.trailName}"
}
}
}
]
}
POLICY
}
If you just add the first statement in the bucket policy and configure your cloudtrail to write to this bucket you can see how CloudTrail logs would be beneficial in getting some valuable information. The below screenshot shows how the CloudTrail service reported InsufficientS3BucketPolicyException
error while trying to create trail.
Also the CloudTrail would need explicit permission to use the KMS key to encrypt logs on behalf of specific accounts. If KMS key policy is not correctly configured for CloudTrail, CloudTrail cannot deliver logs. The IAM global condition key aws:SourceArn
helps ensure that CloudTrail uses the KMS key only for this specific organizational trail what we are configuring. We would have to update the KMS policy for the key that we previously created with the below statement:
statement {
sid = "Allow CloudTrail to encrypt logs"
effect = "Allow"
actions = ["kms:GenerateDataKey*"]
resources = ["*"]
principals {
type = "Service"
identifiers = ["cloudtrail.amazonaws.com"]
}
condition {
test = "StringLike"
variable = "kms:EncryptionContext:aws:cloudtrail:arn"
values = ["arn:aws:cloudtrail:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:trail/${var.trailName}"]
}
condition {
test = "StringEquals"
variable = "aws:SourceArn"
values = ["arn:aws:cloudtrail:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:trail/${var.trailName}"]
}
}
If you run the terraform apply
command with all the above configuration you will have the Trail created as shown below:
The terraform source code for above setup can be found here
Posted on March 23, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.