</>Học Dev
Bài học

Tuần 4 - Ngày 19: IAM và Least Privilege

Tuần 4 – Ngày 19

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

IAMModelPrincipalActionResource(Ai?)(Làmgì?)(Trêncáigì?)Condition(Khinào?)Users3:GetObjectarn:aws:s3:::bucketRoleec2:RunInstancesarn:aws:ec2:...Servicedynamodb:Queryarn:aws:dynamodb:...
Khái niệmÝ nghĩaVí dụ
IdentityThực thể có thể xác thựcIAM User alice, Role lambda-exec
PrincipalIdentity đang request actionUser alice gọi s3:GetObject
ActionThao tác trên services3:PutObject, iam:PassRole
ResourceTài nguyên bị tác động (ARN)arn:aws:s3:::my-bucket/*
PolicyJSON document cấp quyềnIdentity-based hoặc resource-based
ConditionRàng buộc thêmaws: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

IAMIdentityTypesUser(conngưi,long-livedcredential)Accesskey(ID+Secret)ConsolepasswordMFAdeviceGroup(collectionofuserschđgánpolicy)developers,admins,billingRole(assumableidentity,temporarycredential)EC2instanceprofileLambdaexecutionroleECStaskroleCross-accountrole
Tiêu chíUserRole
CredentialLong-lived (access key)Temporary (STS, ~1h)
Ai dùngCon ngườiService hoặc identity được trust
AuthenticationPassword / Access keyAssumeRole API call
Best practiceHạn chế tối đa, dùng SSO/IdCMặ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 policyCó field Principal?Hỗ trợ cross-account
Identity-basedKhông (principal ngầm là identity được gắn)Cần resource-based hoặc role assumption
Resource-basedBắt buộcCó (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

AccountB(222222222222)AccountA(111111111111)User:aliceRole:data-reader(identitypolicy:TrustPolicyallowsts:AssumeRole)(allowAccountB)PermissionPolicy(s3:GetObject)Step1.alicests:AssumeRole(RoleArn=A:data-reader)STSStep2.STStemporarycreds(key+secret+token,TTL~1h)aliceStep3.alices3:GetObject(vitemporarycreds)S3inAccountA
# 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:*ecs:* → revoke admin, cấp policy tối thiểu.


7. So sánh ngắn với GCP và Azure

Khái niệmAWS IAMGCP IAMAzure
IdentityUser, Role, ServiceUser, Service AccountUser, Service Principal, Managed Identity
QuyềnPolicy (JSON)Role (predefined/custom) gồm nhiều permissionRole Definition
BindingPolicy attached to identityIAM Binding: (member, role, resource)Role Assignment
ScopePer-resource ARNProject / Folder / Org hierarchySubscription / Resource Group / Resource
Cross-accountAssumeRole + STSWorkload Identity FederationCross-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

IAMPolicyEvaluationStep1:CóExplicitDenybtkpolicynào?YesDENY(final)NotiếpStep2Step2:CóAllowrõràng?YesALLOWNoDENY(defaultdeny-by-default)

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 NotAction chỉ với Effect: Deny (deny mọi thứ ngoại trừ X). Tránh Allow + 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

  1. 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.

  2. Tại sao cấp iam:PassRole với Resource * lại nguy hiểm?

    Xem đáp án

    iam:PassRole cho phép identity "gắn" một role cho service (Lambda, EC2, ECS). Nếu cấp iam:PassRole trê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:PassedToService cho đúng service.

  3. 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.

  4. 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:AssumeRole và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.

  5. 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 NotAction chỉ với Effect: Deny (vd: deny mọi thứ ngoại trừ vài action read-only). Tránh Allow + 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

Tài liệu tham khảo chính thức


Bài tiếp theo →