Skip to content

Policy Controller — OPA Constraint Engine Trên Fleet

Tại Sao Quan Trọng Trong Production

Kubernetes RBAC kiểm soát ai có thể làm với Kubernetes API. Nhưng RBAC không kiểm soát content của những gì được tạo ra. Developer có quyền create Deployment vẫn có thể tạo Deployment với privileged: true containers, không có resource limits, không có security context, pull image từ untrusted registry. RBAC cho phép action, không filter content của action.

Policy Controller (và OPA Gatekeeper underlying nó) giải quyết gap này: validate và enforce constraints trên content của Kubernetes resources, không chỉ trên action. Trong fleet context, Policy Controller là cơ chế đảm bảo mọi cluster tuân thủ cùng một bộ guardrails — từ security policies đến compliance requirements đến naming conventions.

Kiến Trúc Policy Controller

Quan Hệ Với OPA Gatekeeper

Policy Controller là GKE-managed version của OPA Gatekeeper — open source project kết hợp Open Policy Agent (OPA) với Kubernetes. Policy Controller là managed service: Google handle deployment, upgrades, và monitoring của Gatekeeper components. Underlying architecture là giống nhau.

Hai components chính của Gatekeeper/Policy Controller:

Admission Webhook Controller: Kubernetes ValidatingAdmissionWebhook intercepts mọi create/update request trước khi persist vào etcd. Controller evaluate resource against active Constraints và quyết định allow hay deny.

Audit Controller: Chạy định kỳ (mặc định mỗi 60 giây), query tất cả existing resources trong cluster và check against Constraints. Report violations ngay cả đối với resources đã exist trước khi Constraint được tạo.

Hai controllers này phục vụ hai mục đích hoàn toàn khác nhau — không phải một cái là "test" và một cái là "production".

ConstraintTemplate — Định Nghĩa Policy Logic

Rego Language và OPA

Rego là declarative query language của OPA dùng để viết policy. ConstraintTemplate wrap Rego code thành reusable policy definition.

Ví dụ ConstraintTemplate enforce container phải có resource limits:

yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredresources
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredResources   # Tên CRD sẽ được tạo
      validation:
        openAPIV3Schema:
          type: object
          properties:
            limits:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredresources

        violation[{"msg": msg, "details": {}}] {
          container := input.review.object.spec.containers[_]
          not container.resources.limits
          msg := sprintf("Container %v must have resource limits", [container.name])
        }
        
        violation[{"msg": msg, "details": {}}] {
          container := input.review.object.spec.containers[_]
          required := input.parameters.limits[_]
          not container.resources.limits[required]
          msg := sprintf("Container %v must have limit for %v", [container.name, required])
        }

Khi ConstraintTemplate được apply:

  1. Gatekeeper compile Rego code
  2. Tạo một CRD mới với kind K8sRequiredResources (match với spec.crd.spec.names.kind)
  3. Từ đó, bạn có thể create Constraint objects của kind đó

Rego Evaluation Model

Rego dùng logic programming semantics. Policy được express dưới dạng rules (conditions phải true để rule active). Violation xảy ra khi violation rule evaluate thành non-empty set.

Quan trọng: input trong Rego context là:

  • input.review.object: Kubernetes resource đang được created/updated
  • input.review.kind: Kind của resource
  • input.review.namespace: Namespace (nếu namespace-scoped)
  • input.parameters: Parameters từ Constraint instance

OPA evaluate Rego theo closed-world assumption: những gì không được explicitly allowed là denied. Điều này có nghĩa là phải cẩn thận với logic — một rule không match không có nghĩa là "allow", mà có nghĩa là "no violation found".

Constraint — Instantiation của Policy

Sau khi ConstraintTemplate tạo CRD, bạn tạo Constraint objects để activate policy với parameters và scope cụ thể:

yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredResources
metadata:
  name: require-cpu-memory-limits
spec:
  enforcementAction: deny      # hoặc warn, hoặc dryrun
  match:
    kinds:
    - apiGroups: ["apps"]
      kinds: ["Deployment"]
    - apiGroups: [""]
      kinds: ["Pod"]
    namespaceSelector:
      matchLabels:
        policy.fleet.io/enforce-resources: "true"  # Chỉ apply cho namespaces có label này
    excludedNamespaces:
    - kube-system
    - config-management-system  # Luôn exclude system namespaces
  parameters:
    limits:
    - cpu
    - memory

Constraint có thể scope theo:

  • kinds: Resource types (Deployment, Pod, Service...)
  • namespaceSelector: Namespaces có labels nhất định
  • excludedNamespaces: Namespace list bị loại trừ
  • labelSelector: Chỉ apply cho resources có certain labels

Một ConstraintTemplate có thể có nhiều Constraint instances — mỗi instance với parameters và scope khác nhau. Ví dụ: một template K8sRequiredResources nhưng hai constraints: một require cpu,memory cho production namespaces, một chỉ require memory cho staging namespaces.

Audit Mode vs Enforce Mode — Hai Mô Hình Hoạt Động Khác Nhau

Enforce Mode (enforcementAction: deny)

Admission webhook block request nếu violate constraint. API server trả về 403 Forbidden response. Resource không được tạo/update.

kubectl apply → API Server → Admission Webhook → Rego evaluate → Violation found → 403 Deny

Enforce mode phù hợp khi:

  • Policy đã được validate và stable
  • Team đã aware và prepared
  • Có rollback plan nếu policy block legitimate requests

Admission webhook failure policy là critical cho enforce mode. Nếu Policy Controller pod không available (crash, OOM, upgrade), và failure policy là Fail, mọi API request sẽ bị block. Điều này có thể gây cluster-wide outage. Luôn cấu hình:

yaml
# Trong Policy Controller configuration
webhookTimeout: 5  # Seconds, không nên quá cao để tránh API server latency

Policy Controller được thiết kế với high availability để giảm thiểu risk này, nhưng failure mode cần được documented và tested.

Audit Mode (enforcementAction: dryrun)

Admission webhook vẫn evaluate resource nhưng không block. Resource được tạo thành công. Violations được recorded vào status.violations của Constraint object.

Đồng thời, Audit Controller scan existing resources và report violations.

yaml
# Xem violations từ audit
kubectl get k8srequiredresources require-cpu-memory-limits -o yaml
# Output sẽ có .status.byPod[].violations với danh sách vi phạm

Audit mode KHÔNG phải chỉ để "test policy". Trong production, audit mode có vai trò riêng biệt:

Audit mode production use cases:

  1. Visibility vào existing violations: Nhiều clusters có resources đã được tạo trước khi policy áp dụng. Audit mode cho biết scope của violation mà không block operations.
  2. Gradual rollout: Enable audit mode trên toàn fleet, fix violations, sau đó switch sang enforce.
  3. Non-blocking compliance reporting: Một số organizations muốn visibility vào violations nhưng không muốn block (autonomous teams can self-remediate).
  4. Cross-cluster fleet compliance dashboard: Aggregate violations từ mọi cluster để measure fleet compliance posture.

Warn Mode (enforcementAction: warn)

Kubernetes 1.22+ hỗ trợ warn enforcement action: resource được created nhưng API server trả về warning header. kubectl sẽ hiển thị warning, còn request thì succeed.

Phù hợp cho: policies không cần hard block nhưng muốn developer aware (ví dụ: deprecated API usage, suboptimal configuration patterns).

Fleet-Level Policy Distribution Qua Config Sync

Policy Controller thực sự powerful khi kết hợp với Config Sync để distribute constraints xuyên fleet:

Architecture Fleet Policy

Git Repo: fleet-policy-config/
├── constraint-templates/
│   ├── k8srequiredresources.yaml
│   ├── k8sallowedregistries.yaml
│   └── k8snoprividgedcontainers.yaml
└── constraints/
    ├── prod/
    │   ├── require-resources.yaml     # enforcementAction: deny
    │   └── allowed-registries.yaml   # restrict to corp registry
    └── staging/
        ├── require-resources.yaml     # enforcementAction: dryrun
        └── allowed-registries.yaml   # allow more registries

Platform team manage ConstraintTemplates (policy logic) và Constraints (instantiation). Config Sync distribute chúng đến clusters theo environment.

Clusters sẽ có different RootSync configs:

  • Prod clusters sync từ prod/ directory → enforce mode
  • Staging clusters sync từ staging/ directory → audit mode

Điều này cho phép gradual rollout: test policy ở staging (audit), validate không có false positives, rồi promote sang prod (enforce).

Policy Library Bundled

Policy Controller cung cấp sẵn một policy library với hơn 60 pre-built ConstraintTemplates cho common use cases:

  • Restrict container image registries
  • Require security contexts
  • Block privileged containers
  • Require resource limits
  • Enforce pod disruption budgets
  • Disallow host namespaces
  • Require HTTPS ingress
bash
# Install policy library
kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/policy-controller-bundles/main/...

Không cần viết Rego từ đầu cho common security policies — reuse library và customize via Constraint parameters.

Admission Webhook Integration Trong Request Lifecycle

Hiểu chính xác khi nào Policy Controller được evaluate trong Kubernetes request pipeline là quan trọng để troubleshoot:

kubectl apply Deployment

API Server Authentication (x.509, token, OIDC)

API Server Authorization (RBAC check)

MutatingAdmissionWebhook (mutate resource - add labels, inject sidecars...)

Object Schema Validation

ValidatingAdmissionWebhook ← Policy Controller chạy đây

etcd persist

Response to client

Policy Controller là ValidatingAdmissionWebhook — chạy sau Mutating webhooks. Điều này quan trọng: nếu một mutating webhook thêm resource limits vào Pod spec, Policy Controller sẽ evaluate Pod spec ĐÃ ĐƯỢC MUTATE — tức là resource limits đã có. Policy "require resource limits" sẽ pass.

Timeout implications: Mỗi webhook có timeout (Policy Controller default 10s). Nếu Rego evaluation tốn hơn 10s (với rất nhiều constraints hoặc Rego logic phức tạp), webhook sẽ timeout. Behavior phụ thuộc vào failurePolicy:

  • failurePolicy: Fail: Request bị reject (safe but disruptive)
  • failurePolicy: Ignore: Request được allow dù webhook fail (unsafe)

Với nhiều constraints, Rego evaluation là bottleneck. Tối ưu hóa: dùng match conditions để scope constraints hẹp, tránh evaluate tất cả constraints cho mọi resource.

Violation Reporting và Remediation Workflow

Đọc Violations

bash
# List tất cả violations cho một constraint
kubectl describe k8srequiredresources require-cpu-memory-limits

# Output violations
Status:
  By Pod:
  - Audit Timestamp:  2025-06-01T10:00:00Z
    ID:              gatekeeper-audit-...
    Observed Generation: 1
    Operations:
    - audit
    Violations:
    - Enforcement Action:  dryrun
      Kind:               Deployment
      Message:            Container app must have resource limits
      Name:               payments-api
      Namespace:          payments
      Version:            v1

Fleet-Level Compliance Dashboard

Aggregate violations xuyên fleet bằng cách query mọi cluster:

bash
# Script để aggregate violations từ tất cả fleet members
for cluster in $(gcloud container fleet memberships list --format="value(name)"); do
  gcloud container fleet memberships get-credentials $cluster
  kubectl get k8srequiredresources --all-namespaces -o json | \
    jq -r '.items[] | .status.violations[] | 
      {cluster: "'$cluster'", .kind, .name, .namespace, .message}'
done

Policy Controller kết hợp với Cloud Monitoring và Fleet Observability có thể expose compliance metrics ra dashboard tập trung. Fleet Observability (xem Chapter 8) aggregate metrics từ Policy Controller controllers xuyên clusters.

Constraints Và Failure Modes

ConstraintTemplate validation: Rego code được compiled khi ConstraintTemplate apply. Syntax errors trong Rego sẽ fail ConstraintTemplate creation với error message. Test Rego logic locally trước khi push lên fleet.

Constraint match too broad: Nếu Constraint match kinds: [Pod] và không exclude system namespaces (kube-system, gatekeeper-system, config-management-system), system Pods có thể violate constraints. Luôn add excludedNamespaces cho system namespaces.

Policy Controller pod restart: Khi gatekeeper-controller-manager pod restart, webhook configuration vẫn active nhưng không có controller serve requests. Trong khoảng thời gian này, nếu failurePolicy: Fail, mọi create/update request sẽ fail. Điều này là trade-off giữa security (Fail) và availability (Ignore). Khuyến nghị: dùng Fail cho security-critical constraints, Ignore cho operational convenience constraints.

Constraint không apply retroactively: Khi tạo Constraint mới trong enforce mode, nó chỉ block NEW creates/updates. Existing resources vi phạm không bị force deleted — chỉ audit controller report violations. Xử lý existing violations là separate remediation task.

CEL-based mutation (Mutating Admission Policy): Kubernetes 1.28+ và Policy Controller có thể dùng CEL-based policies thay vì Rego cho simple validation. Nhưng Rego vẫn cần thiết cho complex logic.

References