Gatekeeper 3.1: Policy as Code Goes Production

K8s Guru
5 min read
Gatekeeper 3.1: Policy as Code Goes Production

Introduction

By late 2020, Gatekeeper 3.1 had matured into a production-ready policy enforcement tool for Kubernetes. With improved performance, a curated policy library, and migration tools, Gatekeeper became a viable alternative to PodSecurityPolicy and enabled teams to implement policy-as-code at scale.

This mattered because policy enforcement had become essential for multi-tenant clusters, compliance requirements, and security best practices. While PodSecurityPolicy provided basic pod security controls, it had limitations that made it difficult to use in production. Gatekeeper offered a more flexible, scalable approach that could enforce policies beyond just pod security.

Historical note: Gatekeeper 3.1 was released in May 2020, but by late 2020 it had gained significant production adoption. With PodSecurityPolicy deprecation on the horizon (announced in Kubernetes 1.21), teams began migrating to Gatekeeper as a replacement.

Gatekeeper Production Features

Performance Improvements

  • Parallel Audit: Audit controller parallelizes evaluations across namespaces, reducing audit time by up to 5x.
  • Caching: Improved caching reduces API server load.
  • Metrics: Comprehensive metrics for monitoring policy enforcement.
  • Status Reporting: Constraint status provides violation counts and remediation hints.

Policy Library

  • Community Templates: Pre-built constraint templates for common use cases.
  • Versioned Artifacts: Templates with semantic versioning and documentation.
  • kubectl Plugin: kubectl gatekeeper plugin for managing policies.
  • Migration Tools: Tools for migrating from PodSecurityPolicy to Gatekeeper.

Mutation Support (Alpha)

  • Default Injection: Automatically inject defaults into resources.
  • Sidecar Injection: Inject sidecars based on policies.
  • Label Enforcement: Automatically add required labels.
  • Safe Testing: Dry-run and audit-only modes for safe testing.

Gatekeeper vs PodSecurityPolicy

CapabilityGatekeeperPodSecurityPolicy
Policy ScopeAny Kubernetes resourcePods only
Policy LanguageRego (OPA)Kubernetes API
RBAC ComplexitySimplifiedComplex (requires PSP binding)
PerformanceGood (caching, parallelization)Poor (sequential evaluation)
MutationYes (alpha)No
Audit ModeYesLimited
Best ForComprehensive policy enforcementPod security only

Migration from PodSecurityPolicy

PSP Policy Example

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
spec:
  privileged: false
  allowPrivilegeEscalation: false
  requiredDropCapabilities:
    - ALL
  volumes:
    - 'configMap'
    - 'emptyDir'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    rule: 'MustRunAsNonRoot'

Equivalent Gatekeeper Constraint

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8spspsecuritycontext
spec:
  crd:
    spec:
      names:
        kind: K8sPSPSecurityContext
      validation:
        openAPIV3Schema:
          type: object
          properties:
            runAsNonRoot:
              type: boolean
            allowPrivilegeEscalation:
              type: boolean
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8spspsecuritycontext
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not container.securityContext.runAsNonRoot
          msg := "Container must run as non-root"
        }
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPSecurityContext
metadata:
  name: must-run-as-non-root
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
  parameters:
    runAsNonRoot: true
    allowPrivilegeEscalation: false

Common Policy Patterns

Require Resource Limits

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8srequiredresources
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredResources
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredresources
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not container.resources.limits.memory
          msg := "Container must have memory limits"
        }

Disallow Latest Tags

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8srequiredimagetag
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredImageTag
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredimagetag
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          endswith(container.image, ":latest")
          msg := "Container image must not use :latest tag"
        }

Require Labels

apiVersion: templates.gatekeeper.sh/v1beta1
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}] {
          required := input.parameters.labels[_]
          not input.review.object.metadata.labels[required]
          msg := sprintf("Label '%v' is required", [required])
        }

Multi-Cluster Policy Management

Policy Distribution

# Policy repository structure
policies/
├── constraint-templates/
│   ├── k8s-requiredresources.yaml
│   ├── k8s-requiredlabels.yaml
│   └── k8s-pspsecuritycontext.yaml
├── constraints/
│   ├── production/
│   │   ├── resource-limits.yaml
│   │   └── security-context.yaml
│   └── staging/
│       ├── resource-limits.yaml
│       └── security-context.yaml

GitOps Integration

# ArgoCD Application for policies
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: gatekeeper-policies
spec:
  source:
    repoURL: https://github.com/org/policies
    path: constraints/production
  destination:
    server: https://kubernetes.default.svc

Practical Considerations

Performance Tuning

  • Audit Interval: Adjust audit interval based on cluster size and policy complexity.
  • Constraint Count: Limit number of constraints to avoid performance degradation.
  • Caching: Enable caching for frequently evaluated policies.
  • Metrics: Monitor policy evaluation latency and adjust as needed.

Policy Testing

  • Dry-Run Mode: Test policies in dry-run mode before enforcement.
  • Audit Mode: Use audit mode to identify violations without blocking.
  • Staging Environment: Test policies in staging before production.

Policy Maintenance

  • Version Control: Store policies in Git for version control.
  • Documentation: Document why each policy exists and what it enforces.
  • Regular Review: Review policies regularly and remove unused policies.

Getting Started

# Install Gatekeeper
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.1/deploy/gatekeeper.yaml

# Install constraint template
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/restricted-psp/template.yaml

# Install constraint
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/restricted-psp/samples/restricted-psp/constraint.yaml

# Verify installation
kubectl get constrainttemplates
kubectl get constraints

Caveats & Lessons Learned

  • Rego Learning Curve: Writing Rego policies requires learning the language.
  • Policy Complexity: Complex policies can impact performance; optimize carefully.
  • False Positives: Policies may block legitimate workloads; test thoroughly.
  • Migration Effort: Migrating from PSP requires rewriting policies.

Common Failure Modes

  • “Policy too restrictive”: Policies block legitimate workloads; adjust policies.
  • “Performance degradation”: Too many constraints slow down cluster; optimize policies.
  • “Policy not enforced”: Constraints not matching resources; check match criteria.

Conclusion

Gatekeeper’s production readiness in 2020 marked a turning point in Kubernetes policy enforcement. It provided a flexible, scalable alternative to PodSecurityPolicy that could enforce policies beyond just pod security. With improved performance, a curated policy library, and migration tools, Gatekeeper became the tool of choice for teams implementing policy-as-code.

For organizations with multi-tenant clusters or compliance requirements, Gatekeeper became essential for policy enforcement. It demonstrated that Kubernetes policy enforcement didn’t have to be limited to pod security—it could be comprehensive, flexible, and manageable at scale. Gatekeeper proved that policy-as-code could be both powerful and production-ready.

The patterns and practices established with Gatekeeper would influence the development of other policy engines (Kyverno, Kubewarden) and set the foundation for comprehensive policy enforcement in Kubernetes. Gatekeeper demonstrated that Kubernetes security could be both declarative and enforceable, enabling teams to codify security policies alongside application code.