Skip to content

Firewall Policy Evaluation Engine — Khi nhiều tầng cùng khớp, ai quyết định?

Why this matters in production

Chương 3 dạy firewall fundamentals (stateful, priority, ingress/egress) và hierarchical policies. Nhưng ở một tổ chức thật, một packet đơn lẻ có thể bị soi bởi năm tầng firewall khác nhau cùng lúc: policy ở org, policy do Google quản lý, policy global của VPC, policy regional, và rule VPC legacy. Câu hỏi sống còn không còn là "rule này allow hay deny" mà là "khi năm tầng cùng có ý kiến, kết quả cuối là gì, và theo logic nào".

Hiểu sai engine này dẫn tới hai loại sự cố đối xứng và đều nghiêm trọng:

  • Tưởng đã chặn nhưng thực ra mở: bạn viết một deny rule ở VPC legacy, nhưng một allow ở tầng cao hơn (hierarchical) đã quyết định trước và packet được cho qua mà rule của bạn không bao giờ được chạm tới.
  • Tưởng đã mở nhưng thực ra chặn: bạn allow ở VPC legacy, nhưng một deny ở hierarchical policy chặn từ trên, rule allow của bạn vô nghĩa.

Cả hai đều xuất phát từ việc không nắm thứ tự đánh giá xuyên tầngngữ nghĩa của goto_next. File này mổ xẻ engine đó như một máy đánh giá xác định, không phải một danh sách rule phẳng.

Internal model — engine đánh giá là một pipeline nhiều tầng có thứ tự

Bức tranh lớn: firewall không phải một bảng, mà là một pipeline

Sai lầm mental model phổ biến: hình dung "firewall của tôi" là một danh sách rule. Thực tế, firewall hiệu lực cho một VM là kết quả của một pipeline các tầng policy, mỗi tầng là một danh sách rule riêng, đánh giá theo thứ tự cố định:

   Packet (ingress hoặc egress đối với một VM)


┌─────────────────────────────────────────────────┐
│ 1. HIERARCHICAL FIREWALL POLICIES               │  org → folders
│    (theo thứ tự resource hierarchy, trên xuống) │
└─────────────────────────────────────────────────┘
        │ (nếu rule khớp = goto_next, đi tiếp; nếu allow/deny = DỪNG)

┌─────────────────────────────────────────────────┐
│ 2. REGIONAL SYSTEM FIREWALL POLICIES            │  do Google quản lý
│    (đánh giá ngay sau hierarchical)             │
└─────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────┐
│ 3. GLOBAL NETWORK FIREWALL POLICY (của VPC)     │
└─────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────┐
│ 4. REGIONAL NETWORK FIREWALL POLICY (của VPC)   │
└─────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────┐
│ 5. VPC FIREWALL RULES (legacy)                  │
└─────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────┐
│ 6. IMPLIED RULES: deny mọi ingress,             │
│    allow mọi egress (lưới cuối cùng)            │
└─────────────────────────────────────────────────┘

Theo tài liệu firewall policies, Google "đánh giá rule trong regional system firewall policies ngay sau khi đánh giá rule trong hierarchical firewall policies" — xác nhận tính tuần tự của pipeline. Mỗi tầng có thể đưa ra một trong ba kết cục: allow (terminal), deny (terminal), hoặc goto_next (chuyển quyết định xuống tầng sau).

Bên trong một tầng: priority quyết định rule nào nói

Trong mỗi tầng (mỗi policy), rule được sắp theo priority — số nguyên từ 0 đến 2.147.483.547, số nhỏ = ưu tiên cao (theo tài liệu rule details). Engine quét rule theo priority tăng dần; rule priority cao nhất khớp packet sẽ quyết định kết cục của tầng đó. Nếu kết cục là allow/deny → toàn bộ pipeline dừng. Nếu là goto_next → bỏ qua phần còn lại của tầng này và sang tầng kế tiếp.

Đây là điểm hợp nhất hai cấp độ:

  • Xuyên tầng: thứ tự cố định (hierarchical → ... → implied).
  • Trong tầng: priority quyết định rule nào "phát biểu" thay mặt tầng đó.

goto_next — cơ chế ủy quyền quyết định, và cái bẫy của nó

goto_next là action đặc biệt và là nguồn gốc của nhiều hiểu lầm. Theo tài liệu, goto_next "dừng đánh giá các rule khác trong policy hiện tại, và đánh giá rule ở bước kế tiếp trong thứ tự". Nói cách khác, một rule goto_next khớp packet nghĩa là: "tầng này từ chối ra quyết định cuối, hãy để tầng sau quyết".

Hệ quả tinh tế: goto_next không phải "allow". Nó là "deferral". Nhưng nhiều người đọc một hierarchical rule goto_next rồi tưởng traffic đã được cho qua — sai. Nó chỉ được chuyển xuống các tầng dưới; nếu tầng dưới deny (hoặc rơi vào implied deny ingress), packet vẫn bị chặn. goto_next là cách tầng trên nói "không phải việc của tôi, hỏi tầng dưới".

Mental model goto_next: hình dung mỗi tầng là một thẩm phán có quyền (a) tha (allow, kết thúc), (b) kết tội (deny, kết thúc), hoặc (c) chuyển hồ sơ lên tòa kế (goto_next). Một hồ sơ chỉ "trắng án" khi một tầng nào đó allow trước khi chạm implied deny. "Được chuyển hồ sơ" không bằng "trắng án".

Allow ở một tầng = bỏ qua MỌI tầng dưới

Đây là hệ quả quan trọng nhất của pipeline và là nguồn của sự cố "tưởng chặn nhưng mở": vì allow là terminal, nếu một hierarchical policy ở org allow một packet, thì mọi deny rule ở VPC legacy bên dưới không bao giờ được đánh giá. Người viết deny ở tầng thấp tưởng mình đã chặn, nhưng tầng cao đã "tha" trước.

Đây cũng là lý do hierarchical policies là công cụ quản trị mạnh: chúng đánh giá trước mọi thứ, nên một guardrail org-level (ví dụ deny SSH từ internet trên toàn org) không thể bị một project tự ý "mở lại" bằng VPC rule — trừ khi hierarchical rule dùng goto_next để cố tình ủy quyền xuống. Sự bất đối xứng quyền lực giữa các tầng chính là tính năng quản trị.

Implied rules: lưới cuối cùng

Nếu packet đi hết pipeline mà không tầng nào ra quyết định allow/deny (mọi tầng khớp đều goto_next hoặc không khớp rule nào), nó rơi vào implied rules của network firewall: implied deny cho mọi ingress, implied allow cho mọi egress. Đây là default an toàn: traffic vào bị chặn trừ khi được allow tường minh; traffic ra được phép trừ khi bị chặn tường minh. Hiểu điều này giải thích vì sao một VM "không có rule nào khớp" thì vẫn không nhận được kết nối vào (implied deny ingress) nhưng vẫn ra được internet (implied allow egress) — cho tới khi bạn thêm egress deny.

Mục tiêu rule: secure tags vs service accounts vs resources

Engine cần biết rule áp dụng cho VM nào. Ở các network firewall policy hiện đại, theo tài liệu rule details, có ba cơ chế target:

  • Service accounts (target-service-accounts): rule áp cho VM chạy bằng SA đó.
  • Secure tag values (target-secure-tags): các tag được quản trị bằng IAM (khác network tag legacy). Đây là điểm tiến hóa quan trọng — tài liệu policy hiện đại không dùng "network tags" làm target nữa, mà dùng secure tags gắn với resource manager và có kiểm soát IAM.
  • VPC network resources (target-resources): áp theo network.

Khác biệt cơ chế cốt lõi giữa secure tagsnetwork tags legacy (file Chương 3 bàn network tags): network tag là metadata tự do trên VM — bất kỳ ai có quyền sửa instance đều có thể đổi tag, nên dùng tag để phân quyền firewall là yếu về bảo mật. Secure tags được quản trị bởi IAM (key/value là resource có permission riêng), nên gắn secure tag là một hành động được uỷ quyền, không thể tự ý đổi → phù hợp làm ranh giới bảo mật thật. Khi thiết kế firewall theo định danh, ưu tiên service account hoặc secure tag, không phải network tag legacy.

Stateful connection tracking: vì sao chỉ cần allow một chiều

Engine firewall của GCP là stateful. Theo tài liệu rule details, action allow (và apply_security_profile_group) tạo connection tracking entry cho phép "cả ingress và egress packet" của connection đó, bất kể chiều của rule, miễn protocol/policy hỗ trợ tracking. Cơ chế:

  • Bạn allow ingress TCP 443 tới một web server. Engine tạo entry cho connection.
  • Packet trả lời (egress từ server) không cần một egress allow rule riêng — nó khớp connection tracking entry và được cho qua tự động.

Đây là lý do bạn không phải viết rule cho cả hai chiều của một connection. Nhưng nó cũng là nguồn của một failure mode tinh tế: connection tracking có giới hạn số entry trên mỗi VM; một VM bị flood connection (hoặc nhiều short-lived connection) có thể cạn bảng tracking → connection mới bị rớt dù rule cho phép. Đây là sự cố datapath xuất hiện như "firewall chặn ngẫu nhiên" mà không rule nào sai — nó là cạn tài nguyên stateful, không phải lỗi cấu hình rule.

Constraints, trade-offs & failure modes

"Tưởng chặn nhưng mở" và "tưởng mở nhưng chặn"

Hai failure mode đối xứng, cùng một gốc (không hiểu thứ tự tầng + terminal allow):

  • Mở ngoài ý muốn: deny ở tầng thấp bị allow ở tầng cao vô hiệu hóa (allow terminal, chạy trước).
  • Chặn ngoài ý muốn: allow ở tầng thấp vô nghĩa vì deny ở tầng cao đã chặn.

Phòng tránh: luôn xác định một rule sống ở tầng nào và tầng đó đứng ở đâu trong pipeline. Dùng công cụ Firewall Insights / Connectivity Tests để xem rule nào thực sự quyết định cho một packet, thay vì đọc từng danh sách rule rời rạc.

Quá nhiều tầng ⇒ khó suy luận

Sức mạnh của nhiều tầng (quản trị phân cấp) đánh đổi bằng độ phức tạp suy luận. Một packet bị soi bởi 5 tầng khiến "vì sao nó bị chặn/cho qua" khó trả lời bằng mắt. Trade-off thiết kế: dùng hierarchical policy cho guardrail rộng, ít thay đổi (deny dứt khoát những gì không bao giờ được phép), để các tầng dưới (VPC) cho quy tắc cục bộ — đừng rải logic chồng chéo khắp mọi tầng.

Secure tag vs network tag: bẫy bảo mật

Dùng network tag legacy làm cơ chế phân quyền firewall là một điểm yếu: ai sửa được instance thì sửa được tag, tự "thoát" rule. Ở quy mô lớn với nhiều người có quyền compute, đây là lỗ hổng leo thang. Chuyển sang secure tags (IAM-governed) hoặc service account target cho các rule mang ý nghĩa bảo mật.

Anti-pattern: đọc firewall như một danh sách phẳng

  • Vì sao xảy ra: kinh nghiệm với firewall truyền thống (một bảng rule) và việc Chương 3 giới thiệu VPC rule trước; bỏ qua sự tồn tại và thứ tự ưu tiên của các tầng policy phía trên.
  • Hệ quả ở scale: rule được viết ở sai tầng; guardrail org bị tưởng nhầm là bị override được; deny "không có tác dụng" vì allow ở tầng cao; debug sai hướng vì soi nhầm danh sách.
  • Cách tư duy đúng: firewall hiệu lực là một pipeline tầng có thứ tự (hierarchical → regional system → global → regional → VPC legacy → implied), mỗi tầng allow/deny (terminal) hoặc goto_next (ủy quyền); trong tầng, priority quyết định. Luôn hỏi "rule này ở tầng nào, tầng nào quyết định trước".

GCP-native implementation guidance

bash
# Xem rule hiệu lực cho một instance (kết quả của TOÀN pipeline, không phải 1 tầng)
gcloud compute instances network-interfaces get-effective-firewalls vm-web \
  --zone=us-central1-a

# Hierarchical policy guardrail: deny dứt khoát ở org (chạy TRƯỚC mọi VPC rule)
gcloud compute firewall-policies rules create 1000 \
  --firewall-policy=org-guardrails \
  --direction=INGRESS --action=deny \
  --layer4-configs=tcp:22 --src-ip-ranges=0.0.0.0/0

# Rule dùng SECURE TAG (IAM-governed) thay vì network tag legacy
gcloud compute network-firewall-policies rules create 2000 \
  --firewall-policy=prod-policy --global-firewall-policy \
  --direction=INGRESS --action=allow --layer4-configs=tcp:443 \
  --target-secure-tags=tagValues/123456789

Để biết chính xác rule nào quyết định cho một packet 5-tuple, dùng Connectivity Tests (mô phỏng cả route resolution lẫn toàn pipeline firewall) — đây là cách duy nhất phản ánh đúng thứ tự tầng + goto_next thay vì đoán bằng tay.

Tóm tắt mental model

  • Firewall hiệu lực là một pipeline tầng có thứ tự cố định: hierarchical → regional system (Google) → global network → regional network → VPC legacy → implied rules.
  • Mỗi tầng ra một trong ba kết cục: allow/deny (terminal, dừng pipeline) hoặc goto_next (ủy quyền xuống tầng sau). goto_next ≠ allow.
  • Trong mỗi tầng, priority (0–2.147.483.547, nhỏ = cao) quyết định rule nào phát biểu.
  • Allow ở tầng cao bỏ qua mọi tầng dưới → hierarchical là guardrail mạnh không bị tầng dưới override (trừ khi cố ý goto_next).
  • Implied deny ingress / allow egress là lưới cuối cùng.
  • Target hiện đại: service accountsecure tag (IAM-governed) > network tag legacy (yếu về bảo mật).
  • Engine stateful: allow một chiều tự cho traffic trả lời qua; nhưng connection tracking có giới hạn → cạn bảng = rớt connection dù rule đúng.

References