Kinh nghiệm
8 phút đọc7 tháng 7, 2026253

IAM Deep Dive: Đừng để AWS account của bạn thành cái bẫy

IAM không chỉ là tạo user và gán quyền. Hiểu sai IAM là con đường ngắn nhất để production bị breach hoặc bill AWS tăng vọt.

N

Nguyễn Nhật Long

@nguyennhatlong1303

IAM architecture diagram showing the relationship between Users, Groups, Roles, and Policies with arrows indicating policy attachment, STS token flow for roles, and API call evaluation path, clean flat design on dark background

IAM là thứ mà hầu hết mọi người đều nghĩ mình hiểu, cho đến khi có incident xảy ra. Mình đã từng thấy một team để lộ AWS access key trên GitHub, một team khác thì grant AdministratorAccess cho Lambda function vì "tiện", và một team nữa thì không biết cross-account role là gì cho đến khi cần setup multi-account architecture. Bài này mình sẽ đi thẳng vào những thứ thực sự quan trọng không phải tutorial cơ bản kiểu "tạo user rồi gán policy", mà là cách IAM thực sự hoạt động và tại sao bạn cần hiểu nó đúng từ đầu.

IAM không phải chỉ là "user management"

Nhiều người nhìn vào IAM và nghĩ đây là cái tool để tạo account cho dev team. Thực ra IAM là authorization engine của toàn bộ AWS. Mọi API call đến AWS dù từ CLI, SDK, console, hay Lambda đều phải đi qua IAM để được evaluate.

Architecture của IAM xoay quanh 4 khái niệm:

  • Users: Đại diện cho người thật hoặc application cần long-term credentials. Thực ra best practice hiện tại là hạn chế tạo IAM user, dùng SSO hoặc federated identity thay thế.
  • Groups: Tập hợp users để assign policy dễ hơn. Groups không thể nest (group trong group) cái này hay bị nhầm.
  • Roles: Đây mới là thứ bạn sẽ dùng nhiều nhất. Role không có long-term credentials, thay vào đó nó issue temporary credentials thông qua STS. EC2 instance, Lambda, ECS task, và cả người dùng từ account khác đều assume role để lấy quyền.
  • Policies: JSON document định nghĩa permissions. Gắn vào user, group, hoặc role.
IAM architecture diagram showing the relationship between Users, Groups, Roles, and Policies with arrows indicating policy attachment, STS token flow for roles, and API call evaluation path, clean flat design on dark background

Policy language cái mà bạn cần đọc kỹ

Một IAM policy trông như này:

JSON
1{
2 "Version": "2012-10-17",
3 "Statement": [
4 {
5 "Effect": "Allow",
6 "Action": [
7 "s3:GetObject",
8 "s3:PutObject"
9 ],
10 "Resource": "arn:aws:s3:::my-bucket/*",
11 "Condition": {
12 "StringEquals": {
13 "s3:prefix": "uploads/"
14 }
15 }
16 }
17 ]
18}

Có 4 element chính:

  • Effect: Allow hoặc Deny. Mặc định là deny hết, bạn phải explicit allow.
  • Action: API action nào được phép. Dùng wildcard s3:* thì tiện nhưng nguy hiểm.
  • Resource: ARN của resource cụ thể. Đừng để "Resource": "*" trừ khi thực sự cần.
  • Condition: Điều kiện bổ sung IP range, MFA required, tag matching, thời gian,... Đây là chỗ bạn có thể làm policy rất granular.

Quy tắc evaluation mà nhiều người hay nhầm: Explicit Deny luôn thắng Allow. Nếu có bất kỳ policy nào deny một action, action đó bị block dù có bao nhiêu Allow đi nữa.

Các loại policy và khi nào dùng cái nào

Theo kinh nghiệm của mình, Permission Boundary là thứ bị underuse nhất. Nó cực kỳ hữu ích khi bạn muốn delegate việc tạo role cho team khác nhưng vẫn muốn giới hạn họ không thể tạo role có quyền vượt quá một ngưỡng nhất định. Ví dụ: team A có thể tự tạo role cho Lambda của họ, nhưng không thể tạo role có AdministratorAccess.

Policy TypeGắn vàoUse case
Identity-basedUser / Group / RoleCấp quyền cho principal
Resource-basedResource (S3, SQS, KMS,...)Cross-account access, resource sharing
Permission BoundaryUser / RoleGiới hạn tối đa quyền có thể được cấp
SCP (Service Control Policy)AWS Account / OUGuardrail toàn organization
Session PolicyTemporary sessionRestrict quyền khi assume role

SCPs thì khác hoàn toàn nó không grant quyền, nó chỉ restrict. Dù account có AdministratorAccess, nếu SCP ở Organization level block ec2:TerminateInstances thì không ai trong account đó terminate instance được. Mình thấy cái này cực kỳ powerful cho multi-account setup.

IAM Roles thực sự hoạt động như thế nào

Khi EC2 instance cần gọi S3, flow như sau:

  1. Instance được gắn Instance Profile (wrapper của IAM Role)
  2. EC2 metadata service (169.254.169.254) expose temporary credentials
  3. SDK tự động fetch credentials này, không cần hardcode key
  4. Credentials có TTL (thường 1 giờ) và tự động rotate

Lambda thì tương tự với Execution Role. Mình thấy lỗi phổ biến nhất là dev tạo một Lambda execution role rồi share cho tất cả Lambda functions trong project. Đừng làm vậy mỗi function nên có role riêng với quyền tối thiểu cần thiết.

Cross-account role là pattern cực kỳ hay khi bạn có multi-account setup:

JSON
1// Trust policy của role trong account B (target)
2{
3 "Version": "2012-10-17",
4 "Statement": [
5 {
6 "Effect": "Allow",
7 "Principal": {
8 "AWS": "arn:aws:iam::ACCOUNT_A_ID:root"
9 },
10 "Action": "sts:AssumeRole",
11 "Condition": {
12 "Bool": {
13 "aws:MultiFactorAuthPresent": "true"
14 }
15 }
16 }
17 ]
18}

Account A assume role này để làm việc với resource trong Account B. Không cần tạo user trong Account B, không cần share credentials gì cả.

OIDC Federation GitHub Actions không cần access key

Đây là thứ mình recommend mọi team adopt ngay. Thay vì tạo IAM user, lấy access key rồi nhét vào GitHub Secrets (và lo lắng mỗi khi có người rời team), bạn setup OIDC federation:

YAML
1# GitHub Actions workflow
2jobs:
3 deploy:
4 permissions:
5 id-token: write
6 contents: read
7 steps:
8 - name: Configure AWS credentials
9 uses: aws-actions/configure-aws-credentials@v4
10 with:
11 role-to-assume: arn:aws:iam::123456789:role/github-actions-deploy
12 aws-region: ap-southeast-1

Phía AWS, bạn tạo OIDC Identity Provider với URL

https://token.actions.githubusercontent.com
, rồi tạo role với trust policy cho phép GitHub's OIDC token assume role đó. Có thể restrict theo repo, branch cụ thể:

JSON
1"Condition": {
2 "StringLike": {
3 "token.actions.githubusercontent.com:sub":
4 "repo:your-org/your-repo:ref:refs/heads/main"
5 }
6}

Không còn long-term credentials, không còn rotation headache. Mình đã migrate toàn bộ CI/CD pipeline sang pattern này và không nhìn lại.

Access Analyzer con mắt thứ ba của bạn

IAM Access Analyzer là tool mình thấy nhiều team bỏ qua hoàn toàn. Nó scan resource policies (S3 bucket policy, KMS key policy, IAM role trust policy,...) và alert bạn khi có resource bị expose ra ngoài account hoặc organization.

Enable nó trong từng region, tốn kém không đáng kể, nhưng giá trị nó mang lại rất lớn. Mình đã từng dùng Access Analyzer để phát hiện một S3 bucket có public read access mà không ai trong team biết bucket đó chứa internal documentation.

Ngoài ra, Access Analyzer còn có tính năng policy validation bạn paste policy vào, nó check syntax, security issues, và suggest improvements. Và unused access analysis list ra permissions nào đang được grant nhưng không bao giờ được dùng trong 90 ngày qua.

AWS IAM Access Analyzer dashboard showing a findings panel with detected external access issues, a world map indicating cross-account access origins, and policy recommendations panel, modern AWS console dark theme UI mockup

Tại sao Organizations + SCP là game changer

Nếu bạn đang chạy nhiều hơn một AWS account (và bạn nên production, staging, dev nên tách nhau), AWS Organizations + SCP là thứ bạn cần setup.

SCP cho phép bạn enforce guardrails ở organization level:

JSON
1// Chặn tất cả actions ngoài us-east-1 và ap-southeast-1
2{
3 "Version": "2012-10-17",
4 "Statement": [
5 {
6 "Effect": "Deny",
7 "Action": "*",
8 "Resource": "*",
9 "Condition": {
10 "StringNotEquals": {
11 "aws:RequestedRegion": [
12 "us-east-1",
13 "ap-southeast-1"
14 ]
15 }
16 }
17 }
18 ]
19}

Pattern mình hay dùng là tạo một "baseline SCP" apply cho toàn org: block root account usage, require MFA, restrict region, prevent disabling CloudTrail. Sau đó từng OU (Organizational Unit) có thêm SCP riêng phù hợp với use case.

Setup thực tế cho team

Anh em lưu ý cái checklist này khi setup IAM cho team mới:

Root account lockdown:

  • Enable MFA cho root account ngay lập tức
  • Xóa hoặc không tạo access key cho root
  • Setup billing alerts
  • Không dùng root cho daily operations bao giờ cũng vậy

Cho team members:

Thay vì tạo IAM user, mình recommend dùng AWS IAM Identity Center (trước đây là SSO). Bạn connect với Google Workspace hoặc Okta, team login bằng company account, không cần manage password riêng trong AWS.

Nếu bắt buộc phải dùng IAM user:

Terminal
1# Tạo group với policy phù hợp
2aws iam create-group --group-name developers
3aws iam attach-group-policy \
4 --group-name developers \
5 --policy-arn arn:aws:iam::aws:policy/PowerUserAccess
6
7# Enforce MFA bằng policy
8# Attach policy require MFA cho tất cả actions
9# nếu chưa authenticate bằng MFA

Policy enforce MFA là một JSON khá dài nhưng ý tưởng là: deny tất cả actions (trừ một số actions cần để setup MFA) nếu aws:MultiFactorAuthPresent là false.

Credential rotation:

  • Set password policy: minimum 12 chars, require uppercase/lowercase/number/symbol, expire 90 ngày
  • Access key rotation: 90 ngày là standard, nhiều team làm 30 ngày
  • Dùng AWS Secrets Manager hoặc Parameter Store thay vì hardcode credentials trong code
A step-by-step security checklist flowchart for AWS IAM team setup: root lockdown → enable SSO/Identity Center → create groups with least privilege policies → enforce MFA → setup credential rotation → enable Access Analyzer → configure SCPs, using green checkmarks and connected boxes, modern flat design with AWS color palette

Least privilege không phải slogan, nó là practice hàng ngày

Cái khó nhất của IAM không phải là học syntax policy đó là maintain least privilege theo thời gian. Ban đầu bạn grant quyền tối thiểu, nhưng sau 6 tháng, feature mới thêm vào, permissions được thêm vào để unblock dev, và dần dần mọi thứ drift.

Mình dùng một số approach:

  1. IAM Access Analyzer unused access: Review hàng tháng, revoke permissions không dùng
  2. AWS Config rules: Alert khi có policy quá broad (ví dụ *:* với Resource: *)
  3. CloudTrail + Athena: Query xem ai đang dùng quyền gì thực sự, từ đó tighten policy
  4. Terraform/CDK cho IAM: Khi IAM được manage as code, mọi thay đổi đều có review process, không ai tự ý thêm quyền qua console

Theo kinh nghiệm của mình, cái cuối cùng IaC cho IAM là impactful nhất. Khi dev cần thêm quyền cho service, họ phải tạo PR, có người review, có audit trail. Không còn chuyện "mình chỉ add tạm thôi" rồi quên mất.

IAM là thứ bạn đầu tư setup đúng một lần và hưởng lợi mãi. Làm sai từ đầu thì technical debt rất khó clean up đặc biệt khi organization lớn dần và số lượng accounts, roles, policies tăng lên. Bắt đầu đúng, maintain thường xuyên, và đừng bao giờ compromise security vì "tiện".

Bài tiếp theo trong series: EC2 Deep Dive Từ Instance Types đến Auto Scaling

VPC là con đường, EC2 là những chiếc xe chạy trên đó. Giờ bạn đã có network foundation vững đã hiểu subnet nào public, subnet nào private, traffic đi qua đâu bước tiếp theo là đặt compute resources vào đúng chỗ. Mình sẽ đi từ cách chọn instance type (hint: Graviton ARM rẻ hơn 20% mà performance ngang ngửa), EBS storage, AMI strategy, đến Auto Scaling Group + ALB cho production. Kèm hands-on deploy Next.js app với full HA setup.

NN

Nguyễn Nhật Long

@nguyennhatlong1303

Nguyễn Nhật Long is a Senior Frontend Engineer and Frontend Team Leader with 7 years of experience building real-time fintech platforms. Specializing in React, Next.js, TypeScript, and React Native, shipping 10+ products across Web, Mobile, Telegram Mini-Apps, and Web3.

Thấy hay? Chia sẻ cho bạn bè!

IAM Deep Dive: Đừng để AWS account của bạn thành cái bẫy — Stacklog