Install any skill in seconds. Free to start, no credit card required.
Get Started Free →This skill covers implementing Open Policy Agent (OPA) and Gatekeeper for policy-as-code enforcement in Kubernetes and CI/CD pipelines. It addresses writing Rego policies, deploying OPA Gatekeeper as a Kubernetes admission controller, testing policies in development, and integrating policy evaluation into deployment pipelines.
.claude/skills/implementing-policy-as-code-with-open-policy-agent/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-13 | ✗→✓ | ▲ Improved | — | — |
| case-15 | ✗→✓ | ▲ Improved | — | — |
| case-04 | ✗→✓ | ▲ Improved | — | — |
| case-19 | ✗→✓ | ▲ Improved | — | — |
| case-05 | ✗→✓ | ▲ Improved | — | — |
Do not use for vulnerability scanning (use Trivy/Checkov), for runtime threat detection (use Falco), or for network policy enforcement (use Kubernetes NetworkPolicy or Calico).
bash# Install Gatekeeper via Helm helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts helm install gatekeeper gatekeeper/gatekeeper \ --namespace gatekeeper-system --create-namespace \ --set replicas=3 \ --set audit.replicas=1 \ --set audit.writeToRAMDisk=true
yaml# templates/k8s-required-labels.yaml 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]) } --- # templates/k8s-container-limits.yaml apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8scontainerlimits spec: crd: spec: names: kind: K8sContainerLimits validation: openAPIV3Schema: type: object properties: cpu: type: string memory: type: string targets: - target: admission.k8s.gatekeeper.sh rego: | package k8scontainerlimits 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]) } --- # templates/k8s-block-privileged.yaml apiVersion: templates.gatekeeper.sh/v1 kind: ConstraintTemplate metadata: name: k8sblockprivileged spec: crd: spec: names: kind: K8sBlockPrivileged targets: - target: admission.k8s.gatekeeper.sh rego: | package k8sblockprivileged violation[{"msg": msg}] { container := input.review.object.spec.containers[_] container.securityContext.privileged == true msg := sprintf("Privileged container not allowed: %v", [container.name]) } violation[{"msg": msg}] { container := input.review.object.spec.initContainers[_] container.securityContext.privileged == true msg := sprintf("Privileged init container not allowed: %v", [container.name]) }
yaml# constraints/require-labels.yaml apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sRequiredLabels metadata: name: require-team-labels spec: enforcementAction: deny match: kinds: - apiGroups: [""] kinds: ["Namespace"] - apiGroups: ["apps"] kinds: ["Deployment", "StatefulSet"] excludedNamespaces: - kube-system - gatekeeper-system parameters: labels: - "team" - "environment" - "cost-center" --- # constraints/block-privileged.yaml apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sBlockPrivileged metadata: name: block-privileged-containers spec: enforcementAction: deny match: kinds: - apiGroups: [""] kinds: ["Pod"] - apiGroups: ["apps"] kinds: ["Deployment", "DaemonSet", "StatefulSet"] excludedNamespaces: - kube-system
bash# Install conftest brew install conftest # Test Kubernetes manifests against OPA policies locally conftest test deployment.yaml --policy policies/ --output json # Test Terraform against OPA policies conftest test terraform/main.tf --policy policies/terraform/ --parser hcl2 # Test Dockerfiles conftest test Dockerfile --policy policies/docker/
rego# policies/kubernetes/deny_latest_tag.rego package kubernetes deny[msg] { input.kind == "Deployment" container := input.spec.template.spec.containers[_] endswith(container.image, ":latest") msg := sprintf("Container %v uses :latest tag. Pin to specific version.", [container.name]) } deny[msg] { input.kind == "Deployment" container := input.spec.template.spec.containers[_] not contains(container.image, ":") msg := sprintf("Container %v has no tag. Pin to specific version.", [container.name]) }
yaml# .github/workflows/policy-test.yml name: Policy Validation on: pull_request: paths: ['k8s/**', 'terraform/**', 'policies/**'] jobs: conftest: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install conftest run: | wget -q https://github.com/open-policy-agent/conftest/releases/download/v0.50.0/conftest_0.50.0_Linux_x86_64.tar.gz tar xzf conftest_0.50.0_Linux_x86_64.tar.gz sudo mv conftest /usr/local/bin/ - name: Test K8s manifests run: conftest test k8s/**/*.yaml --policy policies/kubernetes/ --output json - name: Test Terraform run: conftest test terraform/*.tf --policy policies/terraform/ --parser hcl2
| Term | Definition | |------|------------| | OPA | Open Policy Agent — general-purpose policy engine using Rego language for policy decisions | | Rego | OPA's declarative query language for writing policy rules | | Gatekeeper | Kubernetes-native OPA integration implementing admission control via ConstraintTemplates | | ConstraintTemplate | CRD defining the Rego policy logic and parameters schema for a class of constraints | | Constraint | Instance of a ConstraintTemplate with specific parameters and scope (which resources to check) | | Admission Controller | Kubernetes component that intercepts API requests before persistence and can allow or deny them | | conftest | CLI tool for testing structured data (YAML, JSON, HCL) against OPA policies |
Context: Multiple development teams deploy to shared Kubernetes clusters. Some teams run privileged containers and images without resource limits, causing security and stability issues.
Approach:
enforcementAction: warn to identify violations without blocking deploymentsenforcementAction: deny after the remediation periodexcludedNamespaces for kube-system and monitoring namespacesPitfalls: Deploying Gatekeeper with deny mode immediately can break existing workloads. Always start with warn mode. Overly restrictive policies without exemptions for system namespaces can prevent cluster components from functioning.
OPA Policy Evaluation Report
==============================
Cluster: production-east
Date: 2026-02-23
Gatekeeper Version: 3.16.0
CONSTRAINT SUMMARY:
K8sRequiredLabels: 12 violations (warn)
K8sBlockPrivileged: 0 violations (deny)
K8sContainerLimits: 8 violations (deny)
K8sBlockLatestTag: 3 violations (deny)
BLOCKED DEPLOYMENTS (deny):
[K8sContainerLimits] deployment/api-server in ns/payments
- Container 'api' has no memory limit
[K8sBlockLatestTag] deployment/frontend in ns/web
- Container 'nginx' uses :latest tag
AUDIT VIOLATIONS (warn):
[K8sRequiredLabels] namespace/staging
- Missing labels: {cost-center}| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-02 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-23 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +30 percentage points is the difference between those two pass rates over the 23 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.