SiamCafe.net Blog
Technology

OPA Gatekeeper Open Source Contribution — Policy Engine สำหรับ Kubernetes

opa gatekeeper open source contribution
OPA Gatekeeper Open Source Contribution | SiamCafe Blog
2026-06-01· อ. บอม — SiamCafe.net· 1,356 คำ

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 ก่อนเสมอ

📖 บทความที่เกี่ยวข้อง

Linux Namespaces Open Source Contributionอ่านบทความ → BigQuery Scheduled Query Open Source Contributionอ่านบทความ → OPA Gatekeeper Observability Stackอ่านบทความ → DNSSEC Implementation Open Source Contributionอ่านบทความ → OPA Gatekeeper Multi-tenant Designอ่านบทความ →

📚 ดูบทความทั้งหมด →