CloudTrail Logs S3 Bucket Access Logging Enabled
Overview
This check verifies that S3 server access logging is enabled on the bucket that stores your CloudTrail logs. When enabled, S3 server access logging records every request made to your CloudTrail bucket, including who accessed it, when, and what they did.
Risk
Without access logging on your CloudTrail bucket, you have no visibility into who is accessing your audit logs. This creates serious security gaps:
- Attackers could read your CloudTrail logs to learn about your infrastructure without detection
- Someone could delete or modify logs to cover their tracks, and you would not know
- Unauthorized access to sensitive audit data goes unnoticed
- Incident response becomes much harder because you cannot trace log access patterns
- Compliance requirements for audit log protection may not be met
Think of it this way: CloudTrail logs record what happens in your AWS account, but without S3 access logging, nobody is watching who looks at those records.
Remediation Steps
Prerequisites
You need:
- AWS Console access with permissions to modify S3 bucket settings
- A separate S3 bucket to receive the access logs (you should not log to the same bucket)
Required IAM permissions (for administrators)
Your IAM user or role needs these permissions:
s3:GetBucketLoggings3:PutBucketLoggings3:CreateBucket(if creating a new logging bucket)s3:PutBucketPolicycloudtrail:DescribeTrails
AWS Console Method
Step 1: Find your CloudTrail S3 bucket
- Go to CloudTrail Console in us-east-1
- Click Trails in the left sidebar
- Click on your trail name
- Note the S3 bucket name shown in the trail details (this is the bucket you need to configure)
Step 2: Create a logging destination bucket (if needed)
If you do not already have a bucket for access logs:
- Go to S3 Console
- Click Create bucket
- Enter a name like
my-cloudtrail-access-logs-<account-id> - Select us-east-1 as the region
- Keep Block all public access enabled (recommended)
- Click Create bucket
Step 3: Enable server access logging on the CloudTrail bucket
- In the S3 Console, click on your CloudTrail bucket name
- Go to the Properties tab
- Scroll down to Server access logging
- Click Edit
- Select Enable
- For Target bucket, choose your logging destination bucket
- For Target prefix, enter something like
cloudtrail-access-logs/(optional but helpful for organization) - Click Save changes
That's it! S3 will now log all access to your CloudTrail bucket.
AWS CLI (optional)
Step 1: Identify your CloudTrail S3 bucket
aws cloudtrail describe-trails \
--region us-east-1 \
--query 'trailList[*].{Name:Name,S3BucketName:S3BucketName}'
Note the S3BucketName value for your trail.
Step 2: Create a logging destination bucket (if needed)
aws s3api create-bucket \
--bucket my-cloudtrail-access-logs-<your-account-id> \
--region us-east-1
Note: For regions other than us-east-1, you would need to add --create-bucket-configuration LocationConstraint=<region>.
Step 3: Grant S3 logging service permissions
The S3 logging service needs permission to write to your target bucket. Create a bucket policy:
cat > /tmp/logging-bucket-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3ServerAccessLogsPolicy",
"Effect": "Allow",
"Principal": {
"Service": "logging.s3.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::my-cloudtrail-access-logs-<your-account-id>/cloudtrail-access-logs/*",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "<your-account-id>"
}
}
}
]
}
EOF
Apply the policy:
aws s3api put-bucket-policy \
--bucket my-cloudtrail-access-logs-<your-account-id> \
--policy file:///tmp/logging-bucket-policy.json
Step 4: Enable server access logging
aws s3api put-bucket-logging \
--bucket <your-cloudtrail-bucket> \
--bucket-logging-status '{
"LoggingEnabled": {
"TargetBucket": "my-cloudtrail-access-logs-<your-account-id>",
"TargetPrefix": "cloudtrail-access-logs/"
}
}' \
--region us-east-1
Replace:
<your-cloudtrail-bucket>with your CloudTrail bucket name<your-account-id>with your AWS account ID
CloudFormation (optional)
This template creates a logging bucket with appropriate policies and enables server access logging on an existing CloudTrail bucket:
AWSTemplateFormatVersion: '2010-09-09'
Description: Enable S3 server access logging for CloudTrail bucket
Parameters:
CloudTrailBucketName:
Type: String
Description: Name of the existing S3 bucket that stores CloudTrail logs
LoggingBucketName:
Type: String
Description: Name for the new bucket that will store access logs
Resources:
AccessLogsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref LoggingBucketName
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
AccessLogsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref AccessLogsBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: S3ServerAccessLogsPolicy
Effect: Allow
Principal:
Service: logging.s3.amazonaws.com
Action: s3:PutObject
Resource: !Sub '${AccessLogsBucket.Arn}/cloudtrail-access-logs/*'
Condition:
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
Outputs:
LoggingBucketName:
Description: Name of the access logs bucket
Value: !Ref AccessLogsBucket
LoggingBucketArn:
Description: ARN of the access logs bucket
Value: !GetAtt AccessLogsBucket.Arn
NextSteps:
Description: Manual step required
Value: !Sub |
After deploying this stack, enable server access logging on your CloudTrail bucket
(${CloudTrailBucketName}) using the console or CLI, pointing to ${LoggingBucketName}
with prefix 'cloudtrail-access-logs/'.
Note: CloudFormation cannot directly enable server access logging on an existing bucket. After deploying this stack, you need to enable logging via the console or CLI as described above.
Deploy with:
aws cloudformation deploy \
--template-file cloudtrail-access-logging.yaml \
--stack-name cloudtrail-access-logging \
--parameter-overrides \
CloudTrailBucketName=my-cloudtrail-bucket \
LoggingBucketName=my-cloudtrail-access-logs \
--region us-east-1
Terraform (optional)
# Variables
variable "cloudtrail_bucket_name" {
description = "Name of the existing S3 bucket that stores CloudTrail logs"
type = string
}
variable "logging_bucket_name" {
description = "Name for the bucket that will store access logs"
type = string
}
# Data source for current account
data "aws_caller_identity" "current" {}
# Create the access logs bucket
resource "aws_s3_bucket" "access_logs" {
bucket = var.logging_bucket_name
}
# Block public access to the logging bucket
resource "aws_s3_bucket_public_access_block" "access_logs" {
bucket = aws_s3_bucket.access_logs.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# Enable encryption on the logging bucket
resource "aws_s3_bucket_server_side_encryption_configuration" "access_logs" {
bucket = aws_s3_bucket.access_logs.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
# Bucket policy for S3 logging service
resource "aws_s3_bucket_policy" "access_logs" {
bucket = aws_s3_bucket.access_logs.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "S3ServerAccessLogsPolicy"
Effect = "Allow"
Principal = {
Service = "logging.s3.amazonaws.com"
}
Action = "s3:PutObject"
Resource = "${aws_s3_bucket.access_logs.arn}/cloudtrail-access-logs/*"
Condition = {
StringEquals = {
"aws:SourceAccount" = data.aws_caller_identity.current.account_id
}
}
}
]
})
}
# Reference the existing CloudTrail bucket
data "aws_s3_bucket" "cloudtrail" {
bucket = var.cloudtrail_bucket_name
}
# Enable server access logging on the CloudTrail bucket
resource "aws_s3_bucket_logging" "cloudtrail" {
bucket = data.aws_s3_bucket.cloudtrail.id
target_bucket = aws_s3_bucket.access_logs.id
target_prefix = "cloudtrail-access-logs/"
depends_on = [aws_s3_bucket_policy.access_logs]
}
# Outputs
output "logging_bucket_name" {
description = "Name of the access logs bucket"
value = aws_s3_bucket.access_logs.id
}
output "logging_bucket_arn" {
description = "ARN of the access logs bucket"
value = aws_s3_bucket.access_logs.arn
}
Deploy with:
terraform init
terraform plan \
-var="cloudtrail_bucket_name=my-cloudtrail-bucket" \
-var="logging_bucket_name=my-cloudtrail-access-logs"
terraform apply \
-var="cloudtrail_bucket_name=my-cloudtrail-bucket" \
-var="logging_bucket_name=my-cloudtrail-access-logs"
Verification
After enabling server access logging, verify it is working:
-
In the AWS Console:
- Go to S3 and click on your CloudTrail bucket
- Go to the Properties tab
- Scroll to Server access logging and confirm it shows Enabled
- Check that the target bucket and prefix are correct
-
Wait and check for logs:
- Access your CloudTrail bucket (list objects, download a file)
- Wait 10-15 minutes (S3 access logs are delivered on a best-effort basis)
- Check your logging bucket for new log files
CLI verification commands
Check if logging is enabled on your CloudTrail bucket:
aws s3api get-bucket-logging \
--bucket <your-cloudtrail-bucket> \
--region us-east-1
Expected output (logging enabled):
{
"LoggingEnabled": {
"TargetBucket": "my-cloudtrail-access-logs",
"TargetPrefix": "cloudtrail-access-logs/"
}
}
If the output is empty {}, logging is not enabled.
Check for log files in the target bucket:
aws s3 ls s3://my-cloudtrail-access-logs/cloudtrail-access-logs/ \
--region us-east-1
You should see log files appearing after some access activity (note: there may be a delay of up to several hours).
Additional Resources
- AWS Documentation: Logging requests using server access logging
- AWS Documentation: Best practices for Amazon S3
- AWS Security Hub: CloudTrail Controls
- AWS Documentation: CloudTrail best practices
Notes
- Log delivery timing: S3 server access logs are delivered on a best-effort basis. It can take several hours for logs to appear, and some requests may not be logged.
- Do not log to the same bucket: Never configure a bucket to log to itself. This creates an infinite loop of log entries and can result in unexpected charges.
- Storage costs: Access logs can accumulate quickly if your CloudTrail bucket is frequently accessed. Consider setting up a lifecycle policy on your logging bucket to delete old logs after a retention period.
- Log format: S3 access logs are delivered as space-delimited text files. You may want to set up Athena or another tool to query them efficiently.
- Encryption: Consider enabling encryption on your logging bucket to protect the access log data at rest.
- Versioning: Enable versioning on your logging bucket to protect against accidental deletion of access logs.
- Object Lock: For highly sensitive environments, consider enabling S3 Object Lock on the logging bucket to prevent any modification or deletion of access logs.