S3 Permissions & Policies
S3 access control has multiple layers: bucket policies (resource-based), IAM policies (identity-based), Block Public Access settings, and Access Points. Understanding when to use each is critical for both security and correct functionality.
Bucket Policies & Block Public Access
// Bucket policy — applied at the bucket level, allows cross-account access
// Evaluated alongside IAM policies; DENY always wins
// Allow public read for static website bucket (ACLs disabled, use bucket policy)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-website-bucket/*"
}
]
}
// Allow only CloudFront OAC (Origin Access Control) to read objects
// (Replace PUBLIC read policy once CloudFront is set up)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontOAC",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-website-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EDFDVBD6EXAMPLE"
}
}
}
]
}
// Allow a specific IAM role to read/write to a prefix
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::123456789012:role/my-app-role"},
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::my-app-bucket/uploads/*"
},
{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::123456789012:role/my-app-role"},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-app-bucket",
"Condition": {"StringLike": {"s3:prefix": "uploads/*"}}
}
]
}# Apply bucket policy
aws s3api put-bucket-policy \
--bucket my-website-bucket \
--policy file://bucket-policy.json
# Block Public Access — 4 independent settings, all ON by default for new buckets
# BlockPublicAcls: Reject requests to PUT public ACLs
# IgnorePublicAcls: Ignore public ACLs already applied
# BlockPublicPolicy: Reject requests to PUT bucket policy that grants public access
# RestrictPublicBuckets: Restrict public/cross-account access even if policy allows it
# Check Block Public Access settings
aws s3api get-public-access-block --bucket my-bucket
# Disable Block Public Access for a static website bucket
# (Only do this if you intend public access — use OAC+CloudFront instead)
aws s3api put-public-access-block \
--bucket my-website-bucket \
--public-access-block-configuration \
"BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"
# Legacy ACLs — avoid for new buckets; use bucket policies instead
# "Bucket owner enforced" setting disables ACLs (recommended for new buckets)
aws s3api put-bucket-ownership-controls \
--bucket my-bucket \
--ownership-controls "Rules=[{ObjectOwnership=BucketOwnerEnforced}]"IAM Policies & Access Points
// IAM policy for an application role — read/write to specific bucket prefix
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::my-app-uploads/*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-app-uploads",
"Condition": {
"StringLike": {
"s3:prefix": ["", "uploads/", "uploads/*"]
}
}
}
]
}# VPC Endpoint for S3 — route S3 traffic within AWS network (no internet)
# Gateway endpoint: free; affects route tables; only S3 and DynamoDB
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0abc123 \
--service-name com.amazonaws.us-east-1.s3 \
--route-table-ids rtb-0abc123
# S3 Access Points — per-application access control on a shared bucket
# Useful when multiple teams/apps share a bucket but need isolated permissions
aws s3control create-access-point \
--account-id 123456789012 \
--name data-science-access-point \
--bucket my-shared-data-lake \
--vpc-configuration VpcId=vpc-0abc123
# Access via Access Point ARN
# s3://arn:aws:s3:us-east-1:123:accesspoint/data-science-access-point/key.csv
# Cross-account access — bucket policy grants access to another account
# Account B accesses Account A's bucket:
# Account A bucket policy allows Account B's role
# Account B IAM policy allows s3:* on Account A's bucket ARNKeep your own version of these notes — editable, searchable, and organised by your stack.
Start free