Skip to main content

S3 Bucket Server Access Logging

Overview

This check verifies that your Amazon S3 buckets have server access logging enabled. When enabled, S3 records every request made to a bucket and saves detailed logs to a destination bucket you specify.

Risk

Without access logging, you have no record of who accessed your data, when, or what they did. This makes it nearly impossible to:

  • Detect unauthorized access or data theft
  • Investigate security incidents
  • Meet compliance and audit requirements
  • Understand access patterns for troubleshooting

Think of it like a building without security cameras - if something goes wrong, you have no way to see what happened.

Remediation Steps

Prerequisites

  • Access to the AWS Console with permissions to modify S3 bucket settings
  • A destination bucket to store your logs (can be the same account, ideally a separate bucket)
Setting up a dedicated log bucket

It is a best practice to create a dedicated bucket for storing access logs. This keeps logs organized and allows you to apply specific retention policies.

Console steps:

  1. Go to S3 in the AWS Console
  2. Click Create bucket
  3. Name it something like my-s3-access-logs-bucket
  4. Choose us-east-1 region
  5. Keep Block all public access enabled
  6. Click Create bucket

Important: The log destination bucket must be in the same AWS Region as the source bucket.

AWS Console Method

  1. Open the Amazon S3 Console
  2. Find and click on the bucket that needs logging enabled
  3. Go to the Properties tab
  4. Scroll down to Server access logging and click Edit
  5. Select Enable
  6. For Target bucket, choose your log destination bucket
  7. For Target prefix, enter a prefix like logs/ (this organizes logs by source bucket)
  8. Click Save changes

That's it! AWS will now log all access requests to your bucket.

AWS CLI (optional)

Enable logging on a bucket:

aws s3api put-bucket-logging \
--bucket <your-source-bucket> \
--bucket-logging-status '{
"LoggingEnabled": {
"TargetBucket": "<your-log-bucket>",
"TargetPrefix": "logs/<your-source-bucket>/"
}
}' \
--region us-east-1

Replace:

  • <your-source-bucket> with the bucket you want to monitor
  • <your-log-bucket> with the bucket that will store logs

Grant the logging service permission to write to your log bucket:

Before enabling logging, ensure your log bucket has the proper policy:

aws s3api put-bucket-policy \
--bucket <your-log-bucket> \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3ServerAccessLogsPolicy",
"Effect": "Allow",
"Principal": {
"Service": "logging.s3.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::<your-log-bucket>/logs/*",
"Condition": {
"ArnLike": {
"aws:SourceArn": "arn:aws:s3:::<your-source-bucket>"
},
"StringEquals": {
"aws:SourceAccount": "<your-account-id>"
}
}
}
]
}' \
--region us-east-1
CloudFormation (optional)

This template creates a bucket policy that allows S3 to write access logs. Note that you must enable logging on the source bucket separately (CloudFormation cannot directly modify an existing bucket's logging configuration).

AWSTemplateFormatVersion: '2010-09-09'
Description: Enable S3 bucket server access logging

Parameters:
SourceBucketName:
Type: String
Description: Name of the S3 bucket to enable logging on
LogBucketName:
Type: String
Description: Name of the S3 bucket to store access logs
LogPrefix:
Type: String
Default: 'logs/'
Description: Prefix for log object keys

Resources:
LogBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref LogBucketName
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: S3ServerAccessLogsPolicy
Effect: Allow
Principal:
Service: logging.s3.amazonaws.com
Action: s3:PutObject
Resource: !Sub 'arn:aws:s3:::${LogBucketName}/${LogPrefix}*'
Condition:
ArnLike:
aws:SourceArn: !Sub 'arn:aws:s3:::${SourceBucketName}'
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId

Outputs:
LogBucketArn:
Description: ARN of the log destination bucket
Value: !Sub 'arn:aws:s3:::${LogBucketName}'

Deploy the stack:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name s3-access-logging \
--parameter-overrides \
SourceBucketName=<your-source-bucket> \
LogBucketName=<your-log-bucket> \
--region us-east-1
Terraform (optional)
# S3 Bucket Server Access Logging Configuration

variable "source_bucket_name" {
description = "Name of the S3 bucket to enable logging on"
type = string
}

variable "log_bucket_name" {
description = "Name of the S3 bucket to store access logs"
type = string
}

variable "log_prefix" {
description = "Prefix for log object keys"
type = string
default = "logs/"
}

# Data source to reference existing source bucket
data "aws_s3_bucket" "source" {
bucket = var.source_bucket_name
}

# Data source to reference existing log bucket
data "aws_s3_bucket" "logs" {
bucket = var.log_bucket_name
}

# Get current AWS account ID
data "aws_caller_identity" "current" {}

# Enable logging on the source bucket
resource "aws_s3_bucket_logging" "this" {
bucket = data.aws_s3_bucket.source.id

target_bucket = data.aws_s3_bucket.logs.id
target_prefix = var.log_prefix
}

# Bucket policy to allow S3 logging service to write logs
resource "aws_s3_bucket_policy" "log_bucket_policy" {
bucket = data.aws_s3_bucket.logs.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "S3ServerAccessLogsPolicy"
Effect = "Allow"
Principal = {
Service = "logging.s3.amazonaws.com"
}
Action = "s3:PutObject"
Resource = "${data.aws_s3_bucket.logs.arn}/${var.log_prefix}*"
Condition = {
ArnLike = {
"aws:SourceArn" = data.aws_s3_bucket.source.arn
}
StringEquals = {
"aws:SourceAccount" = data.aws_caller_identity.current.account_id
}
}
}
]
})
}

output "source_bucket_logging_enabled" {
description = "Source bucket with logging enabled"
value = aws_s3_bucket_logging.this.bucket
}

output "log_destination_bucket" {
description = "Bucket receiving access logs"
value = aws_s3_bucket_logging.this.target_bucket
}

Apply the configuration:

terraform init
terraform apply -var="source_bucket_name=<your-source-bucket>" -var="log_bucket_name=<your-log-bucket>"

Verification

After enabling logging, verify the configuration:

  1. Go to the S3 Console and select your bucket
  2. Click the Properties tab
  3. Scroll to Server access logging - it should show Enabled with your target bucket

Note: Logs may take a few minutes to start appearing in your destination bucket.

CLI verification
aws s3api get-bucket-logging --bucket <your-source-bucket> --region us-east-1

Expected output when logging is enabled:

{
"LoggingEnabled": {
"TargetBucket": "your-log-bucket",
"TargetPrefix": "logs/your-source-bucket/"
}
}

If logging is not enabled, the command returns an empty response.

Additional Resources

Notes

  • Log delivery delay: Logs are delivered on a best-effort basis, typically within a few hours. They are not real-time.
  • Storage costs: Access logs consume storage in your log bucket. Consider setting up lifecycle policies to manage retention and costs.
  • Same-region requirement: The source bucket and log destination bucket must be in the same AWS Region.
  • Directory buckets: Server access logging is not supported for S3 directory buckets (S3 Express One Zone).
  • Complementary logging: For real-time, comprehensive logging, consider also enabling AWS CloudTrail data events for S3, which provides more detailed audit trails.