S3 'Access Denied' Khi Truy Cập URL Công Khai: Block Public Access Đang Chặn Bạn

Bạn upload một file ảnh lên S3, set ACL thành public-read, copy URL ra trình duyệt — và nhận về một trang XML lạnh lùng: 'Access Denied'. Đây là một trong những tình huống gây bực bội nhất khi mới làm việc với S3, và nguyên nhân gần như luôn nằm ở cơ chế Block Public Access hoạt động ở nhiều tầng độc lập nhau.

TL;DR — Tóm Tắt Nhanh

Tầng kiểm soátVị tríGhi đè ACL object?
Block Public Access (Account)S3 Console → Account settingsCó — chặn toàn bộ
Block Public Access (Bucket)S3 Console → Bucket → PermissionsCó — chặn bucket đó
Bucket PolicyBucket → Permissions → Bucket policyPhụ thuộc nội dung policy
Object ACLObject → PermissionsChỉ có hiệu lực nếu các tầng trên cho phép

Block Public Access Hoạt Động Như Thế Nào

Nhiều người hình dung S3 permission như một lớp duy nhất: set object thành public là xong. Thực tế, AWS áp dụng mô hình phân tầng — mỗi tầng có thể độc lập chặn truy cập công khai, bất kể các tầng bên dưới cấu hình gì.

graph TD REQ["HTTP GET Request (Anonymous User)"] --> ACC["Account-level Block Public Access"] ACC -- "Bất kỳ cờ nào = true" --> DENY1["❌ Access Denied"] ACC -- "Tất cả cờ = false" --> BKT["Bucket-level Block Public Access"] BKT -- "IgnorePublicAcls = true" --> DENY2["❌ ACL bị bỏ qua"] BKT -- "RestrictPublicBuckets = true" --> DENY3["❌ Anonymous bị chặn"] BKT -- "Tất cả cờ = false" --> POL["Bucket Policy Evaluation"] POL -- "Explicit Deny" --> DENY4["❌ Access Denied"] POL -- "Allow hoặc không có policy" --> ACL["Object ACL Evaluation"] ACL -- "public-read grant tồn tại" --> OK["✅ 200 OK"] ACL -- "Không có public grant" --> DENY5["❌ Access Denied"]
  1. Account-level Block Public Access: Cài đặt toàn tài khoản, áp dụng cho mọi bucket. Nếu bật ở đây, không bucket nào trong account có thể public — dù bucket policy hay ACL có cho phép.
  2. Bucket-level Block Public Access: Áp dụng riêng cho từng bucket. Có 4 cờ độc lập, mỗi cờ kiểm soát một loại truy cập khác nhau.
  3. Bucket Policy: Nếu Block Public Access không chặn, bucket policy được đánh giá tiếp theo.
  4. Object ACL: Tầng cuối cùng — chỉ có tác dụng khi tất cả các tầng trên đã cho phép.
Hãy hình dung Block Public Access như một công tắc tổng ở bảng điện — dù bạn đã bật từng ổ cắm riêng lẻ (ACL object), nếu công tắc tổng ngắt, không ổ cắm nào hoạt động.

Điểm quan trọng: BlockPublicAclsIgnorePublicAcls là hai cờ khác nhau. BlockPublicAcls ngăn việc đặt ACL public mới, còn IgnorePublicAcls khiến AWS bỏ qua mọi ACL public đã tồn tại khi đánh giá truy cập. Nếu IgnorePublicAcls đang bật, object ACL public-read của bạn hoàn toàn vô hiệu.

Chẩn Đoán S3 Access Denied — Từng Bước

Bước 1: Kiểm tra Block Public Access ở cấp Account

Đây là tầng bị bỏ qua nhiều nhất. Khi tạo tài khoản AWS mới, Block Public Access được bật mặc định ở cấp account — nghĩa là mọi cấu hình public ở bucket hay object đều bị vô hiệu hóa ngay lập tức. Kiểm tra trạng thái hiện tại:

aws s3control get-public-access-block \
  --account-id 123456789012

Nếu output trả về bất kỳ cờ nào là true, đó là nguyên nhân gốc rễ. Để tắt toàn bộ ở cấp account (chỉ làm nếu bạn hiểu rõ tác động):

aws s3control put-public-access-block \
  --account-id 123456789012 \
  --public-access-block-configuration \
    BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false

Bước 2: Kiểm tra Block Public Access ở cấp Bucket

Ngay cả khi cấp account đã thông, bucket vẫn có thể có cờ riêng. Bucket-level settings ghi đè lên ACL và bucket policy của chính bucket đó:

aws s3api get-public-access-block \
  --bucket ten-bucket-cua-ban

Nếu IgnorePublicAclstrue, ACL public-read trên object của bạn đang bị bỏ qua hoàn toàn. Tắt cờ này ở cấp bucket:

aws s3api put-public-access-block \
  --bucket ten-bucket-cua-ban \
  --public-access-block-configuration \
    BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false

Bước 3: Xác nhận Object ACL thực sự là public-read

Sau khi các tầng Block Public Access đã được gỡ, kiểm tra xem ACL trên object có thực sự được set đúng không — đặc biệt nếu object được upload trước khi bạn thay đổi cấu hình bucket:

aws s3api get-object-acl \
  --bucket ten-bucket-cua-ban \
  --key duong-dan/ten-file.jpg

Tìm trong output một grant có Grantee.URIhttp://acs.amazonaws.com/groups/global/AllUsersPermissionREAD. Nếu không có, set lại:

aws s3api put-object-acl \
  --bucket ten-bucket-cua-ban \
  --key duong-dan/ten-file.jpg \
  --acl public-read

Bước 4: Kiểm tra Bucket Policy có đang chặn không

Nếu bucket có một policy với Deny statement — kể cả Deny có điều kiện — nó có thể ghi đè Allow từ ACL. Explicit Deny luôn thắng trong mô hình IAM của AWS:

aws s3api get-bucket-policy \
  --bucket ten-bucket-cua-ban

Tìm bất kỳ "Effect": "Deny" nào áp dụng cho s3:GetObject với Principal là * hoặc anonymous users. Nếu RestrictPublicBuckets đang bật, AWS cũng sẽ chặn mọi cross-account và anonymous access dù bucket policy có Allow.

graph LR SYM["Triệu chứng: Access Denied trên URL"] --> D1["Chẩn đoán sai: Kiểm tra bucket-level Block Public Access"] D1 --> OBS["Quan sát: Bucket settings trông đúng"] OBS --> D2["Chẩn đoán sai 2: Nghi ngờ CloudFront / cache"] D2 --> ROOT["Nguyên nhân thực: Account-level Block Public Access vẫn đang BẬT"] ROOT --> FIX["Fix: aws s3control get-public-access-block --account-id 123456789012"]

Trường Hợp Thực Tế: Misdiagnosis Phổ Biến Nhất

Tình huống điển hình: bạn vào S3 Console, tắt Block Public Access ở bucket, set ACL object thành public-read, thử lại URL — vẫn 'Access Denied'. Bạn kiểm tra lại bucket settings, mọi thứ trông đúng. Bạn bắt đầu nghi ngờ CloudFront, nghi ngờ cache, nghi ngờ đủ thứ.

Nguyên nhân thực tế: Block Public Access ở cấp account vẫn đang bật. Console S3 khi bạn vào bucket chỉ hiển thị bucket-level settings — account-level settings nằm ở một trang hoàn toàn khác: S3 Console → Block Public Access settings for this account. Hai trang này trông giống nhau nhưng kiểm soát hai tầng độc lập.

Đây là lý do bước 1 phải luôn là account-level — không phải bucket-level.

Cách Tiếp Cận Được Khuyến Nghị: Dùng Bucket Policy Thay Vì ACL

AWS hiện khuyến nghị sử dụng bucket policy để cấp quyền public thay vì ACL object, đặc biệt sau khi S3 Object Ownership mặc định chuyển sang BucketOwnerEnforced trên các bucket mới — chế độ này vô hiệu hóa ACL hoàn toàn. Nếu bucket của bạn đang dùng BucketOwnerEnforced, ACL public-read sẽ không có tác dụng dù Block Public Access đã tắt.

Kiểm tra Object Ownership setting:

aws s3api get-bucket-ownership-controls \
  --bucket ten-bucket-cua-ban

Nếu output là BucketOwnerEnforced, bạn cần dùng bucket policy để public:

🔽 Bucket Policy — Cho phép public read toàn bộ bucket
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::ten-bucket-cua-ban/*"
    }
  ]
}

Apply policy này:

aws s3api put-bucket-policy \
  --bucket ten-bucket-cua-ban \
  --policy file://public-read-policy.json

IAM Policy Để Kiểm Tra Cấu Hình (Cho DevOps/Admin)

Nếu bạn cần cấp quyền cho một IAM user hoặc role để chạy các lệnh chẩn đoán trên, đây là policy tối thiểu cần thiết. Lưu ý rằng s3control:GetPublicAccessBlock (kiểm tra account-level) và s3:GetPublicAccessBlock (kiểm tra bucket-level) là hai action khác nhau thuộc hai service namespace khác nhau:

🔽 IAM Policy — Quyền chẩn đoán S3 Block Public Access
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "BucketLevelDiagnostics",
      "Effect": "Allow",
      "Action": [
        "s3:GetPublicAccessBlock",
        "s3:GetBucketPolicy",
        "s3:GetBucketAcl",
        "s3:GetObjectAcl",
        "s3:GetBucketOwnershipControls"
      ],
      "Resource": [
        "arn:aws:s3:::ten-bucket-cua-ban",
        "arn:aws:s3:::ten-bucket-cua-ban/*"
      ]
    },
    {
      "Sid": "AccountLevelDiagnostics",
      "Effect": "Allow",
      "Action": [
        "s3control:GetPublicAccessBlock"
      ],
      "Resource": "arn:aws:s3control::123456789012:account/123456789012"
    }
  ]
}

Lưu Ý Bảo Mật Trước Khi Tắt Block Public Access

Block Public Access được bật mặc định có lý do chính đáng — nó ngăn data breach do cấu hình sai. Trước khi tắt, hãy xác nhận:

  • Bucket này thực sự cần public access (ví dụ: static website, public CDN origin).
  • Không có dữ liệu nhạy cảm nào trong bucket hoặc các prefix con.
  • Nếu chỉ cần public một số object, cân nhắc dùng CloudFront với Origin Access Control (OAC) thay vì public bucket trực tiếp — bucket vẫn private, chỉ CloudFront mới có quyền đọc.
  • Tắt ở cấp account ảnh hưởng toàn bộ tài khoản — chỉ làm nếu bạn kiểm soát được tất cả bucket trong account.

Wrap-Up: Checklist Xử Lý S3 Access Denied

Khi gặp lỗi S3 Access Denied trên URL public, chạy theo thứ tự này:

  1. Kiểm tra account-level Block Public Access (aws s3control get-public-access-block).
  2. Kiểm tra bucket-level Block Public Access (aws s3api get-public-access-block).
  3. Kiểm tra Object Ownership — nếu BucketOwnerEnforced, ACL vô hiệu, dùng bucket policy.
  4. Xác nhận object ACL hoặc bucket policy đã được set đúng.
  5. Kiểm tra bucket policy có Deny statement nào không.

Tài liệu tham khảo chính thức: AWS S3 Block Public AccessS3 Object Ownership.

Glossary — Thuật Ngữ Chính

Thuật ngữGiải thích
Block Public AccessCơ chế của S3 ngăn chặn truy cập công khai, hoạt động độc lập ở cấp account và bucket.
IgnorePublicAclsCờ khiến S3 bỏ qua mọi ACL public khi đánh giá quyền truy cập, dù ACL đã được set.
Object ACLDanh sách kiểm soát truy cập gắn trực tiếp vào một S3 object cụ thể.
BucketOwnerEnforcedChế độ Object Ownership vô hiệu hóa hoàn toàn ACL, buộc dùng bucket policy để kiểm soát quyền.
Explicit DenyQuy tắc Deny trong IAM/bucket policy luôn ghi đè mọi Allow, bất kể nguồn gốc của Allow đó.

Nhận xét

Bài đăng phổ biến từ blog này

EC2 Không Có Internet Trong Custom VPC: Cách Gắn Internet Gateway và Cập Nhật Route Table

Route 53 Alias vs CNAME: Khi Nào Dùng Alias Record Cho Zone Apex?