Skip to content

Portal | Level: L2: Operations | Topics: Policy Engines, RBAC | Domain: Kubernetes

Policy Engines (OPA/Kyverno) - Primer

Why This Matters

RBAC controls who can do what. Policy engines control what can exist. They enforce organizational standards at the API level: no pods without resource limits, no images from untrusted registries, no privileged containers. Without policy enforcement, one misconfigured deployment can compromise your cluster.

The Problem

Kubernetes RBAC answers: "Can this user create a Deployment?" Policy engines answer: "Should this specific Deployment be allowed?"

RBAC: "Developer can create pods in namespace X" (identity-based)
Policy: "All pods must have resource limits" (content-based)

OPA Gatekeeper vs Kyverno

Feature OPA Gatekeeper Kyverno
Policy language Rego (custom DSL) YAML (native K8s)
Learning curve Steep (Rego is unique) Low (just YAML)
Validation Yes Yes
Mutation Limited Yes (first-class)
Generation No Yes (auto-create resources)
Community CNCF graduated (OPA) CNCF incubating
Best for Complex logic, multi-platform K8s-native teams

Kyverno

Installation

helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno kyverno/kyverno -n kyverno --create-namespace

Policy Types

Type What it does
Validate Block or warn on non-compliant resources
Mutate Automatically modify resources
Generate Auto-create companion resources
VerifyImages Check container image signatures

Validate: Require Resource Limits

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-limits
spec:
  validationFailureAction: Enforce  # or Audit
  rules:
    - name: check-limits
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "CPU and memory limits are required"
        pattern:
          spec:
            containers:
              - resources:
                  limits:
                    memory: "?*"
                    cpu: "?*"

Validate: Restrict Image Registries

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: restrict-registries
spec:
  validationFailureAction: Enforce
  rules:
    - name: validate-registries
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Images must come from ghcr.io or docker.io"
        pattern:
          spec:
            containers:
              - image: "ghcr.io/* | docker.io/*"

Validate: Disallow Privileged Containers

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-privileged
spec:
  validationFailureAction: Enforce
  rules:
    - name: no-privileged
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Privileged containers are not allowed"
        pattern:
          spec:
            containers:
              - securityContext:
                  privileged: "!true"

Mutate: Add Default Labels

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-labels
spec:
  rules:
    - name: add-team-label
      match:
        any:
          - resources:
              kinds:
                - Pod
      mutate:
        patchStrategicMerge:
          metadata:
            labels:
              managed-by: kyverno
              environment: "{{request.namespace}}"

Generate: Auto-Create NetworkPolicy

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: generate-netpol
spec:
  rules:
    - name: default-deny
      match:
        any:
          - resources:
              kinds:
                - Namespace
      generate:
        apiVersion: networking.k8s.io/v1
        kind: NetworkPolicy
        name: default-deny
        namespace: "{{request.object.metadata.name}}"
        data:
          spec:
            podSelector: {}
            policyTypes:
              - Ingress
              - Egress

OPA Gatekeeper

Installation

helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm install gatekeeper gatekeeper/gatekeeper -n gatekeeper-system --create-namespace

How It Works

  1. Define a ConstraintTemplate (the policy logic in Rego)
  2. Create a Constraint (apply the template with parameters)

ConstraintTemplate: Require Labels

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])
        }

Constraint: Apply the Template

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-team-label
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels: ["team", "environment"]

Rego Basics

# Deny if container image uses 'latest' tag
package k8sdisallowlatest

violation[{"msg": msg}] {
  container := input.review.object.spec.containers[_]
  endswith(container.image, ":latest")
  msg := sprintf("Container %v uses :latest tag", [container.name])
}

violation[{"msg": msg}] {
  container := input.review.object.spec.containers[_]
  not contains(container.image, ":")
  msg := sprintf("Container %v has no tag (defaults to :latest)", [container.name])
}

Audit Mode vs Enforce Mode

Mode Behavior
Enforce Reject non-compliant resources at admission
Audit/Warn Allow resources but log violations

Rollout strategy: 1. Deploy policies in Audit mode 2. Review violations: kubectl get policyreport -A (Kyverno) or kubectl get constraint -o yaml 3. Fix existing violations 4. Switch to Enforce mode

Pod Security Standards

Kubernetes has built-in Pod Security Standards (replacing PodSecurityPolicies):

# Apply Pod Security Standards via namespace labels
kubectl label namespace grokdevops \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/warn=restricted \
  pod-security.kubernetes.io/audit=restricted
Level What it allows
Privileged Everything (no restrictions)
Baseline Prevents known privilege escalations
Restricted Strongly restricted (non-root, no caps, read-only root)

Common Policies Every Cluster Should Have

  1. Require resource limits - Prevent noisy neighbors
  2. Disallow privileged containers - Security baseline
  3. Restrict image registries - Only trusted sources
  4. Require non-root user - Reduce attack surface
  5. Disallow latest tag - Ensure reproducibility
  6. Require labels - Cost allocation and ownership
  7. Default deny NetworkPolicy - Network segmentation
  8. Disallow hostPath volumes - Prevent host filesystem access

Common Pitfalls

  1. Enforcing too early — Start in Audit mode. Enforcing immediately breaks existing workloads.
  2. No exceptions — Some system pods need privileges. Use exclusions for kube-system.
  3. Policy conflicts — A mutating policy and validating policy can fight. Test together.
  4. Webhook failures — If Kyverno/Gatekeeper is down, the webhook blocks all resource creation. Set failurePolicy: Ignore for non-critical policies.
  5. Performance — Complex Rego policies add latency to every API call. Keep policies simple.

Wiki Navigation

Prerequisites

Next Steps