Skip to content

IAM Policy Model — Allow Policy, Evaluation Order và Principal Access Boundary

Tại sao cần hiểu sâu policy model

Hầu hết engineers làm việc với GCP IAM theo cách "thêm role binding, test xem access được chưa". Cách này hoạt động cho use case đơn giản nhưng sẽ thất bại khi hệ thống phức tạp hơn: access bị deny không rõ lý do, tại sao revoke role ở project-level nhưng user vẫn access được, tại sao thêm deny policy nhưng user vẫn vào được, tại sao IAM check pass nhưng vẫn bị block.

Những vấn đề này đều có nguyên nhân nằm trong IAM policy model và evaluation pipeline — ba pha evaluation, cách policy kế thừa theo hierarchy, và sự tương tác giữa các policy type. Hiểu đúng model này là nền tảng để debug và thiết kế access control chính xác.


Internal model: IAM không phải là một policy đơn

Trước hết, cần phá vỡ một hiểu lầm phổ biến: IAM trong GCP không phải là "một policy" mà là một hệ thống gồm nhiều loại policy được evaluate theo thứ tự xác định.

Theo tài liệu chính thức, GCP có bốn loại policy chính trong hệ thống IAM:

  1. Allow Policies — cấp quyền (gắn 1-1 với resource)
  2. Deny Policies — từ chối quyền (gắn nhiều với resource, tối đa 500 per resource)
  3. Principal Access Boundary (PAB) Policies — giới hạn resource nào principal được phép truy cập
  4. Access Policies — kiểm soát Eventarc resources (phạm vi hẹp, ít phổ biến)

Trong đó Allow PolicyDeny Policy là những gì hầu hết engineer tương tác hàng ngày. PAB là cơ chế mới hơn (IAM v3 API) và quan trọng ở enterprise scale.


Allow Policy: Cấu trúc bên trong

Allow policy là object được gắn vào một resource GCP. Cần hiểu rõ ràng: mỗi resource chỉ có đúng một allow policy, và policy đó chứa một danh sách các role bindings.

Cấu trúc của một Allow Policy

json
{
  "version": 3,
  "bindings": [
    {
      "role": "roles/storage.objectViewer",
      "members": [
        "user:alice@example.com",
        "serviceAccount:my-sa@my-project.iam.gserviceaccount.com",
        "group:data-team@example.com"
      ],
      "condition": {
        "title": "Chỉ đọc trong giờ hành chính",
        "expression": "request.time.getHours('Asia/Ho_Chi_Minh') >= 8 && request.time.getHours('Asia/Ho_Chi_Minh') <= 17"
      }
    },
    {
      "role": "roles/storage.admin",
      "members": [
        "user:bob@example.com"
      ]
    }
  ],
  "etag": "BwWWja0YfJA=",
  "version": 3
}

Mỗi binding gồm:

  • role: một role (tập hợp permissions)
  • members (hoặc principals trong API v3): danh sách principal được gán role
  • condition (optional): CEL expression kiểm soát khi nào binding có hiệu lực

Version field quan trọng: version 1 không hỗ trợ conditions, version 3 bắt buộc nếu có conditional bindings.

Các loại Principal (Members)

GCP IAM nhận diện các principal types sau:

Principal IdentifierLoại
user:EMAILGoogle Account cá nhân
serviceAccount:EMAILService Account
group:EMAILGoogle Group
domain:DOMAINGoogle Workspace domain
principalSet://iam.googleapis.com/...Workload Identity pool members
allUsersBất kỳ ai (kể cả không auth)
allAuthenticatedUsersBất kỳ Google Account nào

Cảnh báo production: allUsersallAuthenticatedUsers là anti-pattern nghiêm trọng. allAuthenticatedUsers không có nghĩa là "user trong tổ chức của bạn" — nó bao gồm mọi Google Account trên thế giới. Không bao giờ dùng hai identifier này cho resource sensitive.

Policy etag — Optimistic Locking

etag field trong allow policy là cơ chế optimistic locking để tránh concurrent modification conflict. Khi bạn đọc policy (GET), nhận được etag; khi set lại (SET), phải gửi kèm etag đó. Nếu policy đã bị người khác thay đổi trong thời gian đó, SET sẽ fail với error 409 Conflict.

Đây là pattern quan trọng trong automation: các tool như Terraform và gcloud đều handle etag tự động, nhưng nếu viết code tự cập nhật IAM policy, bắt buộc phải implement read-modify-write với etag.


Ba pha evaluation: PAB → Deny → Allow

Đây là phần quan trọng nhất của model. Mỗi IAM authorization check đi qua ba pha theo thứ tự:

Pha 1: Principal Access Boundary (PAB) Check

PAB là policy type mới nhất (IAM v3). Về bản chất, PAB trả lời câu hỏi: "Principal này có được phép tương tác với resource này không?" — trước khi xét xem họ có role gì.

PAB policies gắn với principal set (một nhóm principal, ví dụ tất cả service accounts trong một project) và định nghĩa danh sách resource eligibility — những resource nào principal set đó được phép truy cập.

Nếu request resource không nằm trong PAB policy của principal → access bị deny ngay ở Pha 1, không cần check tiếp.

PAB giải quyết một vấn đề IAM allow policy không giải được: ngăn lateral movement. Ví dụ: một service account trong production project không nên được phép access resource trong staging project, kể cả khi vô tình được grant role ở đó. PAB enforce điều này ở level principal, không phải level permission.

Binding structure của PAB:

  • Một PAB policy gắn với tối đa unlimited principal sets
  • Một principal set có thể có tối đa 10 PAB policies
  • PAB policies là children của organization node

Pha 2: Deny Policy Check

Nếu pass Pha 1, IAM kiểm tra các deny policies applicable cho resource và action được request.

Theo GCP documentation: "IAM always checks relevant deny policies before checking relevant allow policies."

Deny policies gắn với resources và kế thừa theo hierarchy (giống allow policies). Một resource có thể có tối đa 500 deny policies. IAM collect tất cả deny policies applicable cho resource (từ resource đó và tất cả ancestors trong hierarchy), sau đó kiểm tra từng deny rule.

Nếu bất kỳ deny rule nào match và condition evaluate to true (hoặc không evaluate được) → access bị deny. Không cần check allow policies nữa.

Chi tiết về deny policies xem ở Chapter 31 - File 05.

Pha 3: Allow Policy Check

Nếu pass cả hai pha trên, IAM kiểm tra allow policies để xem principal có permission cần thiết không.

Allow policies được collect từ resource target và tất cả ancestor resources trong hierarchy (project, folder, organization). Đây là cơ chế policy inheritance. Một principal được grant role ở organization level tự động có role đó trên mọi resource trong organization.

IAM tổng hợp effective policy bằng cách union tất cả bindings từ tất cả levels. Nếu bất kỳ binding nào trong effective policy grant permission cần thiết → access được phép.

Không có "deny" trong allow policy. Allow policies chỉ có grant, không có explicit deny. Muốn deny, phải dùng Deny Policy (Pha 2) hoặc đơn giản là không grant.


Effective Policy: Union across hierarchy

Khái niệm effective policy là nền tảng để understand tại sao access có thể "đến từ đâu không biết".

Cho một resource R trong hierarchy: Organization → Folder → Project → Resource:

Effective Policy của R = 
    Policy(Organization) ∪ Policy(Folder) ∪ Policy(Project) ∪ Policy(R)

Ví dụ cụ thể: alice@example.com được grant roles/viewer ở Organization level. Dù không có bất kỳ binding nào ở Project hay Resource level cho alice, cô ta vẫn là Viewer trên mọi resource trong organization. Không có cách nào remove access của alice ở Project/Resource level bằng allow policy — inheritance là one-way, không override được bằng cách "không thêm binding".

Đây là điểm nguy hiểm nhất của IAM model: access granted ở level cao hơn không thể bị revoked ở level thấp hơn thông qua allow policies. Để block, phải dùng Deny Policy hoặc PAB Policy.

Điều kiện biên: Conditions và effective policy

Khi một binding có condition, binding đó chỉ được tính vào effective policy nếu condition evaluate to true. Nếu condition không evaluate được (ví dụ attribute không available cho resource type đó), binding được coi như không tồn tại.

Đây là một subtlety quan trọng: một conditional binding với condition resource.type == 'storage.googleapis.com/Bucket' áp dụng cho bucket resource, nhưng không có hiệu lực khi applied cho Compute Engine instance — không phải "deny", mà "binding không được tính".


Organization Policy: Layer riêng biệt với IAM

Organization Policy Service là một hệ thống riêng biệt hoàn toàn với IAM. Nó không nằm trong IAM evaluation pipeline (PAB → Deny → Allow). Thay vào đó, Organization Policies được enforce ở GCP API resource layer — tức là trước khi IAM được gọi.

Phân biệt rõ ràng:

  • IAM kiểm soát who có thể làm what action trên resource
  • Organization Policy kiểm soát what configurations resource được phép có

Ví dụ: Organization Policy constraints/compute.vmExternalIpAddresses với value deny ngăn việc tạo VM với external IP — bất kể user có roles/compute.instanceAdmin hay không. User có thể có đủ IAM permission để tạo VM, nhưng Org Policy block ở API layer trước khi request đến IAM check.

Chi tiết về Organization Policies ở Chapter 31 - File 09.


Một request đi qua đâu: Full flow

Để consolidate, đây là full flow của một IAM authorization check:

1. Request: alice@example.com calls storage.objects.get 
   trên gs://my-bucket/data.csv

2. GCP API layer:
   → Kiểm tra Organization Policies (nếu có constraint áp dụng)
   → Nếu vi phạm org policy → HTTP 403 với POLICY_VIOLATION reason
   
3. IAM Pha 1 — PAB Check:
   → Lấy tất cả PAB policies gắn với alice@example.com
   → Kiểm tra my-bucket có trong resource eligibility không
   → Nếu không → HTTP 403 với IAM_PERMISSION_DENIED

4. IAM Pha 2 — Deny Check:
   → Collect deny policies từ my-bucket + project + folder + org
   → Kiểm tra có deny rule nào match alice + storage.objects.get không
   → Nếu có → HTTP 403 với IAM_PERMISSION_DENIED

5. IAM Pha 3 — Allow Check:
   → Collect allow policies từ my-bucket + project + folder + org
   → Tổng hợp effective policy
   → Kiểm tra có binding nào grant storage.objects.get cho alice không
   → Nếu không → HTTP 403 với IAM_PERMISSION_DENIED
   → Nếu có → Request được phép

Constraints quan trọng cần biết

Policy size limit

Một allow policy có giới hạn size: không vượt quá 64KB. Khi organization lớn với nhiều bindings, giới hạn này có thể bị hit. Giải pháp: dùng Google Groups thay vì individual user bindings — một binding cho một group thay thế N bindings cho N users.

Conditional binding limit

Mỗi allow policy có thể có tối đa 100 conditional role bindings. Bindings không có condition không tính vào giới hạn này.

Version compatibility

Allow policy version: 1 không hỗ trợ conditions và không support policy với conditional bindings. Khi GET policy của resource có conditional bindings với version 1, API trả về error. Phải request version: 3 explicitly.

bash
# Luôn dùng version 3 khi làm việc với policies
gcloud projects get-iam-policy MY_PROJECT \
  --format=json \
  | jq '.version'
# Nếu trả về 1 nhưng có conditional bindings → phải request v3:
gcloud projects get-iam-policy MY_PROJECT \
  --format=json \
  --filter="version=3"

Inheritance không thể bị override bằng "không grant"

Như đã nói, nếu alice có role ở Organization level, không có cách nào "revoke" ở Project level bằng cách không thêm binding. Muốn restrict, phải dùng Deny Policy ở Project level hoặc remove role ở Organization level.


Anti-pattern: Nhầm lẫn IAM với Authorization System đơn giản

Một sai lầm phổ biến là nghĩ IAM hoạt động như một ACL đơn giản: "chỉ cần add người vào resource là xong". Mô hình này bỏ qua:

  1. Inheritance: access có thể đến từ bất kỳ level nào trong hierarchy
  2. Multiple policy types: allow policy chỉ là một trong bốn loại
  3. Evaluation order: deny check trước allow, PAB trước deny
  4. Eventual consistency: change không có effect ngay lập tức

Khi debug "tại sao user này vẫn có access sau khi revoke role", cần kiểm tra theo thứ tự:

  1. User có role ở ancestor level (org/folder) không? → Effective policy
  2. User có trong group nào được grant role không? → Group membership
  3. User có impersonation chain nào không? → Service account impersonation
  4. Có PAB policy nào prevent resource check không? → PAB
  5. Caching của IAM có delay không? → Propagation delay (thường 60 giây, có thể đến vài phút)

References