Mục tiêu học tập
- Hiểu các khái niệm IAM cốt lõi: identity, principal, role, policy
- Áp dụng nguyên tắc least privilege và nhận diện các pattern over-privileged phổ biến
- Phân biệt IAM User vs Role vs Group, identity-based vs resource-based policy, trust policy
- Thiết kế cross-account access bằng
sts:AssumeRole - Sử dụng IAM Access Analyzer và Last Accessed report để cắt quyền dư thừa
- So sánh ngắn IAM AWS với GCP IAM bindings và Azure RBAC
- Tránh các "bẫy" IAM:
NotAction, deny-by-default, allow vs explicit deny
1. Khái niệm cốt lõi của IAM
IAM (Identity and Access Management) trả lời câu hỏi: "Ai (principal) được phép làm gì (action) trên tài nguyên nào (resource), trong điều kiện nào (condition)?"
Bốn thành phần chính
| Khái niệm | Ý nghĩa | Ví dụ |
|---|---|---|
| Identity | Thực thể có thể xác thực | IAM User alice, Role lambda-exec |
| Principal | Identity đang request action | User alice gọi s3:GetObject |
| Action | Thao tác trên service | s3:PutObject, iam:PassRole |
| Resource | Tài nguyên bị tác động (ARN) | arn:aws:s3:::my-bucket/* |
| Policy | JSON document cấp quyền | Identity-based hoặc resource-based |
| Condition | Ràng buộc thêm | aws:SourceIp, aws:MultiFactorAuthPresent |
2. Principle of Least Privilege
Least privilege: cấp đúng quyền cần thiết để hoàn thành công việc — không hơn, không kém. Đây là nguyên tắc nền tảng của zero-trust security.
Các pattern over-privileged thường gặp
Anti-pattern 1: Wildcard * ở Action và Resource
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}]
}
Đây là AdministratorAccess ngầm — ai có policy này có thể xoá account, tạo IAM user mới, tắt CloudTrail. Tuyệt đối không gắn cho service workload.
Anti-pattern 2: AdministratorAccess cho dev/CI role trên production
CI runner chỉ cần deploy ECS service nhưng nhận AdministratorAccess "cho tiện". Hậu quả: leaked credential = toàn quyền account.
Anti-pattern 3: s3:* trên * thay vì bucket cụ thể
// ❌ BAD
{ "Effect": "Allow", "Action": "s3:*", "Resource": "*" }
// ✅ GOOD
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::app-uploads-prod/*"
}
Anti-pattern 4: iam:PassRole quá rộng
iam:PassRole cho phép một service "đeo" role khác. Nếu cho phép iam:PassRole trên *, attacker có thể privilege escalation bằng cách pass AdministratorAccess role cho Lambda mới.
// ✅ Giới hạn chỉ role cụ thể
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::123456789012:role/lambda-exec-prod",
"Condition": {
"StringEquals": { "iam:PassedToService": "lambda.amazonaws.com" }
}
}
3. User vs Role vs Group
| Tiêu chí | User | Role |
|---|---|---|
| Credential | Long-lived (access key) | Temporary (STS, ~1h) |
| Ai dùng | Con người | Service hoặc identity được trust |
| Authentication | Password / Access key | AssumeRole API call |
| Best practice | Hạn chế tối đa, dùng SSO/IdC | Mặc định cho mọi workload |
Khuyến nghị 2024: Không tạo IAM User cho con người nữa — dùng AWS IAM Identity Center (SSO) kết hợp Okta/Google Workspace. IAM User chỉ còn dùng cho legacy hoặc service account không thể role-based.
4. Identity-based vs Resource-based Policy
Identity-based policy
Gắn vào User, Group hoặc Role. Trả lời: "Identity này được phép làm gì?"
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::reports/*"
}]
}
Resource-based policy
Gắn vào resource (S3 bucket, SQS queue, Lambda function, KMS key). Trả lời: "Ai được phép truy cập resource này?"
// S3 bucket policy — cross-account read
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::222222222222:role/analytics" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::shared-data/*"
}]
}
| Loại policy | Có field Principal? | Hỗ trợ cross-account |
|---|---|---|
| Identity-based | Không (principal ngầm là identity được gắn) | Cần resource-based hoặc role assumption |
| Resource-based | Bắt buộc | Có (chỉ cần principal là ARN account khác) |
5. Trust Policy và Cross-account Access
Trust policy — "Ai được phép assume role này?"
Mỗi role có 2 policy: trust policy (ai được phép assume) và permissions policy (sau khi assume thì làm được gì).
// Trust policy của role "data-reader" trong account A (111111111111)
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::222222222222:root" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "sts:ExternalId": "shared-secret-uuid" },
"Bool": { "aws:MultiFactorAuthPresent": "true" }
}
}]
}
Principal: root của account 222... = bất kỳ identity trong account đó (nếu có quyền sts:AssumeRole) đều assume được. Thường thắt chặt thêm bằng ExternalId để chống "confused deputy".
Cross-account flow
# Account B user gọi assume role
aws sts assume-role \
--role-arn arn:aws:iam::111111111111:role/data-reader \
--role-session-name alice-session \
--external-id shared-secret-uuid
# Output: AccessKeyId, SecretAccessKey, SessionToken (TTL ~1h)
Confused deputy problem: service B (vd: third-party SaaS) được trust để assume role trong account A. Nếu trust policy chỉ check
Principal: B's account, một customer khác của B cũng có thể bị B vô tình impersonate. ExternalId là shared secret giữa A và B để chứng minh request thực sự đến từ customer A.
6. IAM Access Analyzer và Last Accessed
IAM Access Analyzer
Tự động phát hiện resource bị share ra ngoài account/organization của bạn (S3 bucket public, role có trust policy quá rộng, KMS key share cho account ngoài).
# Bật analyzer cho organization
aws accessanalyzer create-analyzer \
--analyzer-name org-analyzer \
--type ORGANIZATION
# Liệt kê finding
aws accessanalyzer list-findings \
--analyzer-arn arn:aws:access-analyzer:us-east-1:...:analyzer/org-analyzer
Access Analyzer còn có policy validation — kiểm tra IAM policy có lỗi syntax, security warning trước khi attach.
Last Accessed report
Xem identity/role nào lâu rồi không dùng action gì → cắt quyền dư.
# Generate report cho role
aws iam generate-service-last-accessed-details \
--arn arn:aws:iam::111111111111:role/ci-deploy
aws iam get-service-last-accessed-details \
--job-id <job-id-từ-lệnh-trên>
Output cho biết service nào role đã dùng/không dùng trong N ngày qua. Pattern phổ biến: role có AdministratorAccess nhưng 90 ngày chỉ dùng s3:* và ecs:* → revoke admin, cấp policy tối thiểu.
7. So sánh ngắn với GCP và Azure
| Khái niệm | AWS IAM | GCP IAM | Azure |
|---|---|---|---|
| Identity | User, Role, Service | User, Service Account | User, Service Principal, Managed Identity |
| Quyền | Policy (JSON) | Role (predefined/custom) gồm nhiều permission | Role Definition |
| Binding | Policy attached to identity | IAM Binding: (member, role, resource) | Role Assignment |
| Scope | Per-resource ARN | Project / Folder / Org hierarchy | Subscription / Resource Group / Resource |
| Cross-account | AssumeRole + STS | Workload Identity Federation | Cross-tenant access policy |
GCP IAM ví dụ
# Binding: alice@example.com là roles/storage.objectViewer trên bucket "data"
bindings:
- members:
- user:alice@example.com
role: roles/storage.objectViewer
Azure RBAC ví dụ
az role assignment create \
--assignee alice@contoso.com \
--role "Storage Blob Data Reader" \
--scope /subscriptions/<sub-id>/resourceGroups/<rg>
Điểm chung: cả ba đều theo mô hình (who, what, where, when). Khác biệt lớn nhất là scope hierarchy — GCP có inheritance theo Org → Folder → Project rõ ràng, AWS Organizations + SCP là cơ chế tương tự cho AWS.
8. IAM Trap thường gặp
Trap 1: Deny-by-default, Explicit Deny luôn thắng
Mọi thứ mặc định bị deny. Cần Allow rõ ràng. Nếu có Explicit Deny ở bất kỳ policy nào (identity, resource, SCP, permission boundary) → DENY luôn thắng.
Trap 2: NotAction không phải là Deny
{
"Effect": "Allow",
"NotAction": "iam:*",
"Resource": "*"
}
Câu này thường được hiểu nhầm là "deny mọi iam:*". Thực tế: Allow tất cả action không phải iam:*. Tương đương với một policy admin trừ IAM. Dùng NotAction với Effect: Allow rất nguy hiểm — dễ vô tình cấp quyền mới mà bạn không lường trước (vd: AWS thêm service mới).
Best practice: dùng
NotActionchỉ vớiEffect: Deny(deny mọi thứ ngoại trừ X). TránhAllow + NotAction.
Trap 3: Permission boundary ≠ SCP ≠ Identity policy
Effective permissions =
Identity policy
∩ Permission boundary
∩ SCP (organization-level)
∩ Session policy (nếu có)
\ Explicit Deny (mọi tầng)
- SCP (Service Control Policy): áp toàn account trong Organization, ngay cả root user cũng bị giới hạn. SCP không cấp quyền, chỉ là maximum allowed.
- Permission boundary: gán cho User/Role, giới hạn maximum quyền của identity đó.
- Identity policy: quyền thực tế cấp cho identity.
Nếu identity policy cho s3:* nhưng SCP chỉ cho s3:GetObject → effective = s3:GetObject.
Trap 4: Resource: "*" với action chỉ apply trên *
Một số action như ec2:DescribeInstances chỉ hợp lệ với Resource: "*" (không hỗ trợ resource-level permission). Đừng nhầm là quyền bị "rộng quá" — đôi khi bắt buộc. Check IAM Authorization reference từng action.
9. Câu hỏi ôn tập
-
Trust policy và permissions policy của một IAM Role khác nhau ở chức năng nào?
Xem đáp án
Trust policy quyết định ai được phép assume role (Principal nào được phép gọi
sts:AssumeRole). Permissions policy quyết định sau khi assume thì làm được gì (action/resource nào được Allow).Cả hai cần khớp: nếu trust policy không cho phép B assume, B không thể vào role; nếu trust policy cho phép nhưng permissions rỗng, vào role cũng vô dụng.
-
Tại sao cấp
iam:PassRolevới Resource*lại nguy hiểm?Xem đáp án
iam:PassRolecho phép identity "gắn" một role cho service (Lambda, EC2, ECS). Nếu cấpiam:PassRoletrên*, identity có thể gắn bất kỳ role nào trong account — bao gồm role cóAdministratorAccess— cho Lambda mới của họ, rồi gọi Lambda đó để escalate.Fix: giới hạn Resource là ARN cụ thể của role được phép pass, kèm Condition
iam:PassedToServicecho đúng service. -
Khi có cả Allow (identity policy) và Deny (SCP) trên cùng action, kết quả là gì?
Xem đáp án
DENY. Explicit Deny ở bất kỳ tầng nào (identity policy, resource policy, SCP, permission boundary, session policy) luôn thắng Allow. SCP có Deny → request bị từ chối ngay cả khi identity policy cho phép. Đây là cơ chế cốt lõi để platform team áp guardrail mà developer không thể bypass.
-
ExternalId trong trust policy giải quyết vấn đề gì?
Xem đáp án
Confused deputy problem: khi service B (third-party SaaS) được nhiều customer trust để assume role, một customer ác ý có thể trick B gọi assume role vào account customer khác. ExternalId là shared secret giữa customer A và service B — khi B gọi
sts:AssumeRolevào account A, phải truyền đúng ExternalId của A. Customer ác ý không biết secret nên không impersonate được.ExternalId chỉ hiệu quả nếu mỗi customer có ExternalId khác nhau và secret thực sự không leak.
-
Pattern
Effect: Allow + NotAction: iam:*nguy hiểm vì sao?Xem đáp án
Câu này cấp Allow cho mọi action không phải
iam:*— tức là gần như admin trừ IAM. Khi AWS thêm service mới (vd:bedrock:*), identity tự động có quyền mà bạn không hề review. Khác với pattern thông thường (Allow danh sách action cụ thể) — chỉ cấp đúng action liệt kê.Best practice: dùng
NotActionchỉ vớiEffect: Deny(vd: deny mọi thứ ngoại trừ vài action read-only). TránhAllow + NotAction.
Bài tập thực hành
# 1. Tạo role có trust policy chỉ cho phép Lambda assume
cat > trust-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "lambda.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
EOF
aws iam create-role \
--role-name lab-lambda-exec \
--assume-role-policy-document file://trust-policy.json
# 2. Gắn policy tối thiểu (chỉ ghi log)
aws iam attach-role-policy \
--role-name lab-lambda-exec \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
# 3. Generate Last Accessed report
aws iam generate-service-last-accessed-details \
--arn arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):role/lab-lambda-exec
# 4. Bật Access Analyzer
aws accessanalyzer create-analyzer \
--analyzer-name lab-analyzer \
--type ACCOUNT
aws accessanalyzer list-findings \
--analyzer-arn $(aws accessanalyzer list-analyzers --query 'analyzers[0].arn' --output text)
# 5. Dùng IAM Policy Simulator (UI: https://policysim.aws.amazon.com)
# Test xem identity có thể làm action nào trên resource cụ thể không.
# 6. Cleanup
aws iam detach-role-policy --role-name lab-lambda-exec \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role --role-name lab-lambda-exec