# 🤖 Account hardening: an all-region CloudTrail with log-file validation, # writing to its own locked-down bucket (90-day retention — enough to # investigate, not an unbounded cost). Management events only, which keeps # the trail in the free tier. resource "aws_s3_bucket" "trail_logs" { bucket = "eleven-cloudtrail-${local.account_id}" } resource "aws_s3_bucket_public_access_block" "trail_logs" { bucket = aws_s3_bucket.trail_logs.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } resource "aws_s3_bucket_lifecycle_configuration" "trail_logs" { bucket = aws_s3_bucket.trail_logs.id rule { id = "expire-90d" status = "Enabled" filter {} expiration { days = 90 } } } data "aws_iam_policy_document" "trail_logs" { statement { sid = "CloudTrailAclCheck" actions = ["s3:GetBucketAcl"] resources = [aws_s3_bucket.trail_logs.arn] principals { type = "Service" identifiers = ["cloudtrail.amazonaws.com"] } } statement { sid = "CloudTrailWrite" actions = ["s3:PutObject"] resources = ["${aws_s3_bucket.trail_logs.arn}/AWSLogs/${local.account_id}/*"] principals { type = "Service" identifiers = ["cloudtrail.amazonaws.com"] } condition { test = "StringEquals" variable = "s3:x-amz-acl" values = ["bucket-owner-full-control"] } } # TLS-only: deny any request arriving over plain HTTP (CloudTrail's own # delivery is HTTPS; this just makes it policy). statement { sid = "DenyInsecureTransport" effect = "Deny" actions = ["s3:*"] resources = [ aws_s3_bucket.trail_logs.arn, "${aws_s3_bucket.trail_logs.arn}/*", ] principals { type = "*" identifiers = ["*"] } condition { test = "Bool" variable = "aws:SecureTransport" values = ["false"] } } } resource "aws_s3_bucket_policy" "trail_logs" { bucket = aws_s3_bucket.trail_logs.id policy = data.aws_iam_policy_document.trail_logs.json } resource "aws_cloudtrail" "main" { name = "eleven-account-trail" s3_bucket_name = aws_s3_bucket.trail_logs.id is_multi_region_trail = true include_global_service_events = true enable_log_file_validation = true depends_on = [aws_s3_bucket_policy.trail_logs] }