Cấu trúc IAM Policy JSON: Phân tích chi tiết Effect, Action, Resource và Condition
Lần đầu nhìn vào một IAM policy JSON, hầu hết engineer đều đọc được nội dung nhưng không thực sự hiểu tại sao policy đó hoạt động theo cách nó hoạt động — đặc biệt khi policy bị deny mà không rõ lý do. Hiểu rõ cấu trúc IAM policy cơ bản, cụ thể là bốn phần tử Effect, Action, Resource, và Condition, là nền tảng để debug quyền truy cập chính xác thay vì thử-sai.
TL;DR — Tóm tắt nhanh cấu trúc IAM Policy
| Phần tử | Vai trò | Giá trị hợp lệ |
|---|---|---|
Effect | Cho phép hoặc từ chối | Allow hoặc Deny |
Action | API action nào được áp dụng | Chuỗi như s3:GetObject, wildcard s3:* |
Resource | Tài nguyên nào bị ảnh hưởng | ARN cụ thể hoặc * |
Condition | Điều kiện ngữ cảnh bổ sung | Operator + key + value (tùy chọn) |
Cách IAM Policy hoạt động — Nền tảng cần hiểu trước
Trước khi đi vào từng phần tử, cần nắm rõ luồng đánh giá policy của AWS IAM. Khi một principal thực hiện một API call, IAM thu thập tất cả các policy áp dụng cho principal đó — identity-based policies, resource-based policies, SCPs, permission boundaries — rồi đánh giá theo thứ tự ưu tiên cố định.
- Explicit Deny thắng tất cả — Nếu bất kỳ policy nào có
Effect: Denykhớp với request, truy cập bị từ chối ngay lập tức, bất kể có bao nhiêuAllowkhác. - Mặc định là Deny — Nếu không có statement nào khớp với request, IAM từ chối theo mặc định (implicit deny).
- Allow phải tường minh — Cần ít nhất một statement
Allowkhớp để truy cập được cấp phép. - SCPs giới hạn tối đa — Service Control Policies ở cấp AWS Organizations có thể chặn quyền ngay cả khi identity policy cho phép.
Phân tích chi tiết từng phần tử IAM Policy
1. Effect — Quyết định cuối cùng
Effect chỉ nhận hai giá trị: Allow hoặc Deny. Đây là phần tử đơn giản nhất về cú pháp nhưng lại có trọng lượng lớn nhất trong logic đánh giá. Một Deny tường minh không thể bị override bởi bất kỳ Allow nào — kể cả policy của admin. Đây không phải best practice, đây là cơ chế cứng của IAM evaluation logic.
Hãy nghĩ Explicit Deny như một khóa vật lý trên cửa — dù bạn có bao nhiêu chìa khóa (Allow), cánh cửa vẫn không mở được nếu khóa đang gài.
{
"Effect": "Deny",
"Action": "s3:DeleteBucket",
"Resource": "*"
}
Statement trên sẽ chặn xóa bucket S3 bất kể principal có bao nhiêu quyền Allow khác — kể cả AdministratorAccess.
2. Action — API nào được kiểm soát
Action xác định API action cụ thể mà statement áp dụng. Format chuẩn là service-prefix:ActionName, ví dụ ec2:DescribeInstances hay iam:CreateRole. Wildcard * được hỗ trợ ở cả cấp service (s3:*) lẫn cấp toàn bộ (*).
Điểm hay bị nhầm: không phải mọi action đều hỗ trợ resource-level permission. Một số action — đặc biệt là các List và Describe action — yêu cầu "Resource": "*" vì chúng không hoạt động trên một resource cụ thể. Nếu bạn chỉ định ARN cho những action này, policy sẽ không có hiệu lực như mong đợi.
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::my-app-bucket/*"
}
Để kiểm tra một action cụ thể có hỗ trợ resource-level permission hay không, tra cứu trực tiếp trong AWS Service Authorization Reference. Cột 'Resource types' trong bảng action sẽ cho biết action đó có thể được giới hạn xuống resource cụ thể hay bắt buộc phải dùng *.
3. Resource — Phạm vi tài nguyên
Resource xác định tài nguyên AWS cụ thể mà statement áp dụng, được biểu diễn bằng ARN. Đây là nơi nhiều policy bị viết quá rộng nhất — dùng "Resource": "*" khi thực tế chỉ cần truy cập một bucket hay một function cụ thể.
Một số pattern ARN phổ biến:
# S3 bucket (global service — không có region và account ID)
arn:aws:s3:::my-app-bucket
# Tất cả object trong bucket
arn:aws:s3:::my-app-bucket/*
# Lambda function cụ thể
arn:aws:lambda:us-east-1:123456789012:function:my-function
# Tất cả Lambda function trong region
arn:aws:lambda:us-east-1:123456789012:function:*
# IAM role (global service — không có region)
arn:aws:iam::123456789012:role/MyAppRole
Lưu ý quan trọng: S3 và IAM là global service — ARN của chúng không chứa region. Lambda, EC2, RDS là regional service — ARN phải bao gồm region. Nhầm lẫn điều này dẫn đến policy không khớp resource và bị deny im lặng.
4. Condition — Kiểm soát ngữ cảnh
Condition là phần tử tùy chọn nhưng mạnh nhất để triển khai least privilege thực sự. Nó cho phép giới hạn quyền dựa trên ngữ cảnh của request: IP nguồn, thời gian, MFA status, tag của resource, hay nhiều yếu tố khác.
Cú pháp của Condition có ba lớp lồng nhau: condition operator → condition key → giá trị so sánh.
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-app-bucket/*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "us-east-1"
},
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
Khi có nhiều condition key trong cùng một operator, chúng được đánh giá theo logic AND — tất cả phải đúng. Khi có nhiều giá trị trong một key, chúng được đánh giá theo logic OR — ít nhất một phải đúng.
Các condition operator phổ biến:
| Operator | Dùng cho | Ví dụ key |
|---|---|---|
StringEquals | So sánh chuỗi chính xác | aws:RequestedRegion |
StringLike | So sánh chuỗi với wildcard | s3:prefix |
ArnLike | So sánh ARN với wildcard | aws:SourceArn |
IpAddress | So sánh địa chỉ IP / CIDR | aws:SourceIp |
Bool | Giá trị boolean | aws:MultiFactorAuthPresent |
DateGreaterThan | So sánh thời gian | aws:CurrentTime |
Ví dụ thực tế — Policy hoàn chỉnh với bốn phần tử
Dưới đây là một policy thực tế cấp quyền cho một Lambda function đọc secret từ Secrets Manager, nhưng chỉ khi request đến từ region us-east-1 và resource có tag môi trường phù hợp.
🔽 Xem policy JSON đầy đủ
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSecretsManagerReadInProd",
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "us-east-1",
"aws:ResourceTag/Environment": "production"
}
}
},
{
"Sid": "DenyDeleteSecret",
"Effect": "Deny",
"Action": "secretsmanager:DeleteSecret",
"Resource": "*"
}
]
}
Statement đầu tiên cho phép đọc secret — nhưng chỉ khi cả hai condition đều thỏa mãn. Statement thứ hai chặn xóa secret hoàn toàn, bất kể context. Đây là pattern deny-as-guardrail thường thấy trong môi trường production.
Bẫy thực tế — Khi policy đúng cú pháp nhưng sai hành vi
Đây là pattern lỗi hay gặp nhất: engineer viết policy cho S3, test thấy GetObject hoạt động, nhưng ListBucket bị deny. Họ kiểm tra lại policy, thấy cả hai action đều có Allow — và bắt đầu nghi ngờ IAM hoạt động sai.
Nguyên nhân thực tế: s3:ListBucket áp dụng lên bucket (arn:aws:s3:::my-bucket), còn s3:GetObject áp dụng lên object (arn:aws:s3:::my-bucket/*). Nếu policy chỉ có Resource: arn:aws:s3:::my-bucket/*, statement Allow cho s3:ListBucket sẽ không bao giờ khớp — vì resource ARN không match.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-app-bucket"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-app-bucket/*"
}
]
}
Hai statement riêng biệt với hai ARN khác nhau — đây là cách đúng. Đây là ví dụ điển hình của behavioral interaction giữa action scope và resource ARN mà chỉ đọc tài liệu từng phần riêng lẻ sẽ không thấy rõ.
Debug IAM Policy với AWS CLI
Khi policy không hoạt động như kỳ vọng, công cụ đầu tiên cần dùng là IAM Policy Simulator — hoặc trực tiếp qua CLI với iam simulate-principal-policy.
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/dev-user \
--action-names s3:GetObject \
--resource-arns arn:aws:s3:::my-app-bucket/data/file.txt
Output sẽ trả về allowed hoặc explicitDeny / implicitDeny — phân biệt rõ giữa bị chặn bởi Deny tường minh hay đơn giản là không có Allow nào khớp. Đây là sự khác biệt quan trọng khi debug.
# Kiểm tra policy đính kèm trực tiếp vào một IAM user
aws iam list-attached-user-policies \
--user-name dev-user
# Kiểm tra inline policy
aws iam list-user-policies \
--user-name dev-user
# Xem nội dung một managed policy (lấy version mặc định trước)
aws iam get-policy \
--policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicy
Để xem nội dung document của một policy version cụ thể, dùng aws iam get-policy-version với --version-id lấy từ output của get-policy ở trên (trường DefaultVersionId).
Wrap-up — Cấu trúc IAM Policy và bước tiếp theo
Bốn phần tử Effect, Action, Resource, và Condition tạo thành ngôn ngữ kiểm soát truy cập của AWS IAM. Hiểu đúng từng phần tử — đặc biệt là cách resource ARN scope ảnh hưởng đến action matching và cách Explicit Deny override mọi Allow — giúp bạn viết policy chính xác thay vì viết rộng để tránh lỗi.
Bước tiếp theo thực tế:
- Dùng IAM Policy Simulator để test policy trước khi deploy.
- Tra cứu Service Authorization Reference để xác nhận action nào hỗ trợ resource-level permission.
- Xem xét dùng Permission Boundaries để giới hạn tối đa quyền của IAM entity trong môi trường multi-team.
Glossary — Thuật ngữ chính
| Thuật ngữ | Định nghĩa |
|---|---|
| Explicit Deny | Statement với Effect: Deny — override mọi Allow, không có ngoại lệ. |
| Implicit Deny | Từ chối mặc định khi không có statement Allow nào khớp với request. |
| ARN | Amazon Resource Name — định danh duy nhất cho mọi tài nguyên AWS, format: arn:aws:service:region:account-id:resource. |
| Condition Key | Thuộc tính ngữ cảnh của request (IP, region, MFA status...) dùng trong phần tử Condition. |
| Resource-level Permission | Khả năng giới hạn một action xuống ARN cụ thể thay vì phải dùng *. |
Nhận xét
Đăng nhận xét