SiamCafe · Blog
OPA Gatekeeper Open Source Contribution — Policy
บทความ

OPA Gatekeeper Open Source Contribution — Policy

เผยแพร่ 28 พฤษภาคม 2569

OPA Gatekeeper คืออะไร

OPA (Open Policy Agent) เป็น open source policy engine ที่ใช้ภาษา Rego สำหรับเขียน policies Gatekeeper เป็น Kubernetes-native implementation ของ OPA ที่ทำงานเป็น admission controller ตรวจสอบ Kubernetes resources ก่อนที่จะถูกสร้างหรือแก้ไขใน cluster

Gatekeeper ช่วยบังคับ policies เช่น ห้ามใช้ container images จาก untrusted registries, บังคับ resource limits สำหรับทุก Pod, ห้าม privileged containers, บังคับ labels ที่จำเป็น, จำกัด namespaces ที่สามารถสร้างได้ และ enforce network policies

Open Source Contribution สำหรับ OPA Gatekeeper เป็นวิธีดีที่จะเรียนรู้ Kubernetes internals, policy-as-code และ contribute กลับให้ community ซึ่ง project อยู่ภายใต้ CNCF (Cloud Native Computing Foundation) มี active community และ welcoming สำหรับ new contributors

ติดตั้ง OPA Gatekeeper บน Kubernetes

วิธีติดตั้งและตั้งค่า Gatekeeper

# === ติดตั้ง OPA Gatekeeper ===

# 1. Using Helm (แนะนำ)
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update

helm install gatekeeper gatekeeper/gatekeeper \
    --namespace gatekeeper-system \
    --create-namespace \
    --set replicas=3 \
    --set audit.replicas=1 \
    --set auditInterval=60

# 2. Using kubectl
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/v3.15.0/deploy/gatekeeper.yaml

# ตรวจสอบ installation
kubectl get pods -n gatekeeper-system
# NAME                                            READY   STATUS
# gatekeeper-audit-xxx                            1/1     Running
# gatekeeper-controller-manager-xxx               1/1     Running
# gatekeeper-controller-manager-xxx               1/1     Running
# gatekeeper-controller-manager-xxx               1/1     Running

# ตรวจสอบ webhook
kubectl get validatingwebhookconfigurations
# NAME                    WEBHOOKS   AGE
# gatekeeper-validating   1          5m

# ตรวจสอบ CRDs
kubectl get crd | grep gatekeeper
# assign.mutations.gatekeeper.sh
# assignmetadata.mutations.gatekeeper.sh
# configs.config.gatekeeper.sh
# constraintpodstatuses.status.gatekeeper.sh
# constrainttemplatepodstatuses.status.gatekeeper.sh
# constrainttemplates.templates.gatekeeper.sh
# expansiontemplate.expansion.gatekeeper.sh
# modifyset.mutations.gatekeeper.sh
# providers.externaldata.gatekeeper.sh

# === Gatekeeper Config ===
cat <<'EOF' | kubectl apply -f -
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
  name: config
  namespace: gatekeeper-system
spec:
  sync:
    syncOnly:
      - group: ""
        version: "v1"
        kind: "Namespace"
      - group: ""
        version: "v1"
        kind: "Pod"
      - group: "networking.k8s.io"
        version: "v1"
        kind: "Ingress"
  match:
    - excludedNamespaces: ["kube-system", "gatekeeper-system"]
      processes: ["*"]
EOF

echo "Gatekeeper installed"

เขียน Constraint Templates และ Policies

สร้าง custom policies ด้วย Rego

# === Constraint Template: Required Labels ===
cat <<'EOF' | kubectl apply -f -
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels

        violation[{"msg": msg}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("Missing required labels: %v", [missing])
        }
EOF

# === Constraint: Enforce Labels ===
cat <<'EOF' | kubectl apply -f -
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-team-label
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
      - apiGroups: ["apps"]
        kinds: ["Deployment"]
    excludedNamespaces: ["kube-system", "gatekeeper-system"]
  parameters:
    labels:
      - "team"
      - "environment"
EOF

# === Constraint Template: Container Image Allowlist ===
cat <<'EOF' | kubectl apply -f -
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sallowedrepos
spec:
  crd:
    spec:
      names:
        kind: K8sAllowedRepos
      validation:
        openAPIV3Schema:
          type: object
          properties:
            repos:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sallowedrepos

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not startswith_any(container.image, input.parameters.repos)
          msg := sprintf("Container image '%v' not from allowed repos: %v", [container.image, input.parameters.repos])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.initContainers[_]
          not startswith_any(container.image, input.parameters.repos)
          msg := sprintf("Init container image '%v' not from allowed repos: %v", [container.image, input.parameters.repos])
        }

        startswith_any(str, prefixes) {
          prefix := prefixes[_]
          startswith(str, prefix)
        }
EOF

# === Constraint: Allow Only Trusted Registries ===
cat <<'EOF' | kubectl apply -f -
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: allowed-repos
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
      - apiGroups: ["apps"]
        kinds: ["Deployment", "StatefulSet", "DaemonSet"]
  parameters:
    repos:
      - "gcr.io/my-project/"
      - "docker.io/library/"
      - "registry.k8s.io/"
EOF

# === Constraint Template: Resource Limits ===
cat <<'EOF' | kubectl apply -f -
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sresourcelimits
spec:
  crd:
    spec:
      names:
        kind: K8sResourceLimits
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sresourcelimits

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not container.resources.limits
          msg := sprintf("Container '%v' has no resource limits", [container.name])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not container.resources.limits.cpu
          msg := sprintf("Container '%v' has no CPU limit", [container.name])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not container.resources.limits.memory
          msg := sprintf("Container '%v' has no memory limit", [container.name])
        }
EOF

# Test violation
kubectl create deployment test --image=nginx --dry-run=server
# Error: admission webhook denied the request: Missing required labels

echo "Policies created"

Contribute to OPA Gatekeeper Project

วิธี contribute to open source project

=== Contributing to OPA Gatekeeper ===

1. Fork and Clone

git clone https://github.com/YOUR_USERNAME/gatekeeper.git

cd gatekeeper

git remote add upstream https://github.com/open-policy-agent/gatekeeper.git

git fetch upstream

2. Development Setup

Prerequisites: Go 1.21+, Docker, kind/minikube, make

Install dependencies

go mod download

Build

make build

Run tests

make test

Run unit tests only

go test ./pkg/... -v

Run integration tests

make test-e2e

3. Local Development with kind

kind create cluster --name gatekeeper-dev

Build and load local image

make docker-buildx IMG=gatekeeper:dev

kind load docker-image gatekeeper:dev --name gatekeeper-dev

Deploy local build

make deploy IMG=gatekeeper:dev

4. Finding Issues to Work On

GitHub Issues with labels:

  • "good first issue" — สำหรับ first-time contributors
  • "help wanted" — ต้องการความช่วยเหลือ
  • "kind/bug" — bug fixes
  • "kind/feature" — new features
  • "area/rego" — Rego policy related
  • "area/docs" — documentation improvements

5. Making a Contribution

Create feature branch

git checkout -b feature/my-contribution

Make changes

... edit files ...

Run linter

make lint

Run tests

make test

Commit with conventional format

git commit -m "feat: add support for ephemeral containers in resource limits"

Or: fix: correct label matching for wildcard patterns

Or: docs: update constraint template examples

Or: test: add unit tests for mutation webhook

Push and create PR

git push origin feature/my-contribution

Create Pull Request on GitHub

6. PR Checklist

[ ] Tests pass (make test)

[ ] Linting passes (make lint)

[ ] Documentation updated if needed

[ ] CHANGELOG.md updated for user-facing changes

[ ] Signed-off commits (git commit -s)

[ ] PR description explains the change clearly

[ ] Referenced related issue (#number)

7. Types of Contributions

  • New Constraint Templates (policy library)
  • Bug fixes in admission controller
  • Performance improvements
  • Documentation improvements
  • Test coverage improvements
  • CI/CD pipeline improvements
  • Rego library functions
  • External data provider integrations

echo "Contribution guide complete"

Testing และ CI/CD สำหรับ Policies

ทดสอบ policies อย่างเป็นระบบ

#!/usr/bin/env python3
# policy_tester.py — OPA Gatekeeper Policy Testing
import subprocess
import json
import yaml
import logging
from pathlib import Path
from typing import Dict, List

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("policy_test")

class GatekeeperPolicyTester:
    def __init__(self, policies_dir="policies"):
        self.policies_dir = Path(policies_dir)
    
    def test_rego_unit(self, rego_file, test_file):
        """Run OPA unit tests on Rego policies"""
        result = subprocess.run(
            ["opa", "test", str(rego_file), str(test_file), "-v"],
            capture_output=True, text=True,
        )
        
        passed = result.returncode == 0
        logger.info(f"Rego test {'PASSED' if passed else 'FAILED'}: {rego_file}")
        
        return {
            "file": str(rego_file),
            "passed": passed,
            "output": result.stdout,
            "errors": result.stderr,
        }
    
    def test_constraint_template(self, template_file):
        """Validate ConstraintTemplate YAML"""
        content = yaml.safe_load(Path(template_file).read_text())
        
        errors = []
        
        # Check required fields
        if content.get("kind") != "ConstraintTemplate":
            errors.append("kind must be ConstraintTemplate")
        
        spec = content.get("spec", {})
        if not spec.get("crd"):
            errors.append("spec.crd is required")
        
        targets = spec.get("targets", [])
        if not targets:
            errors.append("spec.targets is required")
        
        for target in targets:
            if not target.get("rego"):
                errors.append("targets[].rego is required")
            
            # Validate Rego syntax
            if target.get("rego"):
                rego_check = subprocess.run(
                    ["opa", "check", "--strict", "-"],
                    input=target["rego"],
                    capture_output=True, text=True,
                )
                if rego_check.returncode != 0:
                    errors.append(f"Rego syntax error: {rego_check.stderr}")
        
        passed = len(errors) == 0
        logger.info(f"Template {'VALID' if passed else 'INVALID'}: {template_file}")
        
        return {
            "file": str(template_file),
            "valid": passed,
            "errors": errors,
        }
    
    def dry_run_constraint(self, constraint_file, test_resources):
        """Test constraint against sample resources"""
        results = []
        
        for resource in test_resources:
            cmd = [
                "kubectl", "apply", "-f", resource,
                "--dry-run=server", "-o", "json",
            ]
            
            result = subprocess.run(cmd, capture_output=True, text=True)
            
            denied = result.returncode != 0 and "denied" in result.stderr.lower()
            
            results.append({
                "resource": resource,
                "admitted": result.returncode == 0,
                "denied": denied,
                "message": result.stderr if denied else "",
            })
        
        return results
    
    def run_all_tests(self):
        """Run all policy tests"""
        results = {"templates": [], "rego_tests": [], "summary": {}}
        
        # Test all constraint templates
        for template in self.policies_dir.glob("**/constraint-template*.yaml"):
            result = self.test_constraint_template(template)
            results["templates"].append(result)
        
        # Run Rego unit tests
        for test in self.policies_dir.glob("**/*_test.rego"):
            rego = test.with_name(test.name.replace("_test.rego", ".rego"))
            if rego.exists():
                result = self.test_rego_unit(rego, test)
                results["rego_tests"].append(result)
        
        # Summary
        results["summary"] = {
            "templates_tested": len(results["templates"]),
            "templates_valid": sum(1 for t in results["templates"] if t["valid"]),
            "rego_tests_run": len(results["rego_tests"]),
            "rego_tests_passed": sum(1 for t in results["rego_tests"] if t["passed"]),
        }
        
        return results

# === Rego Unit Test Example ===
# policies/required_labels_test.rego
#
# package k8srequiredlabels
#
# test_missing_labels {
#   violation[{"msg": msg}] with input as {
#     "review": {
#       "object": {
#         "metadata": {
#           "labels": {"app": "nginx"}
#         }
#       }
#     },
#     "parameters": {
#       "labels": ["team", "environment"]
#     }
#   }
#   contains(msg, "Missing required labels")
# }
#
# test_all_labels_present {
#   count(violation) == 0 with input as {
#     "review": {
#       "object": {
#         "metadata": {
#           "labels": {"team": "platform", "environment": "dev"}
#         }
#       }
#     },
#     "parameters": {
#       "labels": ["team", "environment"]
#     }
#   }
# }

# tester = GatekeeperPolicyTester("policies")
# results = tester.run_all_tests()
# print(json.dumps(results, indent=2))

Production Best Practices

แนวทางใช้งาน Gatekeeper ใน production

=== Production Best Practices ===

1. Start with Audit Mode (Dry Run)

ใช้ enforcementAction: dryrun ก่อน deploy จริง

เพื่อดูว่า policies จะ affect resources อะไรบ้าง

cat <<'EOF' | kubectl apply -f -

apiVersion: constraints.gatekeeper.sh/v1beta1

kind: K8sRequiredLabels

metadata:

name: require-labels-audit

spec:

enforcementAction: dryrun

match:

kinds:

  • apiGroups: ["apps"]

kinds: ["Deployment"]

parameters:

labels: ["team", "environment"]

EOF

Check audit results

kubectl get k8srequiredlabels require-labels-audit -o yaml | grep -A 20 "violations"

2. Gradual Rollout

Phase 1: dryrun (audit only, no blocking)

Phase 2: warn (show warning but allow)

Phase 3: deny (block non-compliant resources)

3. Exclude Critical Namespaces

Always exclude: kube-system, gatekeeper-system

Consider excluding: monitoring, cert-manager

4. High Availability

helm values:

replicas: 3

audit:

replicas: 1

podDisruptionBudget:

minAvailable: 2

resources:

limits:

cpu: 1000m

memory: 512Mi

requests:

cpu: 100m

memory: 256Mi

5. Monitoring Gatekeeper

Prometheus metrics exposed on :8888/metrics

Key metrics:

  • gatekeeper_violations (total violations per constraint)
  • gatekeeper_audit_duration_seconds
  • gatekeeper_constraint_templates (total templates)
  • gatekeeper_constraints (total constraints)
  • gatekeeper_request_count (webhook requests)
  • gatekeeper_request_duration_seconds

Grafana dashboard:

Import dashboard ID: 15763

Alert rules:

  • gatekeeper_violations increasing rapidly
  • gatekeeper_request_duration_seconds > 1s
  • gatekeeper pods not running

6. Emergency Break Glass

If Gatekeeper blocks critical deployments:

Option 1: Delete specific constraint

kubectl delete k8srequiredlabels require-labels-audit

Option 2: Switch to dryrun

kubectl patch k8srequiredlabels require-labels-audit \

--type merge -p '{"spec":{"enforcementAction":"dryrun"}}'

Option 3: Scale down Gatekeeper (last resort)

kubectl scale deployment gatekeeper-controller-manager \

-n gatekeeper-system --replicas=0

Remember to scale back up!

7. Policy Library

Use the Gatekeeper Library for pre-built policies

https://github.com/open-policy-agent/gatekeeper-library

kubectl apply -k https://github.com/open-policy-agent/gatekeeper-library/library

echo "Production setup documented"

FAQ คำถามที่พบบ่อย

Q: OPA Gatekeeper กับ Kyverno ต่างกันอย่างไร?

A: Gatekeeper ใช้ Rego language ที่ powerful แต่ learning curve สูง เหมาะสำหรับ complex policies Kyverno ใช้ YAML-based policies เรียนรู้ง่ายกว่ามาก เหมาะสำหรับ teams ที่ไม่ต้องการเรียนภาษาใหม่ Gatekeeper มี ecosystem ใหญ่กว่า (OPA ใช้กับ services อื่นนอกจาก K8s ได้) Kyverno มี mutation support ที่ดีกว่า สำหรับ organizations ที่ใช้ OPA อยู่แล้ว เลือก Gatekeeper สำหรับ organizations ที่เริ่มใหม่ Kyverno ง่ายกว่า

Q: Gatekeeper ทำให้ deployment ช้าลงไหม?

A: มีผลเล็กน้อย webhook call เพิ่ม latency ~10-50ms ต่อ admission request สำหรับ policies ง่ายๆ สำหรับ complex policies ที่ query external data อาจใช้เวลา 100-500ms ตั้ง timeout ที่เหมาะสม (default 3 seconds) ใช้ failurePolicy: Ignore สำหรับ non-critical policies เพื่อไม่ให้ Gatekeeper ที่มีปัญหา block ทุก deployments

Q: จะเริ่ม contribute ได้อย่างไร?

A: เริ่มจาก good first issues บน GitHub ซึ่งมักเป็น documentation fixes, test improvements, small bug fixes อ่าน CONTRIBUTING.md และ CODE_OF_CONDUCT.md ก่อน join Slack channel (#opa-gatekeeper ใน CNCF Slack) เพื่อถามคำถาม สร้าง constraint templates ใหม่สำหรับ gatekeeper-library เป็น contribution ที่ดีสำหรับเริ่มต้น เพราะไม่ต้องแก้ Go code

Q: Gatekeeper handle cluster upgrades อย่างไร?

A: ก่อน upgrade ตรวจสอบ Gatekeeper compatibility matrix กับ Kubernetes version ที่จะ upgrade upgrade Gatekeeper ก่อน Kubernetes ถ้า Gatekeeper version ใหม่รองรับทั้ง old และ new K8s versions ใช้ Helm upgrade สำหรับ smooth upgrade process backup constraint templates และ constraints ก่อน upgrade ทดสอบใน staging cluster ก่อนเสมอ