OPA Gatekeeper: Production-Grade Policy Enforcement for Kubernetes Clusters
Open Policy Agent (OPA) Gatekeeper has become the industry standard for policy-based control in Kubernetes environments. As a CNCF graduated project, Gatekeeper extends OPA’s policy-as-code capabilities with Kubernetes-native resources, providing declarative admission control that enforces organizational policies across clusters.
In this comprehensive guide, we’ll explore enterprise-grade Gatekeeper implementations, covering constraint template development, mutation policies, audit frameworks, and integration patterns that have proven effective in managing thousands of workloads across multi-tenant environments.
Understanding Gatekeeper Architecture
Core Components and Workflow
Gatekeeper operates as a Kubernetes admission webhook that intercepts API requests before objects are persisted to etcd. The architecture consists of several key components:
Admission Webhook: Validates and mutates resources based on defined policies Constraint Framework: Provides the mechanism for defining and enforcing policies Audit Controller: Periodically evaluates existing resources against constraints Mutation Webhook: Modifies resource specifications based on mutation policies
Let’s deploy a production-ready Gatekeeper installation:
# gatekeeper-deployment.yaml - Production Gatekeeper Setup
apiVersion: v1
kind: Namespace
metadata:
name: gatekeeper-system
labels:
admission.gatekeeper.sh/ignore: "true"
pod-security.kubernetes.io/enforce: restricted
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gatekeeper-audit
namespace: gatekeeper-system
labels:
app: gatekeeper
control-plane: audit-controller
spec:
replicas: 1
selector:
matchLabels:
app: gatekeeper
control-plane: audit-controller
template:
metadata:
labels:
app: gatekeeper
control-plane: audit-controller
spec:
serviceAccountName: gatekeeper-admin
containers:
- name: manager
image: openpolicyagent/gatekeeper:v3.14.0
args:
- --operation=audit
- --operation=status
- --operation=mutation-status
- --logtostderr
- --audit-interval=60
- --constraint-violations-limit=100
- --audit-from-cache=true
- --audit-chunk-size=500
- --emit-audit-events=true
- --audit-events-involved-namespace=true
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
ports:
- containerPort: 8888
name: metrics
protocol: TCP
- containerPort: 9090
name: healthz
protocol: TCP
livenessProbe:
httpGet:
path: /healthz
port: 9090
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /readyz
port: 9090
initialDelaySeconds: 5
periodSeconds: 10
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 1000m
memory: 2Gi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gatekeeper-controller-manager
namespace: gatekeeper-system
labels:
app: gatekeeper
control-plane: controller-manager
spec:
replicas: 3
selector:
matchLabels:
app: gatekeeper
control-plane: controller-manager
template:
metadata:
labels:
app: gatekeeper
control-plane: controller-manager
spec:
serviceAccountName: gatekeeper-admin
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- gatekeeper
topologyKey: kubernetes.io/hostname
containers:
- name: manager
image: openpolicyagent/gatekeeper:v3.14.0
args:
- --port=8443
- --health-addr=:9090
- --prometheus-port=8888
- --logtostderr
- --log-denies=true
- --emit-admission-events=true
- --admission-events-involved-namespace=true
- --log-level=INFO
- --exempt-namespace=gatekeeper-system
- --operation=webhook
- --operation=mutation-webhook
- --disable-opa-builtin={http.send}
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
ports:
- containerPort: 8443
name: webhook-server
protocol: TCP
- containerPort: 8888
name: metrics
protocol: TCP
- containerPort: 9090
name: healthz
protocol: TCP
livenessProbe:
httpGet:
path: /healthz
port: 9090
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /readyz
port: 9090
initialDelaySeconds: 5
periodSeconds: 10
resources:
requests:
cpu: 250m
memory: 1Gi
limits:
cpu: 2000m
memory: 4Gi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
- mountPath: /certs
name: cert
readOnly: true
volumes:
- name: cert
secret:
secretName: gatekeeper-webhook-server-cert
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: gatekeeper-admin
namespace: gatekeeper-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: gatekeeper-manager-role
rules:
- apiGroups:
- "*"
resources:
- "*"
verbs:
- get
- list
- watch
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- config.gatekeeper.sh
resources:
- configs
- configs/status
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- constraints.gatekeeper.sh
resources:
- "*"
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- mutations.gatekeeper.sh
resources:
- "*"
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- status.gatekeeper.sh
resources:
- "*"
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- templates.gatekeeper.sh
resources:
- constrainttemplates
- constrainttemplates/status
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: gatekeeper-manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: gatekeeper-manager-role
subjects:
- kind: ServiceAccount
name: gatekeeper-admin
namespace: gatekeeper-system
Constraint Template Development
Enterprise Constraint Templates
Let’s create comprehensive constraint templates for common enterprise requirements:
# constraint-templates.yaml - Enterprise Constraint Templates
---
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
annotations:
description: "Requires resources to have specified labels"
compliance: "CIS Kubernetes Benchmark 5.7.3"
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
openAPIV3Schema:
type: object
properties:
labels:
type: array
description: "List of required labels"
items:
type: object
properties:
key:
type: string
allowedRegex:
type: string
message:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg, "details": {"missing_labels": missing}}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_].key}
missing := required - provided
count(missing) > 0
msg := sprintf("You must provide labels: %v", [missing])
}
violation[{"msg": msg}] {
value := input.review.object.metadata.labels[key]
expected := input.parameters.labels[_]
expected.key == key
expected.allowedRegex != ""
not re_match(expected.allowedRegex, value)
msg := sprintf("Label <%v: %v> does not match required regex: %v", [key, value, expected.allowedRegex])
}
---
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8scontainerlimits
annotations:
description: "Requires containers to have resource limits"
compliance: "CIS Kubernetes Benchmark 5.2.1, 5.2.2"
spec:
crd:
spec:
names:
kind: K8sContainerLimits
validation:
openAPIV3Schema:
type: object
properties:
cpu:
type: string
memory:
type: string
enforceLimitsEqualsRequests:
type: boolean
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8scontainerlimits
missing_limits(container) {
not container.resources.limits
}
missing_limits(container) {
not container.resources.limits.cpu
}
missing_limits(container) {
not container.resources.limits.memory
}
violation[{"msg": msg, "details": {"missing_limits": name}}] {
container := input.review.object.spec.containers[_]
missing_limits(container)
name := container.name
msg := sprintf("Container <%v> does not have resource limits defined", [name])
}
violation[{"msg": msg}] {
input.parameters.enforceLimitsEqualsRequests
container := input.review.object.spec.containers[_]
not container.resources.limits.cpu == container.resources.requests.cpu
msg := sprintf("Container <%v> must have CPU limits equal to requests", [container.name])
}
violation[{"msg": msg}] {
input.parameters.enforceLimitsEqualsRequests
container := input.review.object.spec.containers[_]
not container.resources.limits.memory == container.resources.requests.memory
msg := sprintf("Container <%v> must have memory limits equal to requests", [container.name])
}
---
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sallowedrepos
annotations:
description: "Requires container images to come from approved registries"
compliance: "Supply Chain Security"
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[_]
satisfied := [good | repo = input.parameters.repos[_] ; good = startswith(container.image, repo)]
not any(satisfied)
msg := sprintf("Container <%v> has an invalid image repo <%v>, allowed repos are %v", [container.name, container.image, input.parameters.repos])
}
violation[{"msg": msg}] {
container := input.review.object.spec.initContainers[_]
satisfied := [good | repo = input.parameters.repos[_] ; good = startswith(container.image, repo)]
not any(satisfied)
msg := sprintf("InitContainer <%v> has an invalid image repo <%v>, allowed repos are %v", [container.name, container.image, input.parameters.repos])
}
---
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sblocknodeselector
annotations:
description: "Prevents use of node selectors that could break tenant isolation"
compliance: "Multi-tenancy Isolation"
spec:
crd:
spec:
names:
kind: K8sBlockNodeSelector
validation:
openAPIV3Schema:
type: object
properties:
blockedNodeSelectors:
type: array
items:
type: object
properties:
key:
type: string
value:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sblocknodeselector
violation[{"msg": msg}] {
blocked := input.parameters.blockedNodeSelectors[_]
selector := input.review.object.spec.nodeSelector[blocked.key]
selector == blocked.value
msg := sprintf("Node selector <%v: %v> is not allowed", [blocked.key, blocked.value])
}
---
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiresecuritycontext
annotations:
description: "Requires pods to have secure security context"
compliance: "CIS Kubernetes Benchmark 5.2.6"
spec:
crd:
spec:
names:
kind: K8sRequireSecurityContext
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiresecuritycontext
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.runAsNonRoot
msg := sprintf("Container <%v> must set runAsNonRoot to true", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
container.securityContext.privileged
msg := sprintf("Container <%v> must not run as privileged", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.allowPrivilegeEscalation == false
msg := sprintf("Container <%v> must set allowPrivilegeEscalation to false", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.readOnlyRootFilesystem
msg := sprintf("Container <%v> must set readOnlyRootFilesystem to true", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
has_dangerous_capability(container)
msg := sprintf("Container <%v> must drop all capabilities and only add specific ones", [container.name])
}
has_dangerous_capability(container) {
not container.securityContext.capabilities.drop
}
has_dangerous_capability(container) {
not contains_all(container.securityContext.capabilities.drop)
}
contains_all(drops) {
drops[_] == "ALL"
}
---
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8singress alloweddomains
annotations:
description: "Restricts ingress hostnames to approved domains"
compliance: "Network Security Policy"
spec:
crd:
spec:
names:
kind: K8sIngressAllowedDomains
validation:
openAPIV3Schema:
type: object
properties:
domains:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8singressalloweddomains
violation[{"msg": msg}] {
input.review.kind.kind == "Ingress"
host := input.review.object.spec.rules[_].host
not domain_allowed(host, input.parameters.domains)
msg := sprintf("Ingress host <%v> is not in allowed domains: %v", [host, input.parameters.domains])
}
domain_allowed(host, domains) {
domain := domains[_]
endswith(host, domain)
}
Applying Constraints to Enforce Policies
Now let’s create the constraint instances that enforce these policies:
# constraints.yaml - Policy Enforcement
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: require-standard-labels
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
- apiGroups: ["apps"]
kinds: ["Deployment", "StatefulSet", "DaemonSet"]
excludedNamespaces:
- kube-system
- kube-public
- kube-node-lease
- gatekeeper-system
parameters:
message: "All resources must have required labels"
labels:
- key: "app.kubernetes.io/name"
allowedRegex: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"
- key: "app.kubernetes.io/instance"
allowedRegex: ".*"
- key: "app.kubernetes.io/version"
allowedRegex: ".*"
- key: "app.kubernetes.io/component"
allowedRegex: ".*"
- key: "app.kubernetes.io/part-of"
allowedRegex: ".*"
- key: "app.kubernetes.io/managed-by"
allowedRegex: ".*"
- key: "environment"
allowedRegex: "^(development|staging|production)$"
- key: "owner"
allowedRegex: "^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$"
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sContainerLimits
metadata:
name: require-container-limits
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
- apiGroups: ["apps"]
kinds: ["Deployment", "StatefulSet", "DaemonSet"]
namespaceSelector:
matchExpressions:
- key: environment
operator: In
values: ["production", "staging"]
parameters:
enforceLimitsEqualsRequests: true
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
name: allowed-image-repos
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
- apiGroups: ["apps"]
kinds: ["Deployment", "StatefulSet", "DaemonSet"]
excludedNamespaces:
- kube-system
- gatekeeper-system
parameters:
repos:
- "gcr.io/company-project/"
- "docker.io/company/"
- "registry.company.com/"
- "quay.io/company/"
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequireSecurityContext
metadata:
name: require-security-context
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
- apiGroups: ["apps"]
kinds: ["Deployment", "StatefulSet", "DaemonSet"]
namespaceSelector:
matchExpressions:
- key: pod-security.kubernetes.io/enforce
operator: Exists
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sIngressAllowedDomains
metadata:
name: ingress-allowed-domains
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: ["networking.k8s.io"]
kinds: ["Ingress"]
parameters:
domains:
- ".company.com"
- ".company.io"
- ".company-internal.net"
Mutation Policies
Automatic Resource Modification
Gatekeeper’s mutation feature allows automatic modification of resources:
# mutations.yaml - Mutation Policies
---
apiVersion: mutations.gatekeeper.sh/v1beta1
kind: Assign
metadata:
name: add-security-context
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
match:
scope: Namespaced
kinds:
- apiGroups: ["*"]
kinds: ["Pod"]
excludedNamespaces:
- kube-system
location: "spec.securityContext"
parameters:
assign:
value:
runAsNonRoot: true
fsGroup: 65534
seccompProfile:
type: RuntimeDefault
---
apiVersion: mutations.gatekeeper.sh/v1beta1
kind: Assign
metadata:
name: add-container-security-context
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
match:
scope: Namespaced
kinds:
- apiGroups: ["*"]
kinds: ["Pod"]
excludedNamespaces:
- kube-system
location: "spec.containers[name: *].securityContext"
parameters:
assign:
value:
allowPrivilegeEscalation: false
runAsNonRoot: true
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
---
apiVersion: mutations.gatekeeper.sh/v1beta1
kind: Assign
metadata:
name: add-resource-limits
spec:
applyTo:
- groups: ["apps"]
kinds: ["Deployment"]
versions: ["v1"]
match:
scope: Namespaced
namespaceSelector:
matchExpressions:
- key: mutation.gatekeeper.sh/add-limits
operator: Exists
location: "spec.template.spec.containers[name: *].resources"
parameters:
assign:
value:
limits:
cpu: "1000m"
memory: "1Gi"
requests:
cpu: "100m"
memory: "128Mi"
pathTests:
- subPath: "spec.template.spec.containers[name: *].resources"
condition: MustNotExist
---
apiVersion: mutations.gatekeeper.sh/v1beta1
kind: AssignMetadata
metadata:
name: add-standard-labels
spec:
match:
scope: Namespaced
kinds:
- apiGroups: ["*"]
kinds: ["Deployment", "StatefulSet", "DaemonSet"]
namespaceSelector:
matchExpressions:
- key: mutation.gatekeeper.sh/add-labels
operator: Exists
location: "metadata.labels"
parameters:
assign:
value:
app.kubernetes.io/managed-by: "gatekeeper"
---
apiVersion: mutations.gatekeeper.sh/v1beta1
kind: ModifySet
metadata:
name: add-image-pull-secrets
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
match:
scope: Namespaced
kinds:
- apiGroups: ["*"]
kinds: ["Pod"]
namespaceSelector:
matchLabels:
require-image-pull-secret: "true"
location: "spec.imagePullSecrets"
parameters:
operation: merge
values:
fromList:
- name: registry-credentials
Compliance and Audit Framework
Continuous Compliance Monitoring
Implement comprehensive audit and compliance monitoring:
# audit-config.yaml - Audit Configuration
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: "apps"
version: "v1"
kind: "Deployment"
- group: "apps"
version: "v1"
kind: "StatefulSet"
- group: "apps"
version: "v1"
kind: "DaemonSet"
- group: "networking.k8s.io"
version: "v1"
kind: "Ingress"
- group: "networking.k8s.io"
version: "v1"
kind: "NetworkPolicy"
validation:
traces:
- user: "system:serviceaccount:gatekeeper-system:gatekeeper-admin"
kind:
group: ""
version: "v1"
kind: "Pod"
dump: "All"
match:
- excludedNamespaces:
- kube-system
- kube-public
- kube-node-lease
processes:
- "audit"
- "webhook"
Compliance Reporting Dashboard
Create a compliance monitoring solution:
// compliance-reporter.go
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
)
type ComplianceReport struct {
Timestamp time.Time `json:"timestamp"`
TotalConstraints int `json:"total_constraints"`
ViolationsByPolicy map[string]int `json:"violations_by_policy"`
ViolationsByNS map[string]int `json:"violations_by_namespace"`
TopViolators []ResourceViolation `json:"top_violators"`
ComplianceScore float64 `json:"compliance_score"`
}
type ResourceViolation struct {
Resource string `json:"resource"`
Namespace string `json:"namespace"`
Constraint string `json:"constraint"`
Message string `json:"message"`
}
type ComplianceReporter struct {
dynamicClient dynamic.Interface
}
func NewComplianceReporter() (*ComplianceReporter, error) {
config, err := rest.InClusterConfig()
if err != nil {
return nil, err
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
return nil, err
}
return &ComplianceReporter{
dynamicClient: dynamicClient,
}, nil
}
func (cr *ComplianceReporter) GenerateReport() (*ComplianceReport, error) {
report := &ComplianceReport{
Timestamp: time.Now(),
ViolationsByPolicy: make(map[string]int),
ViolationsByNS: make(map[string]int),
TopViolators: []ResourceViolation{},
}
// List all constraint types
constraintTemplateGVR := schema.GroupVersionResource{
Group: "templates.gatekeeper.sh",
Version: "v1",
Resource: "constrainttemplates",
}
templates, err := cr.dynamicClient.Resource(constraintTemplateGVR).List(
context.TODO(),
metav1.ListOptions{},
)
if err != nil {
return nil, fmt.Errorf("failed to list constraint templates: %w", err)
}
report.TotalConstraints = len(templates.Items)
// Check violations for each constraint type
totalResources := 0
totalViolations := 0
for _, template := range templates.Items {
kind := template.GetName()
constraintGVR := schema.GroupVersionResource{
Group: "constraints.gatekeeper.sh",
Version: "v1beta1",
Resource: kind,
}
constraints, err := cr.dynamicClient.Resource(constraintGVR).List(
context.TODO(),
metav1.ListOptions{},
)
if err != nil {
log.Printf("Failed to list constraints for %s: %v", kind, err)
continue
}
for _, constraint := range constraints.Items {
violations, err := cr.getViolations(&constraint)
if err != nil {
log.Printf("Failed to get violations: %v", err)
continue
}
constraintName := constraint.GetName()
report.ViolationsByPolicy[constraintName] = len(violations)
totalViolations += len(violations)
for _, violation := range violations {
report.ViolationsByNS[violation.Namespace]++
report.TopViolators = append(report.TopViolators, violation)
}
totalResources += cr.getTotalAuditedResources(&constraint)
}
}
// Calculate compliance score
if totalResources > 0 {
report.ComplianceScore = float64(totalResources-totalViolations) / float64(totalResources) * 100
}
// Limit top violators to 20
if len(report.TopViolators) > 20 {
report.TopViolators = report.TopViolators[:20]
}
return report, nil
}
func (cr *ComplianceReporter) getViolations(constraint *unstructured.Unstructured) ([]ResourceViolation, error) {
violations := []ResourceViolation{}
status, found, err := unstructured.NestedSlice(constraint.Object, "status", "violations")
if err != nil || !found {
return violations, nil
}
for _, v := range status {
violation, ok := v.(map[string]interface{})
if !ok {
continue
}
rv := ResourceViolation{
Constraint: constraint.GetName(),
}
if kind, found := violation["kind"].(string); found {
rv.Resource = kind
}
if name, found := violation["name"].(string); found {
rv.Resource += "/" + name
}
if ns, found := violation["namespace"].(string); found {
rv.Namespace = ns
}
if msg, found := violation["message"].(string); found {
rv.Message = msg
}
violations = append(violations, rv)
}
return violations, nil
}
func (cr *ComplianceReporter) getTotalAuditedResources(constraint *unstructured.Unstructured) int {
total, found, err := unstructured.NestedInt64(constraint.Object, "status", "totalViolations")
if err != nil || !found {
return 0
}
return int(total)
}
func (cr *ComplianceReporter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
report, err := cr.GenerateReport()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(report)
}
func main() {
reporter, err := NewComplianceReporter()
if err != nil {
log.Fatalf("Failed to create compliance reporter: %v", err)
}
http.HandleFunc("/compliance", reporter.ServeHTTP)
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
log.Println("Starting compliance reporter on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("Server failed: %v", err)
}
}
Performance Optimization
Constraint Performance Tuning
Optimize Gatekeeper for high-traffic environments:
# performance-config.yaml
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
name: config
namespace: gatekeeper-system
spec:
# Optimize sync cache
sync:
syncOnly:
- group: ""
version: "v1"
kind: "Namespace"
- group: ""
version: "v1"
kind: "Pod"
- group: "apps"
version: "v1"
kind: "Deployment"
# Enable constraint caching
validation:
traces: []
# Configure audit performance
match:
- excludedNamespaces:
- kube-system
- kube-public
- kube-node-lease
processes:
- "audit"
- "webhook"
# Readiness tracking
readiness:
statsEnabled: true
Monitoring and Alerting
Prometheus Metrics Integration
# servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: gatekeeper
namespace: gatekeeper-system
spec:
selector:
matchLabels:
app: gatekeeper
endpoints:
- port: metrics
interval: 30s
path: /metrics
---
# PrometheusRule for alerts
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: gatekeeper-alerts
namespace: gatekeeper-system
spec:
groups:
- name: gatekeeper
interval: 30s
rules:
- alert: GatekeeperHighViolationRate
expr: rate(gatekeeper_violations_total[5m]) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "High policy violation rate"
description: "Gatekeeper is detecting {{ $value }} violations per second"
- alert: GatekeeperAuditFailure
expr: gatekeeper_audit_last_run_time < (time() - 300)
for: 5m
labels:
severity: critical
annotations:
summary: "Gatekeeper audit not running"
description: "Gatekeeper audit has not run in 5 minutes"
- alert: GatekeeperWebhookLatency
expr: histogram_quantile(0.99, rate(gatekeeper_validation_request_duration_seconds_bucket[5m])) > 1
for: 5m
labels:
severity: warning
annotations:
summary: "High webhook latency"
description: "99th percentile webhook latency is {{ $value }}s"
Best Practices
Policy Development Lifecycle
- Development: Test constraints in a development cluster with
enforcementAction: dryrun - Testing: Deploy to staging with
enforcementAction: warnand monitor violations - Production: Gradually roll out with
enforcementAction: denyafter validation - Maintenance: Regularly review and update policies based on new requirements
Performance Considerations
- Keep Rego policies simple and efficient
- Use namespace selectors to limit scope
- Enable caching for frequently evaluated resources
- Monitor webhook latency and adjust replica count
- Use exemptions sparingly and document thoroughly
Multi-Cluster Management
- Use GitOps to manage policies across clusters
- Implement cluster-specific overrides via Kustomize
- Centralize compliance reporting across clusters
- Maintain consistent policy versions across environments
Conclusion
OPA Gatekeeper provides enterprise-grade policy enforcement for Kubernetes with flexibility and power that scales to the largest deployments. By implementing comprehensive constraint templates, mutation policies, and continuous compliance monitoring, organizations can enforce security, compliance, and operational standards across their entire container infrastructure.
The key to success is starting with core security policies, gradually expanding coverage based on your specific compliance requirements, and continuously monitoring and tuning performance to maintain low latency in admission control.