Organization Policy Framework & Inheritance
Tại sao Organization Policy quan trọng
IAM kiểm soát ai có quyền làm gì. Nhưng ngay cả với IAM chặt chẽ, một developer với quyền Compute Admin hợp lệ vẫn có thể vô tình tạo VM với external IP, tắt shielded boot, hay cho phép public access đến Cloud Storage bucket. Đây không phải lỗi IAM — người dùng đang dùng quyền của họ một cách hợp lệ. Nhưng từ góc nhìn organization security policy, đây là misconfiguration.
Organization Policies giải quyết vấn đề này bằng cách ràng buộc hành vi tạo và cấu hình tài nguyên ở tầng GCP resource management, phía trên IAM. Ngay cả khi một danh tính có quyền IAM đầy đủ, nếu action đó vi phạm org policy, GCP sẽ từ chối operation đó.
Tư duy đúng: Org Policy là guardrail của tổ chức — ràng buộc "what is allowed to be created", không phải "who can create it". IAM ràng buộc "who can do what", còn Org Policy ràng buộc "what kinds of resources/configurations are allowed to exist".
Internal Model: Constraint Framework
Constraint là gì?
Constraint là định nghĩa của một ràng buộc — một quy tắc về configuration cho phép hoặc không cho phép. Constraint xác định:
- Resource types nó áp dụng lên (VMs, buckets, clusters, ...)
- What can be controlled (external IP, boot disk encryption, ...)
- Constraint type: List (danh sách giá trị) hay Boolean (bật/tắt)
Organization Policy là việc enforce một constraint tại một node trong resource hierarchy (organization, folder, project). Nói cách khác:
- Constraint: "quy tắc" (definition)
- Organization Policy: constraint đó được apply như thế nào tại một level cụ thể
Hai loại constraint chính
Boolean constraints: On/off switch. Chỉ có hai giá trị: enforced (true) hoặc not enforced (false).
# Ví dụ boolean constraint
name: projects/my-project/policies/compute.requireOsLogin
spec:
rules:
- enforce: true # Bật OS Login requirement trên project nàyList constraints: Định nghĩa một danh sách giá trị được phép hoặc bị cấm.
# Ví dụ list constraint
name: organizations/123456789/policies/compute.restrictCloudNATUsage
spec:
rules:
- denyAll: "TRUE" # Block tất cả Cloud NAT usage
# Hoặc:
# - values:
# allowedValues:
# - "under:projects/allowed-project"
# deniedValues:
# - "is:projects/denied-project"Managed vs Legacy constraints
Google đã giới thiệu thế hệ constraint mới gọi là managed constraints (trước đây gọi là new constraints). Sự khác biệt quan trọng:
Legacy managed constraints: Thế hệ cũ, sử dụng cú pháp constraints/SERVICE.CONSTRAINT_NAME. Inheritance là merge-based: policy từ parent và child được merge lại khi evaluate.
Managed constraints: Thế hệ mới (với prefix compute.managed., container.managed., etc.). Inheritance là override-based: policy tại child resource không được merge với parent — child policy tự nó là effective policy.
Đây là sự khác biệt cực kỳ quan trọng cho inheritance logic:
Organization: Deny all external IPs (legacy constraint)
└── Folder: Allow external IPs for dev (legacy)
└── Project: Deny all external IPs (legacy)
LEGACY MERGE:
Effective policy tại Project = Organization DENY + Folder ALLOW + Project DENY
= Still DENY (DENY takes precedence in merge)
MANAGED CONSTRAINT:
Effective policy tại Project = Project policy only (override, no merge)
= DENY (chỉ xem project policy)Behavior khác nhau này là nguyên nhân phổ biến gây confusion khi migration từ legacy sang managed constraints.
Policy Inheritance: Cơ Chế Lan Truyền
Hierarchy inheritance flow
Policies được áp dụng tại một node trong hierarchy và tự động kế thừa xuống tất cả descendants:
Organization (policies ở level này ảnh hưởng TẤT CẢ)
├── Folder: Engineering
│ ├── Folder: Production
│ │ ├── Project: prod-api ← Kế thừa từ Engineering + Production + Org
│ │ └── Project: prod-db ← Kế thừa từ Engineering + Production + Org
│ └── Folder: Staging
│ └── Project: staging-api ← Kế thừa từ Engineering + Staging + Org
└── Folder: Operations
└── Project: ops-tooling ← Kế thừa từ Operations + OrgMerge semantics cho legacy constraints
Đối với legacy managed constraints, khi một resource có policies từ nhiều levels trong hierarchy, chúng được merge theo các quy tắc sau:
List constraints — Merge rules:
| Policy | Kết quả |
|---|---|
Parent: denyAll, Child: không có policy | Effective: denyAll (inherit) |
Parent: allowAll, Child: denyAll | Effective: denyAll (child overrides) |
| Parent: deny [A, B], Child: allow [C] | Effective: deny [A, B] AND restrict to [C] |
| Parent: allow [A], Child: allow [B] | Effective: allow [A OR B] (merge) |
Quan trọng: Với legacy constraints, denyAll tại bất kỳ level nào luôn wins — không thể override bằng allowAll tại level thấp hơn (trừ khi dùng exceptions).
Boolean constraints — Merge rules:
Boolean constraints không có merge — boolean enforce tại bất kỳ level nào trong hierarchy đều enforce cho tất cả descendants.
Override vs merge cho managed constraints
Với managed constraints (thế hệ mới), không có merge. Policy tại node gần nhất với resource là effective policy:
Organization: managed constraint — restrict external IPs
└── Folder: managed constraint — allow external IPs for dev projects
Project trong Folder:
Effective policy = Folder's policy (allow external IPs)
Organization's policy bị OVERRIDE bởi Folder's policyĐây là behavior khác với legacy constraints và phù hợp hơn với expectation của engineers khi setting policy hierarchy.
restoreDefault và inherit_from_parent
Khi muốn một resource không bị restrict bởi organization policy và thay vào đó inherit Google's default behavior:
# Reset về Google default (không inherit từ parent)
name: projects/my-project/policies/compute.requireOsLogin
spec:
rules:
- enforce: false # Cho boolean
# Hoặc dùng restoreDefault để không inherit từ parent:
reset: trueNgược lại, inheritFromParent: true trong legacy constraints cho phép một resource explicitly inherit từ parent thay vì define policy riêng.
Managed Constraints Quan Trọng Nhất
Compute Engine
| Constraint | Type | Mục đích |
|---|---|---|
constraints/compute.vmExternalIpAccess | List | Block external IPs cho VMs |
constraints/compute.restrictCloudNATUsage | List | Kiểm soát Cloud NAT usage |
constraints/compute.requireOsLogin | Boolean | Bắt buộc OS Login |
constraints/compute.requireShieldedVm | Boolean | Yêu cầu Shielded VM |
constraints/compute.restrictLoadBalancerCreationForTypes | List | Giới hạn LB types |
constraints/compute.restrictProtocolForwardingCreationForTypes | List | Giới hạn forwarding types |
constraints/compute.skipDefaultNetworkCreation | Boolean | Không tạo default network trong project mới |
constraints/compute.restrictVpcPeering | List | Kiểm soát VPC peering partners |
Google Kubernetes Engine
| Constraint | Type | Mục đích |
|---|---|---|
constraints/container.restrictNfsFileVolumes | Boolean | Block NFS volumes trong GKE |
constraints/container.requireBinaryAuthorization | Boolean | Bắt buộc Binary Authorization |
constraints/container.restrictClusterAdminBinding | Boolean | Ngăn cluster-admin binding |
constraints/container.restrictPrivateClusters | Boolean | Bắt buộc private clusters |
constraints/container.allowedContainerImages | List | Chỉ cho phép images từ trusted registries |
Cloud Storage
| Constraint | Type | Mục đích |
|---|---|---|
constraints/storage.publicAccessPrevention | Boolean | Block public access toàn tổ chức |
constraints/storage.uniformBucketLevelAccess | Boolean | Bắt buộc uniform IAM (không ACL) |
constraints/storage.retentionPolicySeconds | List | Enforce minimum retention period |
IAM và Identity
| Constraint | Type | Mục đích |
|---|---|---|
constraints/iam.disableServiceAccountCreation | Boolean | Block SA creation trong project |
constraints/iam.disableServiceAccountKeyCreation | Boolean | Block SA key creation (enforce Workload Identity) |
constraints/iam.allowedPolicyMemberDomains | List | Chỉ cho phép identities từ domain cụ thể |
constraints/iam.restrictCrossProjectServiceAccountLiveness | Boolean | Block service accounts cross-project |
Exceptions với Resource Tags
Org Policy enforcement là uniform — áp dụng đều cho tất cả resources trong scope. Nhưng đôi khi cần exceptions: ví dụ, một project sandbox được phép dùng external IPs trong khi tổ chức cấm.
Resource Tags là cơ chế tạo conditional enforcement — policy chỉ áp dụng khi resource có (hoặc không có) một tag cụ thể:
# Org Policy với conditional enforcement based on tag
name: organizations/123456789/policies/compute.vmExternalIpAccess
spec:
rules:
# Rule 1: Nếu có tag "env=prod" → deny all external IPs
- condition:
expression: "resource.matchTag('env', 'prod')"
denyAll: "TRUE"
# Rule 2: Nếu có tag "env=dev" → allow external IPs
- condition:
expression: "resource.matchTag('env', 'dev')"
allowAll: "TRUE"
# Rule 3: Default cho mọi resource khác
- denyAll: "TRUE"Quan trọng về tag-based exceptions:
- Tags phải được defined ở level phù hợp trong hierarchy (tag keys tại organization level)
- Tags gắn vào resource phải được set trước khi policy được evaluate
- Tag conditions dùng CEL expression với các functions được support bởi Org Policy (
resource.matchTag(),resource.matchTagId())
Khi nào dùng tag-based exceptions vs. folder-based isolation
Cách 1: Folder-based isolation (đơn giản hơn)
Organization
├── Folder: Production ← Apply strict policies
│ └── Projects: prod-*
└── Folder: Development ← Relaxed policies
└── Projects: dev-*
Cách 2: Tag-based exceptions (flexible hơn nhưng phức tạp hơn)
Organization ← Apply strict policies cho tất cả
├── Projects: prod-* (tag: env=prod) → inherit strict policy
└── Projects: dev-* (tag: env=dev) → tag exception cho phép external IPsFolder-based isolation đơn giản hơn để quản lý. Tag-based phù hợp khi project không thể được tổ chức vào folders rõ ràng, hoặc khi cần granular exceptions ở resource level (một VM cụ thể, không phải cả project).
Không-retroactive Enforcement
Một đặc điểm quan trọng của Org Policies: chúng không áp dụng retroactively cho resources đã tồn tại.
Ví dụ: Nếu bạn apply constraint compute.vmExternalIpAccess: denyAll, các VMs đang chạy với external IPs sẽ không bị tự động remove external IPs. Policy chỉ có effect với:
- Resources mới được tạo (CREATE operations)
- Thay đổi cấu hình (UPDATE operations) trên resources hiện tại, nhưng chỉ cho trường bị constraint
Điều này có thể tạo ra một trạng thái drift: policy nói "không được có external IPs" nhưng 50 VMs vẫn có external IPs từ trước khi policy được apply.
Để phát hiện drift, dùng Cloud Asset Inventory để query resources đang vi phạm policy:
# Tìm tất cả VMs có external IPs trong organization
gcloud asset search-all-resources \
--scope=organizations/123456789 \
--asset-types=compute.googleapis.com/Instance \
--query="accessConfigs.natIP:*" \
--format=json