Skip to content

Security Policy Internal Model & Evaluation Pipeline

Why this matters in production

Phần lớn lỗi cấu hình Cloud Armor không nằm ở nội dung rule mà ở mô hình tư duy sai về nơi và cách rule được thực thi. Kỹ sư quen với mental model "firewall appliance": một hộp đứng giữa client và server, nhìn thấy toàn bộ packet, xử lý tuần tự. Cloud Armor không phải như vậy.

Cloud Armor là một lớp policy chạy trên Google Front End (GFE) — cùng lớp hạ tầng terminate TLS và thực thi global load balancing của Google, đặt tại hàng trăm Point of Presence (PoP) khắp thế giới. Hệ quả trực tiếp:

  • Policy được thực thi gần nguồn traffic nhất có thể, ở edge, trước khi request đi vào backend service của bạn. Một request bị deny ở Cloud Armor không bao giờ chạm tới instance/pod — nó bị chặn ở PoP, thường cách attacker vài mili-giây.
  • Vì GFE đã terminate TLS, Cloud Armor thấy được HTTP đã giải mã (path, header, query, một phần body). Đây là lý do WAF L7 khả thi. Nhưng cũng vì là edge distributed system, một số thứ không khả thi hoặc chỉ xấp xỉ (bộ đếm rate limit, xem phần rate limiting).
  • Enforcement là distributed: không có một "cái hộp" trung tâm. Có hàng nghìn GFE. Điều này định hình mọi giới hạn về tính nhất quán mà ta sẽ gặp lại xuyên suốt chapter.

Hiểu đúng enforcement point này là điều kiện tiên quyết để reason về mọi hành vi khác của Cloud Armor.

Internal model — cơ chế vận hành

Cloud Armor nằm ở đâu trong data path

Với một External Application Load Balancer, đường đi của một request là:

Client → (TLS) → GFE tại PoP gần nhất
                    ├── Cloud Armor security policy evaluation  ← đây
                    ├── (nếu allow) URL map / routing
                    └── → Backend service → NEG/MIG → Pod/VM

Điểm mấu chốt: Cloud Armor evaluation xảy ra sau khi GFE nhận và giải mã request, nhưng trước khi request được route tới backend. Nghĩa là:

  1. Cloud Armor thấy được các thuộc tính L3–L7: IP nguồn, region, ASN, method, path, header, query, và (giới hạn) body.
  2. Cloud Armor không thấy được response từ backend — nó là một inbound request filter, không phải một reverse proxy hai chiều xử lý cả response. (Việc chèn header, redirect là do action tại chỗ, không phải xử lý response backend.)
  3. Vì evaluation ở edge, action như redirect tới trang reCAPTCHA có thể trả về ngay từ GFE mà không tốn tài nguyên backend.

Các loại security policy

Cloud Armor có nhiều loại policy, mỗi loại gắn vào một điểm khác nhau trong hạ tầng và có tập filter/action khác nhau. Nhầm lẫn giữa chúng là nguồn lỗi phổ biến.

Backend security policy — loại phổ biến nhất. Gắn vào backend service phía sau Application Load Balancer, proxy Network Load Balancer, và regional external load balancer. Đây là loại hỗ trợ đầy đủ nhất: allow, deny, throttle, rate-based ban, redirect, cùng toàn bộ WAF preconfigured rules và custom rules. Khi nói "Cloud Armor WAF", 90% trường hợp là loại này.

Edge security policy — áp dụng cho các backend service đã bật Cloud CDN và cho Cloud Storage bucket. Nó được thực thi upstream của cache layer, tức là trước cả khi cache được tra cứu. Vì đặt trước cache, nó có tập tham số filter hạn chế hơn backend policy. Điểm quan trọng: một backend service có thể có đồng thời một edge policy và một backend policy — tạo thành hai lớp phòng thủ (edge filter thô trước cache, backend filter tinh sau đó). Theo tài liệu Google, edge policy "can coexist with backend security policies to provide two layers of protection".

Network edge security policy — bảo vệ regional external passthrough Network Load Balancer, protocol forwarding, và VM có public IP. Loại này hoạt động ở lớp thấp hơn (packet-level, hỗ trợ byte-offset filtering) và yêu cầu Advanced Network DDoS Protection (thuộc Cloud Armor Enterprise). Nó không phải WAF L7 đầy đủ; mục đích chính là chặn L3/L4 tại edge cho passthrough LB — nơi mà GFE không terminate connection.

Internal service security policy — gắn vào Cloud Service Mesh endpoint policy, hỗ trợ fairshare rate limiting cho traffic nội bộ trong mesh.

Mental model: loại policy được quyết định bởi loại load balancer/tài nguyên bạn bảo vệ, không phải bởi "mức bảo mật bạn muốn". Passthrough NLB không thể dùng backend WAF policy vì GFE không terminate TLS cho nó — không có HTTP đã giải mã để inspect.

Cấu trúc một rule

Một security policy là một danh sách rule có thứ tự. Mỗi rule gồm:

  • priority — số nguyên trong khoảng 0 đến 2147483646. Số nhỏ hơn = ưu tiên cao hơn (được duyệt trước). Đây là điểm gây nhầm lẫn kinh điển: nhiều người đọc "priority 1000" và nghĩ nó "quan trọng hơn priority 10", trong khi thực tế ngược lại.
  • match — điều kiện khớp. Có hai dạng: match cơ bản (danh sách IP/CIDR range) hoặc advanced match expression viết bằng CEL (Common Expression Language). Một advanced match cho phép tối đa 5 subexpression.
  • action — hành động khi rule khớp (chi tiết bên dưới).
  • preview (tùy chọn) — nếu bật, rule được đánh giá và ghi log nhưng không thực thi action. Công cụ tối quan trọng để test rule an toàn.
  • description — mô tả (nên đặt cẩn thận, vì nó xuất hiện trong log và giúp debug).

Default rule luôn tồn tại ở priority = 2147483647 (INT-MAX, ngoài khoảng user gán được). Nó khớp mọi request không khớp rule nào phía trên. Action mặc định của default rule quyết định policy của bạn là allowlist (default deny, chỉ cho qua cái được phép) hay denylist (default allow, chỉ chặn cái xấu). Đây là quyết định kiến trúc quan trọng nhất của cả policy.

Evaluation pipeline: first-match-wins theo priority

Đây là trái tim của Cloud Armor. Với mỗi request đến:

  1. GFE lấy danh sách rule của policy, sắp xếp theo priority tăng dần (số nhỏ trước).
  2. Duyệt từng rule theo thứ tự đó. Với mỗi rule, đánh giá match condition.
  3. Rule đầu tiên khớp sẽ thắng — action của nó được áp dụng và quá trình duyệt dừng lại. Các rule có priority lớn hơn (kể cả default rule) không được đánh giá nữa.
  4. Nếu không rule nào khớp, default rule áp dụng.

Đây là mô hình first-match-wins, không phải "most-specific-wins" và không phải "deny-overrides". Hệ quả cực kỳ quan trọng cho thiết kế:

  • Thứ tự quyết định tất cả. Nếu bạn đặt một rule allow cho một IP range ở priority 100, rồi một rule deny cho một sub-range của nó ở priority 200, thì deny không bao giờ chạy với các IP thuộc allow range — vì allow khớp trước ở priority thấp hơn.
  • Quy tắc thiết kế: đặt các rule cụ thể/ngoại lệ ở priority thấp (số nhỏ) và rule chung/bao quát ở priority cao (số lớn). Ví dụ: allow một vài IP admin cụ thể (priority 100) → deny cả một country (priority 1000) → WAF rules (priority 2000+) → default allow.
  • Để chừa khoảng trống priority (100, 200, 300…) để về sau chèn rule vào giữa mà không phải đánh số lại toàn bộ.

Anti-pattern kinh điển: đặt WAF deny ở priority thấp hơn rule allow cho traffic hợp lệ đã biết. Kết quả: WAF chặn cả traffic hợp lệ trước khi allow kịp chạy. Gốc rễ là hiểu sai first-match-wins — nghĩ rằng "allow sẽ ghi đè deny". Không. Rule nào priority nhỏ hơn chạy trước, và nó dừng luôn.

Các action

Khi một rule khớp, một trong các action sau được áp dụng:

  • allow — cho request đi tiếp tới routing/backend. Dừng evaluation.
  • deny(STATUS) — chặn request, trả về mã lỗi. Mã hợp lệ: 403 (Forbidden), 404 (Not Found), 502 (Bad Gateway). Chọn mã có ý nghĩa vận hành: 404 giấu sự tồn tại của resource, 403 nói rõ bị từ chối.
  • throttle — rate limiting dạng giới hạn tốc độ (xem file 04).
  • rate_based_ban — chặn nguồn vượt ngưỡng trong một khoảng thời gian.
  • redirect — chuyển hướng request. Hai kiểu: EXTERNAL_302 (redirect HTTP 302 tới URL bạn chỉ định) hoặc GOOGLE_RECAPTCHA (chuyển tới trang thử thách reCAPTCHA Enterprise để phân biệt người/bot).

Điểm cần nhớ: denyredirect đều được trả về ngay từ GFE, không tốn tài nguyên backend. Đây chính là giá trị của việc enforcement ở edge — attack traffic bị hấp thụ ở lớp gần attacker nhất.

Preview mode — cơ chế test an toàn

preview là tính năng mà mọi thay đổi Cloud Armor nghiêm túc nên đi qua. Khi một rule ở preview mode:

  • Cloud Armor đánh giá rule đúng như bình thường.
  • Nếu khớp, nó ghi log vào field previewSecurityPolicy của request log (thay vì enforcedSecurityPolicy).
  • Nhưng action không được thực thi — request vẫn đi theo quyết định của policy thực (rule enforced tiếp theo hoặc default).

Điều này cho phép bạn triển khai một WAF rule mới, quan sát nó sẽ chặn cái gì trong vài ngày qua log thật, đo lường false positive, rồi mới bỏ preview để enforce. Bỏ qua preview khi rollout WAF ở production là cách nhanh nhất để tự gây outage (WAF chặn nhầm traffic hợp lệ).

Giới hạn body inspection

Cloud Armor không inspect toàn bộ body của request. Theo tài liệu, nó inspect tối đa 8 KB đầu tiên của body cho các trường tiêu chuẩn (và cấu hình mở rộng tới 64 KB với một số điều kiện). Chỉ evaluatePreconfiguredWaf() mới đánh giá được request body, và nó parse được body dạng application/x-www-form-urlencoded và JSON (Content-Type: application/json).

Hệ quả kiến trúc:

  • Một payload tấn công đặt sau byte thứ 8 KB có thể lọt qua WAF. Attacker biết điều này và có thể padding.
  • WebSocket và gRPC chỉ được đánh giá ở HTTP(S) handshake ban đầu, không phải từng frame sau đó.
  • Đừng dựa vào Cloud Armor như biện pháp duy nhất để validate body — nó là lớp phòng thủ edge, không thay thế validation ở application.

Constraints, trade-offs & failure modes

Giới hạn cấu trúc:

  • Tối đa 5 subexpression cho một advanced match condition. Rule logic phức tạp phải tách thành nhiều rule (và lúc đó thứ tự priority lại quan trọng).
  • priority phải duy nhất trong một policy; không được trùng.
  • Một security policy đã gắn vào backend service thì không xóa được cho tới khi gỡ association. Đây là bảo vệ chống xóa nhầm.
  • Nhiều forwarding rule trỏ về cùng một backend service sẽ chia sẻ cùng policy enforcement — bạn không thể có policy khác nhau cho cùng backend service qua các forwarding rule khác nhau.

Region-code geolocation: mapping từ IP sang region code cập nhật khoảng hàng tuần với rollout toàn cầu kéo dài vài ngày. Nghĩa là rule dựa trên origin.region_code có một độ trễ và sai số nhất định — không nên dùng cho quyết định compliance cứng.

Failure mode — thứ tự sai làm rule "vô hình": như đã nói, một rule đúng nội dung nhưng đặt sai priority (sau một rule khớp rộng hơn) sẽ không bao giờ chạy. Triệu chứng: "tôi tạo rule deny SQLi rồi mà attack vẫn qua" — thường vì có một rule allow ở priority thấp hơn khớp trước. Cách debug: xem enforcedSecurityPolicy.priority trong log để biết rule nào thực sự khớp.

Failure mode — default rule sai: nếu default rule là allow (denylist model) và bạn quên rằng WAF không bắt được mọi thứ, bạn đang phơi backend ra với mọi request không khớp rule deny nào. Ngược lại, default deny (allowlist) an toàn hơn nhưng đòi hỏi bạn liệt kê tường minh mọi traffic hợp lệ — dễ gây outage nếu thiếu sót.

Trade-off allowlist vs denylist:

  • Denylist (default allow): dễ vận hành, phù hợp API/web công khai; rủi ro là bỏ sót vector tấn công.
  • Allowlist (default deny): an toàn tối đa, phù hợp internal API/admin panel có tập client biết trước; rủi ro là chặn nhầm traffic hợp lệ khi có client/IP mới.

Production architecture pattern: layered policy ordering

Một layout priority điển hình cho backend policy của một web app công khai, minh họa trực tiếp cho first-match-wins:

yaml
# priority thấp = duyệt trước = ưu tiên cao
- priority: 100   # allow health check / monitoring IP đã biết
  match: inIpRange(origin.ip, '35.191.0.0/16')
  action: allow
- priority: 500   # deny các country bị chặn (compliance/abuse)
  match: origin.region_code == 'XX'
  action: deny(403)
- priority: 1000  # rate limit theo IP
  action: throttle  # (rate_limit_options...)
- priority: 2000  # WAF: SQLi
  match: evaluatePreconfiguredWaf('sqli-v422-stable', {'sensitivity': 1})
  action: deny(403)
- priority: 2100  # WAF: XSS
  match: evaluatePreconfiguredWaf('xss-v422-stable', {'sensitivity': 1})
  action: deny(403)
- priority: 2147483647  # default
  action: allow

Bài học cơ chế từ layout này: allow-list các IP hạ tầng (health check) phải ở priority thấp nhất, nếu không một rule WAF hoặc geo-deny phía trên có thể chặn nhầm health check probe → LB đánh dấu backend unhealthy → outage. Đây là ví dụ điển hình về việc thứ tự quan trọng hơn nội dung.

Official references