Gatekeeper / Policy Controller (OPA) — ConstraintTemplate & Constraint
Khi PSA và CEL không đủ: vì sao cần một policy engine
PSA (file 5) mạnh nhưng cứng nhắc — ba profile cố định, chỉ kiểm Pod. ValidatingAdmissionPolicy (file 8) linh hoạt hơn nhưng CEL có giới hạn về biểu cảm và xử lý logic phức tạp/tham chiếu chéo. Khi cần những policy như:
- "Mọi image phải đến từ
asia-docker.pkg.dev/my-project/*" (kiểm tài nguyên ngoài Pod, regex registry) - "Mọi Ingress phải có host duy nhất trong cluster" (cross-object — phải nhìn các Ingress khác)
- "Mọi namespace phải có label
cost-centervàteam" - "Áp một bộ policy chuẩn PCI-DSS / CIS Kubernetes Benchmark"
...thì bạn cần một policy engine khả lập trình. Trên hệ sinh thái Kubernetes, lựa chọn trưởng thành nhất là Open Policy Agent (OPA) Gatekeeper, và trên GKE là Policy Controller — bản đóng gói của Gatekeeper do Google quản lý (Policy Controller overview).
Gatekeeper khác webhook tự viết (file 2) ở chỗ: bạn không viết code server — bạn khai báo policy bằng dữ liệu (CRD). Gatekeeper là một validating webhook tổng quát: nó nhận mọi request khớp, đánh giá qua các policy bạn đã nạp, và trả về cho/chặn. Khác PSA ở chỗ: policy là tùy ý (Rego), không giới hạn ở ba profile.
Kiến trúc Gatekeeper
Gatekeeper gồm hai thành phần chạy trong cluster, trên nền OPA (engine đánh giá policy bằng ngôn ngữ Rego):
- Admission webhook controller — là một ValidatingWebhookConfiguration (và mutating, với tính năng mutation). Mỗi request CREATE/UPDATE khớp đi qua đây; Gatekeeper đánh giá object với mọi Constraint áp dụng và trả allowed/denied. Đây là phần enforce tại thời điểm admission.
- Audit controller — quét định kỳ mọi object đã tồn tại trong cluster so với các Constraint, ghi vi phạm vào
statuscủa Constraint. Đây là phần phát hiện cấu hình sai có sẵn (vi phạm tồn tại trước khi policy được áp). Chu kỳ điều khiển bởi--audit-interval, mặc định 60 giây (Gatekeeper audit).
Vì Gatekeeper webhook nằm trên đường ghi nóng, mọi cảnh báo ở file 3 áp dụng cho nó: nó cần HA, loại trừ namespace hệ thống, và cấu hình failurePolicy cẩn thận. Policy Controller trên GKE được cấu hình sẵn với các thực hành này, nhưng vẫn nên hiểu để debug.
ConstraintTemplate và Constraint — hai lớp tách biệt
Đây là mô hình cốt lõi và là điểm khiến Gatekeeper mạnh: tách logic policy khỏi việc áp dụng cụ thể thành hai CRD (Gatekeeper how-to).
ConstraintTemplate — định nghĩa logic + schema tham số
ConstraintTemplate định nghĩa một loại policy bằng Rego, đồng thời khai báo schema các tham số mà policy nhận. Tạo một ConstraintTemplate sẽ tự sinh ra một CRD mới (một loại Constraint) mà bạn dùng để áp dụng. Ví dụ — template bắt buộc namespace có một số label:
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels # tên loại Constraint sẽ sinh ra
validation:
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg}] {
required := input.parameters.labels[_]
not input.review.object.metadata.labels[required]
msg := sprintf("thiếu label bắt buộc: %v", [required])
}Rego ở đây định nghĩa quy tắc violation: với mỗi label bắt buộc không có trong object, sinh một vi phạm với message. input.review.object là object đang được kiểm (tương tự object trong CEL), input.parameters là tham số từ Constraint.
Constraint — instance áp dụng giá trị cụ thể
Constraint là một instance của loại CRD vừa sinh ra, cung cấp tham số cụ thể và phạm vi áp dụng (match):
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels # loại do ConstraintTemplate sinh ra
metadata:
name: ns-must-have-team
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
labels: ["team", "cost-center"]Lợi ích của việc tách hai lớp: một template, nhiều constraint. Bạn viết logic k8srequiredlabels một lần, rồi tạo nhiều Constraint với tham số khác nhau (namespace phải có team, Deployment phải có app, v.v.). Đây là điểm Gatekeeper vượt webhook tự viết — không phải viết lại code cho mỗi biến thể.
enforcementAction: deny / dryrun / warn
Trường spec.enforcementAction của Constraint quyết định Gatekeeper làm gì khi phát hiện vi phạm tại admission (Gatekeeper violations):
deny(mặc định) — từ chối request vi phạm.dryrun— không chặn; vi phạm chỉ được ghi vàostatuscủa Constraint (qua audit). Dùng để thử policy trên production mà không gây gián đoạn.warn— không chặn, nhưng trả cảnh báo cho người dùng tại thời điểm apply (giốngwarncủa PSA).
Ba mức này lặp lại đúng mô hình rollout có kỷ luật của cả chương (observe → warn → enforce, xem file 2, 5): triển khai constraint mới ở dryrun, quan sát status.violations, sửa các object vi phạm, rồi mới chuyển deny. Gatekeeper còn hỗ trợ scopedEnforcementActions để áp action khác nhau ở các enforcement point khác nhau (admission vs audit).
Audit loop và status violations
Khác biệt lớn so với webhook tự viết và PSA: Gatekeeper audit định kỳ object đã tồn tại. Webhook chỉ chặn object mới; nó không biết gì về object đã nằm trong cluster trước khi policy được áp. Audit controller của Gatekeeper lấp khoảng này — mỗi 60s (mặc định) nó đánh giá lại mọi object so với mọi Constraint và ghi vi phạm vào status:
kubectl get k8srequiredlabels ns-must-have-team -o yaml
# status:
# totalViolations: 3
# violations:
# - kind: Namespace
# name: legacy-app
# message: "thiếu label bắt buộc: cost-center"Đây là công cụ vận hành cực mạnh: trước khi chuyển một constraint sang deny, đọc status.violations cho biết chính xác object nào sẽ bị chặn — không phải đoán. Đây là dạng "audit có cấu trúc" tốt hơn việc lọc audit log.
Referential constraints — nhìn nhiều object cùng lúc
Phần lớn policy chỉ cần nhìn một object (Pod này có privileged không). Nhưng một số policy là tham chiếu chéo (referential) — quyết định phụ thuộc các object khác: "Ingress host này có trùng host của Ingress khác không?", "Service này có selector trùng Service khác không?". Webhook và CEL policy thuần không làm được điều này dễ dàng vì chúng chỉ thấy object trong request.
Gatekeeper hỗ trợ referential constraint bằng cách đồng bộ (sync) các loại object vào cache của OPA qua một object Config (hoặc SyncSet). Khi đã sync, Rego truy cập được data.inventory — toàn bộ object loại đó trong cluster — để so sánh (Gatekeeper sync). Đây là một trong những lý do mạnh nhất để chọn Gatekeeper thay vì CEL policy: cross-object logic.
Lưu ý đánh đổi: referential constraint đánh giá dựa trên cache có thể trễ (eventually consistent) — nếu hai Ingress trùng host được tạo gần như đồng thời, cache có thể chưa thấy cái kia, và cả hai lọt qua. Referential constraint là "best effort", không phải đảm bảo tuyệt đối về uniqueness.
Policy Controller trên GKE — Gatekeeper được Google đóng gói
Policy Controller là Gatekeeper được tích hợp vào GKE Enterprise, với các giá trị tăng thêm (Policy Controller overview):
- Constraint template library — thư viện constraint dựng sẵn (yêu cầu label, hạn chế registry image, chặn privileged...) — không phải tự viết Rego.
- Policy bundles — nhóm constraint theo chuẩn: CIS Kubernetes Benchmark, PCI-DSS, NIST, Pod Security. Áp cả bundle bằng vài lệnh thay vì cấu hình từng cái (CIS bundle).
- Tích hợp Config Sync / fleet — phân phối constraint qua GitOps tới nhiều cluster trong fleet từ một repo Git/OCI trung tâm. Đây là cách quản lý policy ở quy mô tổ chức (hàng chục–trăm cluster) thay vì cấu hình từng cluster.
- Dashboard trong Cloud Console — xem trạng thái tuân thủ, vi phạm, qua giao diện thay vì
kubectl get ... -o yaml. - Shift-left — phân tích thay đổi không tuân thủ trước khi apply trong CI/CD pipeline.
Trên GKE, Policy Controller là cách "đúng" để chạy Gatekeeper: bạn bật nó như một feature của fleet, không tự cài và vận hành Gatekeeper raw. Điều này giảm gánh nặng vận hành (HA, upgrade, cert của webhook Gatekeeper) — Google lo phần đó.
Gatekeeper vs PSA — ma trận quyết định
Câu hỏi thực chiến: khi nào dùng PSA, khi nào Gatekeeper, dùng cả hai thế nào?
| Tiêu chí | PSA | Gatekeeper / Policy Controller |
|---|---|---|
| Phạm vi | Chỉ Pod, 3 profile cố định | Bất kỳ tài nguyên, logic tùy ý |
| Tùy biến | Không (profile cứng) | Toàn phần (Rego) |
| Cross-object | Không | Có (referential) |
| Chi phí vận hành | Rất thấp (label namespace) | Cao hơn (CRD, audit, webhook) |
| Thư viện chuẩn | 3 profile | CIS/PCI/NIST bundle |
| Failure mode | Built-in, gần như không | Là webhook — có failure mode (file 3) |
Khuyến nghị: dùng cả hai theo lớp, không thay thế nhau.
- PSA làm lớp nền cho hardening Pod (privileged, runAsNonRoot, capabilities) — chi phí thấp, built-in, không thêm failure mode. Mọi cluster nên có.
- Gatekeeper làm lớp tùy biến cho mọi thứ PSA không biểu đạt: image registry, label bắt buộc, cross-object, compliance bundle.
Một anti-pattern là dùng Gatekeeper để tái tạo Pod Security Standards (có template library cho việc này) trong khi PSA đã làm sẵn, miễn phí, không failure mode. Chỉ dùng Gatekeeper cho Pod Security khi cần biến thể mà PSA không cho (ví dụ "restricted nhưng cho phép một capability cụ thể cho một số workload") — khi đó PSA không đủ và Gatekeeper là lựa chọn đúng.
Production architecture patterns
GitOps policy qua Config Sync cho fleet
Mô hình tổ chức trưởng thành: toàn bộ ConstraintTemplate và Constraint nằm trong một repo Git, Config Sync đồng bộ chúng tới mọi cluster trong fleet. Policy trở thành code, review qua PR, áp đồng nhất. Đây là cách duy nhất quản lý policy nhất quán khi có nhiều cluster — cấu hình thủ công từng cluster không scale và dễ lệch.
Rollout policy mới: dryrun → đọc status → deny
Luôn triển khai constraint mới ở enforcementAction: dryrun, để audit chạy vài chu kỳ, đọc status.violations để biết tác động thật, sửa object vi phạm, rồi chuyển deny. Quy trình này biến việc bật policy từ "apply rồi cầu nguyện" thành quyết định dựa trên dữ liệu.
Constraint exemption qua match selector
Để loại trừ namespace hệ thống và các ngoại lệ hợp lệ, dùng match.excludedNamespaces hoặc match.namespaceSelector trong Constraint — tương tự namespaceSelector của webhook (file 3). Policy Controller cũng có cơ chế exempt namespace ở cấp cấu hình.
Common mistakes / anti-patterns
Bật constraint deny thẳng không qua dryrun
Giống PSA: object cũ vi phạm bị chặn ngay, gây gián đoạn. Luôn dryrun + đọc status trước.
Dùng Gatekeeper tái tạo PSA
Thêm chi phí vận hành và failure mode để làm việc PSA đã làm miễn phí. Dùng PSA cho Pod Security trừ khi cần biến thể PSA không cho.
Referential constraint coi như đảm bảo tuyệt đối
Cache đồng bộ có thể trễ; hai object trùng tạo đồng thời có thể lọt. Hiểu rõ đây là best-effort.
Viết Rego phức tạp khi CEL đủ
Nếu policy chỉ là "field này phải thỏa điều kiện kia" trên một object, ValidatingAdmissionPolicy (file 8) nhẹ hơn và không thêm failure mode webhook. Dành Gatekeeper cho cross-object, compliance bundle, và logic thực sự phức tạp.
GCP-native implementation guidance
Bật Policy Controller trên một cluster trong fleet (qua gcloud):
gcloud container fleet policycontroller enable \
--memberships=CLUSTER_NAMEÁp một policy bundle chuẩn (ví dụ CIS Kubernetes Benchmark) — thường qua Config Sync/RootSync trỏ tới repo chứa bundle, hoặc cấu hình fleet. Xem trạng thái tuân thủ:
# Liệt kê các ConstraintTemplate đã nạp
kubectl get constrainttemplates
# Xem vi phạm của một constraint
kubectl get <constraint-kind> <name> -o jsonpath='{.status.totalViolations}'Kiểm thử constraint trước khi áp (shift-left) bằng gator CLI của Gatekeeper trong CI:
gator test --filename=policy/ --filename=manifests/