AWS IAM: Hiểu Đúng Từ Nền Tảng

Hầu hết tài liệu về AWS IAM dạy theo thứ tự: IAM User → Group → Policy → Role. Đọc xong vẫn không hiểu cái đéo gì vì đó là thứ tự AWS present trên Console cho đẹp, không phải thứ tự để hiểu. Bài này đi từ nền tảng lên – hiểu cái gốc trước, mọi thứ còn lại tự khắc click.

Policy – Thứ Duy Nhất Thực Sự Quan Trọng

Mọi người cứ hay nói về IAM User, Group, Role – nhưng thực ra tất cả chỉ là vỏ bọc. Policy mới là thứ thực sự định nghĩa “được làm gì, không được làm gì.” Hiểu Policy là hiểu 80% IAM rồi.

Policy là một JSON document gồm 2 phần tách biệt – và đây là điểm nhiều người không để ý:

Metadata (định danh bên ngoài):

1
2
3
4
5
{
"PolicyName": "S3ReadPolicy",
"PolicyID": "ANPAEXAMPLE123",
"ARN": "arn:aws:iam::123456789012:policy/S3ReadPolicy"
}

Document (logic thực sự – phần quan trọng):

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

Quy tắc cốt lõi: Deny luôn thắng Allow, không có ngoại lệ.

1
2
3
4
5
6
7
Mặc định = Deny

Có Explicit Deny? → Deny ngay, không cần xem tiếp

Có Allow? → Allow

Không có gì? → Implicit Deny (vẫn là Deny)

Và cái hay của Policy là reusable – một Policy attach vào bao nhiêu User, Group, Role tùy thích. Muốn thay đổi quyền thì sửa một chỗ, tất cả nơi dùng Policy đó đều được cập nhật ngay. Không phải đi sửa từng thằng một như thằng ngốc.

ARN – Địa Chỉ Duy Nhất Của Mọi Thứ Trên AWS

Trước khi nói về User, Group, Role – cần hiểu ARN vì nó xuất hiện ở khắp nơi.

1
2
3
arn:aws:iam::123456789012:user/hudson
arn:aws:s3:::my-bucket
arn:aws:lambda:ap-southeast-1:123456789012:function:my-function

Cấu trúc:

1
arn : partition : service : region : account-id : resource
  • partition – thường là aws. Xong.
  • serviceiam, s3, lambda, ec2
  • region – IAM bỏ trống vì global. S3 cũng bỏ trống trong ARN vì bucket name đã unique toàn cầu – nhưng đừng nhầm: S3 vẫn là Regional Service, dữ liệu vẫn nằm cố định ở một Region cụ thể.
  • account-id – số tài khoản AWS
  • resource – tên resource cụ thể

Tại sao cần ARN khi đã có UniqueID? Vì ARN encode đủ context trong một string – service nào, account nào, region nào. Policy cần ARN để reference chính xác “thằng này ở đâu”, chứ UniqueID như AIDAEXAMPLE123 thì biết đéo gì.

IAM User – Đại Diện Cho Con Người

IAM User đại diện cho một người cụ thể. Object trông như này:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"Metadata": {
"UserName": "hudson",
"UniqueID": "AIDAEXAMPLE123",
"ARN": "arn:aws:iam::123456789012:user/hudson"
},
"Credentials": {
"PasswordHash": "<salted_hash>",
"AccessKeys": ["AKIAEXAMPLE123"],
"MFADevices": ["arn:aws:iam::123456789012:mfa/hudson"]
},
"Authorization": {
"GroupMemberships": ["arn:aws:iam::123456789012:group/Developers"],
"AttachedPolicies": ["arn:aws:iam::123456789012:policy/S3ReadPolicy"]
}
}

2 phần quan trọng nhất:

  • Credentials – “Mày là ai?”
  • Authorization Links – “Mày được làm gì?”

UniqueID là bất biến – đổi username thì ID vẫn không đổi. AWS dùng cái này để track nội bộ.

IAM Group – Gom Người Lại Cho Tiện

Không có gì phức tạp. Group là cách gom nhiều User lại để quản lý quyền chung – thay vì đi attach Policy từng thằng một như thằng điên.

1
2
3
4
5
6
7
8
9
10
11
12
{
"GroupName": "Developers",
"GroupID": "AGPAEXAMPLE456",
"ARN": "arn:aws:iam::123456789012:group/Developers",
"Members": [
"arn:aws:iam::123456789012:user/hudson",
"arn:aws:iam::123456789012:user/alice"
],
"AttachedPolicies": [
"arn:aws:iam::123456789012:policy/S3ReadPolicy"
]
}

Điểm cần nhớ:

  • Group không thể chứa Group khác – chỉ chứa User thôi
  • User thuộc nhiều Group thì quyền là tổng hợp tất cả Policy – nhưng Deny từ bất kỳ đâu vẫn thắng hết

IAM Role – Quyền Tạm Thời Cho Service

Đây là phần nhiều người bị confuse nhất – vì Role khác hoàn toàn với User.

Role không thuộc về ai cố định. Không có credentials cố định. Được thiết kế cho service như Lambda, EC2 – những thứ không có username hay password.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"RoleName": "EC2S3SQSRole",
"RoleID": "AROAEXAMPLE789",
"ARN": "arn:aws:iam::123456789012:role/EC2S3SQSRole",

"TrustPolicy": {
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
},

"AttachedPolicies": [
"arn:aws:iam::123456789012:policy/S3ReadPolicy",
"arn:aws:iam::123456789012:policy/SQSAccessPolicy"
]
}

Một điểm quan trọng mà docs hay bỏ qua: không thể gán Role thẳng vào EC2 được. AWS yêu cầu phải có một object trung gian gọi là Instance Profile:

1
2
3
4
5
IAM Role

Instance Profile (wrapper bắt buộc)

EC2 Instance

Instance Profile chỉ là một cái vỏ bọc chứa Role – 1 Instance Profile chứa đúng 1 Role. Khi tạo Role cho EC2 trên Console, AWS tự động tạo Instance Profile cùng tên nên mình không để ý. Nhưng nếu dùng CLI hay Terraform thì phải tạo thủ công, không tạo là gán không được.

Role phải trả lời 2 câu hỏi:

1
2
TrustPolicy      → "Thằng nào được phép dùng Role này?"
AttachedPolicies → "Sau khi dùng thì được làm gì?"

Mental model: TrustPolicy giống như visa, còn AttachedPolicies là luật pháp trong nước. Có visa không có nghĩa làm gì cũng được; có quyền bên trong mà không có visa thì cũng vào không được.

Analogy chuẩn nhất là quy trình xin visa:

1
2
TrustPolicy      = Visa (được phép nhập cảnh không?)
AttachedPolicies = Luật pháp trong nước (vào rồi được làm gì?)

Hai lớp độc lập – thiếu một là xong.

Và đây là lý do TrustPolicy tồn tại: nếu không có nó, bất kỳ service nào cũng có thể assume Role đó – kể cả hacker compromise được một service bất kỳ. TrustPolicy là lớp bảo vệ đầu tiên, chặn ngay trước khi Policy bên trong được evaluate.

Access Key vs API Key – Đừng Nhầm

Access Key – long-term credentials gắn với IAM User:

1
2
3
4
5
{
"AccessKeyId": "AKIAEXAMPLE123",
"SecretAccessKey": "xxxxxxxxxxx",
"Status": "Active"
}
  • AccessKeyId bắt đầu bằng AKIA – đây là dấu hiệu long-term credentials
  • SecretAccessKey chỉ hiển thị một lần duy nhất – mất thì tạo lại, không recover được
  • Quyền vẫn phụ thuộc hoàn toàn vào Policy – Access Key chỉ là cơ chế authentication, không phải authorization

API Key (service-specific credentials) – chỉ dùng được cho một service cụ thể:

1
2
3
4
5
6
{
"ServiceName": "codecommit.amazonaws.com",
"ServiceUserName": "hudson-at-123456789012",
"ServicePassword": "xxxxxxxxxxx",
"Status": "Active"
}

Sự khác biệt thực sự:

Access Key API Key
Authenticate với Mọi AWS service Một service cụ thể
Format KeyId + Secret Username + Password
Expire Không tự động Có thể set

API Key ra đời để giải quyết bài toán security scope – lỡ bị lộ thì hacker cũng chỉ access được đúng một service, không phải toàn bộ AWS account.

STS – Cái Máy In Tiền Tạm Thời

STS (Security Token Service) là service đứng sau toàn bộ cơ chế temporary credentials. Mỗi khi EC2 hay Lambda assume Role, STS generate ra bộ credentials tạm thời:

1
2
3
4
5
6
{
"AccessKeyId": "ASIAEXAMPLE123",
"SecretAccessKey": "xxxxxxxxxxx",
"SessionToken": "xxxxxxxxxxx",
"Expiration": "2024-01-15T10:00:00Z"
}

AccessKeyId bắt đầu bằng ASIA – AWS nhìn vào prefix này biết ngay đây là temporary credentials.

Tại sao cần cả 3 thứ?

Đây là phần confuse nhất – và câu trả lời không đơn giản như mình nghĩ ban đầu.

SecretAccessKeySessionToken giải quyết 2 vấn đề hoàn toàn khác nhau:

1
2
3
4
5
6
7
8
SecretAccessKey → dùng ở CLIENT-SIDE để sign HTTP request (AWS SigV4)
KHÔNG BAO GIỜ truyền đi qua mạng
chứng minh "mày đúng là chủ sở hữu credentials này"

SessionToken → gửi kèm trong MỌI HTTP request (header: x-amz-security-token)
chứa SecretAccessKey đã mã hóa + permissions + expiration
AWS giải mã ra, tính lại signature, so sánh
→ không cần lookup database

SessionToken thực chất là gì?

Đây là phần hay nhất – SessionToken chứa SecretAccessKey bên trong nó, dưới dạng đã được mã hóa bằng Master Key của AWS STS.

Flow khi S3 nhận request:

1
2
3
4
5
6
7
8
9
10
11
EC2 gửi request kèm:
Header: Signature (ký bằng SecretAccessKey)
Header: SessionToken (chứa SecretAccessKey đã mã hóa)

S3 nhận request:
1. Giải mã SessionToken bằng Master Key
2. Lấy SecretAccessKey ra từ bên trong
3. Tự tính lại Signature
4. So sánh với Signature EC2 gửi lên

Trùng khớp → Allow ✓

Stateless hoàn toàn – S3 không cần gọi về STS hay database để verify. Chỉ cần Master Key trong RAM, giải mã xong trong vài microseconds. Đây là lý do AWS xử lý được hàng tỷ request mỗi giây mà không chết.

Tại sao không dùng Asymmetric Cryptography (RSA/ECDSA)?

Câu hỏi hợp lý – JWT dùng RS256, TLS dùng RSA, tại sao AWS SigV4 lại dùng HMAC-SHA256?

3 lý do:

1. Performance – HMAC-SHA256 nhanh hơn RSA từ 10-100 lần. CPU có hardware acceleration sẵn cho SHA-256. Với hàng chục triệu request mỗi giây, chuyển sang RSA thì chi phí CPU tăng vọt và latency tăng đáng kể.

2. AWS kiểm soát cả 2 đầu – Điểm yếu của symmetric cryptography là chia sẻ key giữa 2 bên không biết nhau. Nhưng AWS không có vấn đề này – STS và S3/DynamoDB đều là của họ, truyền SecretAccessKey an toàn bằng cách nhúng vào SessionToken. Không cần asymmetric để giải quyết bài toán key exchange.

3. Payload size – HMAC-SHA256 chỉ 32 bytes. RSA-2048 là 256 bytes. Nhân với hàng tỷ request thì tiết kiệm băng thông rất lớn.

HMAC-SHA256 (AWS đang dùng) RSA/ECDSA
Tốc độ verify Cực nhanh (microseconds) Chậm hơn 10-100 lần
CPU overhead Thấp, có hardware acceleration Cao, tốn phép toán số lớn
Signature size 32 bytes 256-512 bytes
Key exchange Cần kênh an toàn Public key phát công khai
Dùng khi Kiểm soát cả 2 đầu (AWS) Hai bên không biết nhau (Internet)

Tổng Kết

1
2
3
4
5
6
7
8
9
10
11
Policy (nền tảng -- định nghĩa quyền)

attach vào...
├── IAM User (con người, long-term credentials)
├── IAM Group (tập hợp người, inherit policy từ Group)
└── IAM Role (service, temporary credentials từ STS)

Credentials:
├── Access Key (AKIA...) → long-term, authenticate mọi service
├── API Key → long-term, một service cụ thể
└── STS Token (ASIA...) → short-term, tự động refresh, stateless verify

Flow cơ bản nhất – con người dùng AWS:

1
Tạo IAM User → Attach Policy → Generate Access Key → Call API

Flow khi build application trên AWS:

1
2
3
Tạo Policy → Tạo Role + TrustPolicy → Gán Role vào EC2/Lambda
→ SDK tự động lấy temporary credentials từ STS
→ Call API mà không cần hard-code credentials

Điểm cần nhớ: Hard-code Access Key vào source code là hành động của thằng không biết Role là gì. Đừng làm vậy.