Skip to main content

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:GetBucketLogging
  • s3:PutBucketLogging
  • s3:CreateBucket (if creating a new logging bucket)
  • s3:PutBucketPolicy
  • cloudtrail:DescribeTrails

AWS Console Method

Step 1: Find your CloudTrail S3 bucket

  1. Go to CloudTrail Console in us-east-1
  2. Click Trails in the left sidebar
  3. Click on your trail name
  4. 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:

  1. Go to S3 Console
  2. Click Create bucket
  3. Enter a name like my-cloudtrail-access-logs-<account-id>
  4. Select us-east-1 as the region
  5. Keep Block all public access enabled (recommended)
  6. Click Create bucket

Step 3: Enable server access logging on the CloudTrail bucket

  1. In the S3 Console, click on your CloudTrail bucket name
  2. Go to the Properties tab
  3. Scroll down to Server access logging
  4. Click Edit
  5. Select Enable
  6. For Target bucket, choose your logging destination bucket
  7. For Target prefix, enter something like cloudtrail-access-logs/ (optional but helpful for organization)
  8. 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:

  1. 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
  2. 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

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.