CloudTrail Multi-Region Enabled
Overview
This check verifies that AWS CloudTrail is logging API activity across all AWS regions. CloudTrail records every API call made in your AWS account, but by default, a trail only captures events in the region where it was created. A multi-region trail ensures you have visibility into API activity everywhere in your account.
Risk
Without multi-region logging, attackers can operate undetected in regions where you have no visibility. This creates serious security blind spots:
- Unauthorized resources (like crypto-mining EC2 instances) can be launched in unmonitored regions
- Data exfiltration through S3 buckets or other services in obscure regions goes unnoticed
- Privilege escalation and IAM changes in unmonitored regions leave no audit trail
- Forensic investigations are incomplete when incidents span multiple regions
- Compliance requirements (PCI DSS, HIPAA, SOC 2) typically mandate complete audit coverage
Remediation Steps
Prerequisites
You need:
- AWS Console access with permissions to create or modify CloudTrail trails
- An S3 bucket for storing CloudTrail logs (or permission to create one)
Required IAM permissions (for administrators)
Your IAM user or role needs these permissions:
cloudtrail:CreateTrailcloudtrail:UpdateTrailcloudtrail:DescribeTrailscloudtrail:StartLoggings3:CreateBucket(if creating a new bucket)s3:PutBucketPolicys3:GetBucketPolicy
AWS Console Method
-
Open CloudTrail in the AWS Console
- Go to CloudTrail Console in us-east-1
-
Check existing trails
- Click Trails in the left sidebar
- Look for a trail that shows "Yes" under the Multi-region column
- If you already have one with logging enabled, you're done
-
Create a new multi-region trail (if needed)
- Click Create trail
- Enter a Trail name (e.g.,
organization-trail) - Under Storage location, choose to create a new S3 bucket or use an existing one
- If creating new, enter a bucket name (must be globally unique)
-
Enable multi-region logging
- Under Additional settings, find Trail apply to all regions
- Select Yes to make this a multi-region trail
- Optionally enable Log file validation for tamper detection
-
Configure log events (optional)
- Choose which events to log: Management events, Data events, or both
- Management events are usually sufficient for security monitoring
-
Complete the setup
- Click Create trail
- Verify the trail shows as Logging in the Trails list
Updating an existing trail:
- Click on the trail name
- Click Edit in the General details section
- Change Multi-region trail to Yes
- Click Save changes
AWS CLI (optional)
Create a new multi-region trail
First, create an S3 bucket for CloudTrail logs (replace <your-unique-bucket-name>):
aws s3api create-bucket \
--bucket <your-unique-bucket-name> \
--region us-east-1
Apply the required bucket policy for CloudTrail (replace placeholders):
cat > /tmp/cloudtrail-bucket-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::<your-unique-bucket-name>"
},
{
"Sid": "AWSCloudTrailWrite",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::<your-unique-bucket-name>/AWSLogs/<your-account-id>/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
EOF
aws s3api put-bucket-policy \
--bucket <your-unique-bucket-name> \
--policy file:///tmp/cloudtrail-bucket-policy.json
Create the multi-region trail:
aws cloudtrail create-trail \
--name organization-trail \
--s3-bucket-name <your-unique-bucket-name> \
--is-multi-region-trail \
--enable-log-file-validation \
--region us-east-1
Start logging:
aws cloudtrail start-logging \
--name organization-trail \
--region us-east-1
Update an existing trail to multi-region
aws cloudtrail update-trail \
--name <trail-name> \
--is-multi-region-trail \
--region us-east-1
Ensure logging is enabled:
aws cloudtrail start-logging \
--name <trail-name> \
--region us-east-1
CloudFormation (optional)
This template creates a multi-region CloudTrail trail with an S3 bucket:
AWSTemplateFormatVersion: '2010-09-09'
Description: Multi-region CloudTrail trail with S3 bucket
Parameters:
TrailName:
Type: String
Description: Name for the CloudTrail trail
Default: organization-trail
BucketPrefix:
Type: String
Description: Prefix for the S3 bucket name (account ID will be appended)
Default: cloudtrail-logs
Resources:
CloudTrailBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${BucketPrefix}-${AWS::AccountId}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
CloudTrailBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref CloudTrailBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AWSCloudTrailAclCheck
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:GetBucketAcl
Resource: !GetAtt CloudTrailBucket.Arn
- Sid: AWSCloudTrailWrite
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:PutObject
Resource: !Sub ${CloudTrailBucket.Arn}/AWSLogs/${AWS::AccountId}/*
Condition:
StringEquals:
s3:x-amz-acl: bucket-owner-full-control
MultiRegionTrail:
Type: AWS::CloudTrail::Trail
DependsOn: CloudTrailBucketPolicy
Properties:
TrailName: !Ref TrailName
S3BucketName: !Ref CloudTrailBucket
IsMultiRegionTrail: true
IsLogging: true
EnableLogFileValidation: true
IncludeGlobalServiceEvents: true
Outputs:
TrailArn:
Description: ARN of the CloudTrail trail
Value: !GetAtt MultiRegionTrail.Arn
BucketName:
Description: S3 bucket storing CloudTrail logs
Value: !Ref CloudTrailBucket
Deploy with:
aws cloudformation deploy \
--template-file cloudtrail-multi-region.yaml \
--stack-name cloudtrail-multi-region \
--parameter-overrides TrailName=organization-trail \
--region us-east-1
Terraform (optional)
# Variables
variable "trail_name" {
description = "Name of the CloudTrail trail"
type = string
default = "organization-trail"
}
variable "bucket_prefix" {
description = "Prefix for the S3 bucket name"
type = string
default = "cloudtrail-logs"
}
# Data sources
data "aws_caller_identity" "current" {}
# S3 bucket for CloudTrail logs
resource "aws_s3_bucket" "cloudtrail" {
bucket = "${var.bucket_prefix}-${data.aws_caller_identity.current.account_id}"
}
resource "aws_s3_bucket_versioning" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_public_access_block" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# Bucket policy for CloudTrail
resource "aws_s3_bucket_policy" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AWSCloudTrailAclCheck"
Effect = "Allow"
Principal = {
Service = "cloudtrail.amazonaws.com"
}
Action = "s3:GetBucketAcl"
Resource = aws_s3_bucket.cloudtrail.arn
},
{
Sid = "AWSCloudTrailWrite"
Effect = "Allow"
Principal = {
Service = "cloudtrail.amazonaws.com"
}
Action = "s3:PutObject"
Resource = "${aws_s3_bucket.cloudtrail.arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/*"
Condition = {
StringEquals = {
"s3:x-amz-acl" = "bucket-owner-full-control"
}
}
}
]
})
}
# Multi-region CloudTrail trail
resource "aws_cloudtrail" "main" {
name = var.trail_name
s3_bucket_name = aws_s3_bucket.cloudtrail.id
is_multi_region_trail = true
enable_logging = true
enable_log_file_validation = true
include_global_service_events = true
depends_on = [aws_s3_bucket_policy.cloudtrail]
}
# Outputs
output "trail_arn" {
description = "ARN of the CloudTrail trail"
value = aws_cloudtrail.main.arn
}
output "bucket_name" {
description = "S3 bucket storing CloudTrail logs"
value = aws_s3_bucket.cloudtrail.id
}
Deploy with:
terraform init
terraform plan -var="trail_name=organization-trail"
terraform apply -var="trail_name=organization-trail"
Verification
After making changes, verify multi-region logging is working:
-
In the AWS Console:
- Go to CloudTrail > Trails
- Confirm your trail shows Yes under the Multi-region column
- Click the trail name and verify Logging is ON
-
Test from another region:
- Switch to a different region (e.g., eu-west-1)
- Perform any AWS action (like listing EC2 instances)
- Return to your S3 bucket and verify logs appear for that region
CLI verification commands
List all trails and check multi-region status:
aws cloudtrail describe-trails \
--region us-east-1 \
--query 'trailList[*].{Name:Name,IsMultiRegion:IsMultiRegionTrail,S3Bucket:S3BucketName}'
Check if logging is enabled for a specific trail:
aws cloudtrail get-trail-status \
--name <trail-name> \
--region us-east-1 \
--query '{IsLogging:IsLogging,LatestDeliveryTime:LatestDeliveryTime}'
Expected output for a properly configured trail:
{
"IsLogging": true,
"LatestDeliveryTime": "2024-01-15T10:30:00.000000+00:00"
}
Run the Prowler check again to confirm remediation:
prowler aws --checks cloudtrail_multi_region_enabled
Additional Resources
- AWS Documentation: Creating a Trail
- AWS Documentation: Receiving CloudTrail Log Files from Multiple Regions
- AWS Documentation: CloudTrail Best Practices
- CIS AWS Foundations Benchmark (Control 3.1)
Notes
- One trail is usually enough: A single multi-region trail captures events from all regions. You do not need separate trails per region.
- Global services: Enable "Include global service events" to capture IAM, CloudFront, and other global service events (only in the home region to avoid duplicates).
- Organization trails: If using AWS Organizations, consider creating an organization trail that logs events for all member accounts.
- Costs: CloudTrail charges per 100,000 events after the first management event copy. Multi-region trails may increase costs, but the security benefits typically outweigh this.
- Log file validation: Always enable log file validation to detect if logs have been tampered with.
- Encryption: Consider enabling KMS encryption for CloudTrail logs for additional security (see
cloudtrail_kms_encryption_enabledcheck).