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.
Nguyễn Nhật Long
@nguyennhatlong1303

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.

Policy language cái mà bạn cần đọc kỹ
Một IAM policy trông như này:
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:
AllowhoặcDeny. 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 Type | Gắn vào | Use case |
|---|---|---|
| Identity-based | User / Group / Role | Cấp quyền cho principal |
| Resource-based | Resource (S3, SQS, KMS,...) | Cross-account access, resource sharing |
| Permission Boundary | User / Role | Giới hạn tối đa quyền có thể được cấp |
| SCP (Service Control Policy) | AWS Account / OU | Guardrail toàn organization |
| Session Policy | Temporary session | Restrict 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:
- Instance được gắn Instance Profile (wrapper của IAM Role)
- EC2 metadata service (
169.254.169.254) expose temporary credentials - SDK tự động fetch credentials này, không cần hardcode key
- 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:
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:
1# GitHub Actions workflow2jobs:3 deploy:4 permissions:5 id-token: write6 contents: read7 steps:8 - name: Configure AWS credentials9 uses: aws-actions/configure-aws-credentials@v410 with:11 role-to-assume: arn:aws:iam::123456789:role/github-actions-deploy12 aws-region: ap-southeast-1
Phía AWS, bạn tạo OIDC Identity Provider với URL
https://token.actions.githubusercontent.com1"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.

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:
1// Chặn tất cả actions ngoài us-east-1 và ap-southeast-12{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:
1# Tạo group với policy phù hợp2aws iam create-group --group-name developers3aws iam attach-group-policy \4 --group-name developers \5 --policy-arn arn:aws:iam::aws:policy/PowerUserAccess67# Enforce MFA bằng policy8# Attach policy require MFA cho tất cả actions9# 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

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:
- IAM Access Analyzer unused access: Review hàng tháng, revoke permissions không dùng
- AWS Config rules: Alert khi có policy quá broad (ví dụ
*:*vớiResource: *) - CloudTrail + Athena: Query xem ai đang dùng quyền gì thực sự, từ đó tighten policy
- 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.
Nguyễn Nhật Long
@nguyennhatlong1303Nguyễ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è!
