RSTR-IAC-009 — S3 bucket with public ACL
Summary
A Terraform aws_s3_bucket (or aws_s3_bucket_acl) sets
acl = "public-read" or acl = "public-read-write". Every object
in the bucket is now readable (and, with the second form, writable)
by every IPv4 address on the public internet.
Public buckets are the single most common AWS data-leak vector
because the field is one line of HCL away from "private". AWS
itself ships four separate guardrails to make it harder
(Block Public Access settings, IAM analyzer, security-hub
findings, bucket policy explicit-deny rules) — rastray is the same
fence at the IaC layer.
Severity
High.
Languages
Terraform (.tf, .tfvars).
What rastray flags
resource "aws_s3_bucket" "assets" {
bucket = "my-org-assets"
acl = "public-read" # ← flagged
}
resource "aws_s3_bucket_acl" "assets" {
bucket = aws_s3_bucket.assets.id
acl = "public-read-write" # ← flagged
}
What rastray deliberately does not flag
acl = "private"(default).acl = "authenticated-read"— limited to any AWS account, which is a separate (smaller) blast radius and is occasionally intentional for cross-account delivery.acl = "bucket-owner-full-control"— required pattern for cross-account object writes.
How to fix it
-
Switch the ACL to
"private". -
If you need public delivery, front the bucket with CloudFront and an origin access identity:
resource "aws_s3_bucket" "assets" { bucket = "my-org-assets" acl = "private" } resource "aws_cloudfront_distribution" "cdn" { origin { domain_name = aws_s3_bucket.assets.bucket_regional_domain_name origin_id = "s3-assets" s3_origin_config { origin_access_identity = aws_cloudfront_origin_access_identity.oai.cloudfront_access_identity_path } } # ... } -
Add
aws_s3_bucket_public_access_blockwith all four flags set totrue— it's a no-cost belt-and-braces:resource "aws_s3_bucket_public_access_block" "assets" { bucket = aws_s3_bucket.assets.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true }