Enforcing Kubernetes Pod Security Standards in AKS with OPA Gatekeeper
Step-by-step guide to implementing Open Policy Agent Gatekeeper in Azure Kubernetes Service for enforcing pod security standards, resource limits, and compliance policies.
Objective
Security in Kubernetes isn’t just about network policies and RBAC—it’s about preventing dangerous configurations before they hit your cluster. I’ve seen too many incidents caused by a single privileged pod or a container without resource limits bringing down an entire cluster.
In this hands-on POC, I’ll walk you through implementing OPA Gatekeeper in Azure Kubernetes Service to enforce Pod Security Standards. By the end, you’ll have admission control policies that:
- Deny privileged containers
- Enforce CPU/memory resource limits
- Restrict host networking and hostPath volumes
- Require specific labels and annotations
- Block containers running as root
This is the kind of proactive security that prevents 3am pages.
Why OPA Gatekeeper Over Built-in PSS?
Kubernetes has built-in Pod Security Standards (PSS), so why use Gatekeeper?
Built-in PSS limitations:
- Only three profiles (privileged, baseline, restricted)
- Limited customization
- No audit mode for gradual rollout
- Can’t create custom policies for business logic
Gatekeeper advantages:
- Custom policy creation using Rego language
- Dry-run mode for testing policies before enforcement
- Audit existing resources for compliance
- Template reusability across multiple constraints
- Integration with CI/CD for policy-as-code
When to use which:
- Small clusters, simple requirements → Built-in PSS
- Enterprise environments, custom policies, compliance → Gatekeeper
For this POC, we’re going with Gatekeeper because it gives you production-grade policy control.
Architecture: How Gatekeeper Works
graph LR
user[Developer
kubectl apply]
apiServer[Kubernetes
API Server]
webhook[Admission
Webhook]
gatekeeper[OPA Gatekeeper
Controller]
templates[Constraint
Templates]
constraints[Constraints
Policies]
etcd[(etcd
Cluster State)]
user -->|1. Submit Pod| apiServer
apiServer -->|2. Admission Request| webhook
webhook -->|3. Validate| gatekeeper
gatekeeper -->|4. Check Policy| templates
gatekeeper -->|5. Check Config| constraints
gatekeeper -->|6. Allow/Deny| webhook
webhook -->|7. Response| apiServer
apiServer -->|8. Store or Reject| etcd
apiServer -.->|9. Success/Error| user
style gatekeeper fill:#7fba00,color:#fff
style apiServer fill:#0078d4,color:#fff
style constraints fill:#ff8c00,color:#fff
Key components:
- ConstraintTemplates: Reusable policy definitions (written in Rego)
- Constraints: Instances of templates with specific parameters
- Admission Webhook: Intercepts API requests before they’re persisted
- OPA Engine: Evaluates policies against incoming requests
- Audit Controller: Periodically scans existing resources for violations
Prerequisites
Before starting, ensure you have:
# Azure CLI (logged in)
az --version
# kubectl configured for your AKS cluster
kubectl version --client
# Helm 3.x
helm version
# (Optional) Terraform for infrastructure-as-code
terraform --version
AKS cluster requirements:
- Kubernetes 1.24 or higher
- RBAC enabled
- At least 2 nodes (for Gatekeeper redundancy)
Step 1: Deploy AKS Cluster with Terraform
Let’s start with a production-ready AKS cluster configured for security.
# terraform/main.tf
resource "azurerm_resource_group" "aks" {
name = "rg-aks-gatekeeper-poc"
location = "eastus"
}
resource "azurerm_kubernetes_cluster" "aks" {
name = "aks-gatekeeper-poc"
location = azurerm_resource_group.aks.location
resource_group_name = azurerm_resource_group.aks.name
dns_prefix = "aks-gatekeeper"
kubernetes_version = "1.28.3"
default_node_pool {
name = "system"
node_count = 2
vm_size = "Standard_D4s_v5"
os_disk_size_gb = 128
enable_auto_scaling = true
min_count = 2
max_count = 4
upgrade_settings {
max_surge = "33%"
}
}
identity {
type = "SystemAssigned"
}
network_profile {
network_plugin = "azure"
network_policy = "azure"
load_balancer_sku = "standard"
}
oms_agent {
log_analytics_workspace_id = azurerm_log_analytics_workspace.aks.id
}
tags = {
Environment = "POC"
Purpose = "OPA-Gatekeeper"
}
}
resource "azurerm_log_analytics_workspace" "aks" {
name = "law-aks-gatekeeper"
location = azurerm_resource_group.aks.location
resource_group_name = azurerm_resource_group.aks.name
sku = "PerGB2018"
retention_in_days = 30
}
# Get credentials
resource "null_resource" "get_credentials" {
provisioner "local-exec" {
command = "az aks get-credentials --resource-group ${azurerm_resource_group.aks.name} --name ${azurerm_kubernetes_cluster.aks.name} --overwrite-existing"
}
depends_on = [azurerm_kubernetes_cluster.aks]
}
Deploy the infrastructure:
cd terraform
terraform init
terraform plan
terraform apply -auto-approve
# Verify cluster access
kubectl get nodes
Step 2: Install OPA Gatekeeper
Option A: Install via Helm (Recommended)
# Add Gatekeeper Helm repository
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update
# Create namespace
kubectl create namespace gatekeeper-system
# Install Gatekeeper with production settings
helm install gatekeeper gatekeeper/gatekeeper \
--namespace gatekeeper-system \
--set replicas=2 \
--set auditInterval=60 \
--set constraintViolationsLimit=100 \
--set auditFromCache=true \
--set enableExternalData=false \
--set validatingWebhookTimeoutSeconds=5 \
--set mutatingWebhookTimeoutSeconds=2
# Verify installation
kubectl get pods -n gatekeeper-system
# Expected output:
# NAME READY STATUS
# gatekeeper-audit-5c7d9f8b4d-x7k2m 1/1 Running
# gatekeeper-controller-manager-6d8b9c7f8d-9p4m5 1/1 Running
# gatekeeper-controller-manager-6d8b9c7f8d-t7p3k 1/1 Running
Option B: Install via Kubectl (Alternative)
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/v3.15.0/deploy/gatekeeper.yaml
# Verify
kubectl get crd | grep gatekeeper
kubectl get validatingwebhookconfigurations | grep gatekeeper
Verify Gatekeeper Components
# Check all CRDs are installed
kubectl get crd | grep constraints.gatekeeper.sh
kubectl get crd | grep templates.gatekeeper.sh
# Check webhook is registered
kubectl get validatingwebhookconfigurations gatekeeper-validating-webhook-configuration
# View Gatekeeper logs
kubectl logs -n gatekeeper-system -l control-plane=controller-manager --tail=50
Step 3: Create Constraint Templates
Constraint templates define the policy logic. Let’s create templates for our security requirements.
Template 1: Block Privileged Containers
# policies/templates/block-privileged-containers.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8spspprivilegedcontainer
annotations:
description: "Blocks containers with privileged security context"
spec:
crd:
spec:
names:
kind: K8sPSPPrivilegedContainer
validation:
openAPIV3Schema:
type: object
properties:
exemptImages:
description: "List of container images exempt from this policy"
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8spspprivilegedcontainer
violation[{"msg": msg, "details": {}}] {
c := input_containers[_]
c.securityContext.privileged
not is_exempt(c.image)
msg := sprintf("Privileged container is not allowed: %v", [c.name])
}
input_containers[c] {
c := input.review.object.spec.containers[_]
}
input_containers[c] {
c := input.review.object.spec.initContainers[_]
}
is_exempt(image) {
exempt_images := object.get(input.parameters, "exemptImages", [])
exempt_images[_] == image
}
Template 2: Enforce Resource Limits
# policies/templates/require-resource-limits.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequireresources
annotations:
description: "Requires all containers to have CPU and memory limits"
spec:
crd:
spec:
names:
kind: K8sRequireResources
validation:
openAPIV3Schema:
type: object
properties:
exemptNamespaces:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequireresources
violation[{"msg": msg}] {
container := input_containers[_]
not container.resources.limits.cpu
msg := sprintf("Container %v must have CPU limit", [container.name])
}
violation[{"msg": msg}] {
container := input_containers[_]
not container.resources.limits.memory
msg := sprintf("Container %v must have memory limit", [container.name])
}
violation[{"msg": msg}] {
container := input_containers[_]
not container.resources.requests.cpu
msg := sprintf("Container %v must have CPU request", [container.name])
}
violation[{"msg": msg}] {
container := input_containers[_]
not container.resources.requests.memory
msg := sprintf("Container %v must have memory request", [container.name])
}
input_containers[c] {
c := input.review.object.spec.containers[_]
not is_exempt_namespace
}
input_containers[c] {
c := input.review.object.spec.initContainers[_]
not is_exempt_namespace
}
is_exempt_namespace {
exempt := object.get(input.parameters, "exemptNamespaces", [])
exempt[_] == input.review.object.metadata.namespace
}
Template 3: Block Host Networking
# policies/templates/block-host-networking.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8spspblockhostnamespace
annotations:
description: "Blocks pods from using host networking"
spec:
crd:
spec:
names:
kind: K8sPSPBlockHostNamespace
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8spspblockhostnamespace
violation[{"msg": msg, "details": {}}] {
input.review.object.spec.hostNetwork
msg := "Using host network is not allowed"
}
violation[{"msg": msg, "details": {}}] {
input.review.object.spec.hostIPC
msg := "Using host IPC is not allowed"
}
violation[{"msg": msg, "details": {}}] {
input.review.object.spec.hostPID
msg := "Using host PID is not allowed"
}
Template 4: Restrict HostPath Volumes
# policies/templates/restrict-hostpath.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8spspallowedvolumes
annotations:
description: "Restricts hostPath volume usage"
spec:
crd:
spec:
names:
kind: K8sPSPAllowedVolumes
validation:
openAPIV3Schema:
type: object
properties:
allowedTypes:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8spspallowedvolumes
violation[{"msg": msg}] {
volume := input_volumes[_]
not allowed_volume_type(volume)
msg := sprintf("Volume type %v is not allowed", [volume_type(volume)])
}
input_volumes[v] {
v := input.review.object.spec.volumes[_]
}
allowed_volume_type(volume) {
allowed := object.get(input.parameters, "allowedTypes", [])
allowed[_] == volume_type(volume)
}
volume_type(volume) = "configMap" {
volume.configMap
}
volume_type(volume) = "secret" {
volume.secret
}
volume_type(volume) = "emptyDir" {
volume.emptyDir
}
volume_type(volume) = "persistentVolumeClaim" {
volume.persistentVolumeClaim
}
volume_type(volume) = "hostPath" {
volume.hostPath
}
Apply all templates:
kubectl apply -f policies/templates/
Step 4: Create Constraints (Policy Instances)
Now we instantiate the templates with specific configurations.
Constraint 1: Block Privileged Pods
# policies/constraints/block-privileged.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPPrivilegedContainer
metadata:
name: block-privileged-containers
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
excludedNamespaces:
- kube-system
- gatekeeper-system
parameters:
exemptImages:
- "mcr.microsoft.com/oss/kubernetes/pause:3.6"
Constraint 2: Require Resource Limits
# policies/constraints/require-resources.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequireResources
metadata:
name: require-container-resources
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
excludedNamespaces:
- kube-system
- gatekeeper-system
parameters:
exemptNamespaces:
- kube-system
Constraint 3: Block Host Networking
# policies/constraints/block-host-networking.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPBlockHostNamespace
metadata:
name: block-host-namespace
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
excludedNamespaces:
- kube-system
- calico-system
Constraint 4: Restrict HostPath
# policies/constraints/restrict-hostpath.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPAllowedVolumes
metadata:
name: restrict-volume-types
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
excludedNamespaces:
- kube-system
parameters:
allowedTypes:
- configMap
- secret
- emptyDir
- persistentVolumeClaim
Apply all constraints:
kubectl apply -f policies/constraints/
# Verify constraints are created
kubectl get constraints
# Check constraint status
kubectl describe k8spspprivilegedcontainer block-privileged-containers
Step 5: Validation Testing
Now comes the fun part—testing our policies.
Test 1: Try to Deploy Privileged Pod (Should Fail)
# tests/privileged-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: privileged-pod
namespace: default
spec:
containers:
- name: nginx
image: nginx:latest
securityContext:
privileged: true
kubectl apply -f tests/privileged-pod.yaml
# Expected output:
# Error from server (Forbidden): error when creating "tests/privileged-pod.yaml":
# admission webhook "validation.gatekeeper.sh" denied the request:
# [block-privileged-containers] Privileged container is not allowed: nginx
Policy working correctly!
Test 2: Deploy Compliant Pod (Should Succeed)
# tests/compliant-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: compliant-pod
namespace: default
spec:
containers:
- name: nginx
image: nginx:latest
securityContext:
privileged: false
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
kubectl apply -f tests/compliant-pod.yaml
# Expected output:
# pod/compliant-pod created
Compliant pod accepted!
Test 3: Pod Without Resource Limits (Should Fail)
# tests/no-limits-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: no-limits-pod
namespace: default
spec:
containers:
- name: nginx
image: nginx:latest
kubectl apply -f tests/no-limits-pod.yaml
# Expected output:
# Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request:
# [require-container-resources] Container nginx must have CPU limit
# [require-container-resources] Container nginx must have memory limit
Resource policy enforced!
Test 4: Pod with Host Networking (Should Fail)
# tests/host-network-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: host-network-pod
namespace: default
spec:
hostNetwork: true
containers:
- name: nginx
image: nginx:latest
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
kubectl apply -f tests/host-network-pod.yaml
# Expected output:
# Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request:
# [block-host-namespace] Using host network is not allowed
Host networking blocked!
Step 6: Audit Existing Resources
Gatekeeper can audit existing resources that violate policies.
# View audit results for all constraints
kubectl get constraints -o yaml | grep -A 10 violations
# Check specific constraint violations
kubectl get k8spspprivilegedcontainer block-privileged-containers -o jsonpath='{.status.violations}'
# View total violation count
kubectl get constraints --all-namespaces -o json | \
jq -r '.items[] | select(.status.totalViolations > 0) | "\(.metadata.name): \(.status.totalViolations) violations"'
Generate Audit Report
# Create script to generate audit report
cat <<'EOF' > audit-report.sh
#!/bin/bash
echo "=== Gatekeeper Audit Report ==="
echo "Generated at: $(date)"
echo ""
for constraint in $(kubectl get constraints -o name); do
name=$(echo $constraint | cut -d'/' -f2)
violations=$(kubectl get $constraint -o jsonpath='{.status.totalViolations}')
if [ "$violations" -gt 0 ]; then
echo "FAIL: $name: $violations violations"
kubectl get $constraint -o jsonpath='{range .status.violations[*]}{.message}{"\n"}{end}' | sed 's/^/ - /'
echo ""
else
echo "PASS: $name: 0 violations"
fi
done
EOF
chmod +x audit-report.sh
./audit-report.sh
Step 7: Dry-Run Mode (Testing Before Enforcement)
Before enforcing policies in production, test them in dry-run mode.
# policies/constraints/dry-run-example.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPPrivilegedContainer
metadata:
name: block-privileged-dryrun
spec:
enforcementAction: dryrun # Changed from "deny" to "dryrun"
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
# Apply dry-run constraint
kubectl apply -f policies/constraints/dry-run-example.yaml
# Deploy non-compliant pod (will succeed but log violation)
kubectl apply -f tests/privileged-pod.yaml
# Check audit logs for violations
kubectl logs -n gatekeeper-system -l control-plane=audit-controller | grep violation
Dry-run workflow:
- Deploy constraint with
enforcementAction: dryrun - Monitor audit logs for 1-2 weeks
- Fix violations in existing resources
- Change to
enforcementAction: deny - Deploy to production
Step 8: Integration with Prometheus and Alerting
Monitor Gatekeeper metrics and alert on policy violations.
Expose Gatekeeper Metrics
# monitoring/gatekeeper-servicemonitor.yaml
apiVersion: v1
kind: Service
metadata:
name: gatekeeper-metrics
namespace: gatekeeper-system
labels:
app: gatekeeper
spec:
ports:
- name: metrics
port: 8888
targetPort: 8888
selector:
control-plane: controller-manager
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: gatekeeper
namespace: gatekeeper-system
spec:
selector:
matchLabels:
app: gatekeeper
endpoints:
- port: metrics
interval: 30s
Prometheus Alerts
# monitoring/gatekeeper-alerts.yaml
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[5m]) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "High rate of policy violations"
description: "Gatekeeper is detecting {{ $value }} violations per second"
- alert: GatekeeperWebhookFailure
expr: |
rate(gatekeeper_validation_request_count{admission_status="error"}[5m]) > 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "Gatekeeper webhook failures detected"
description: "Admission webhook is failing at {{ $value }} requests per second"
Query Gatekeeper Metrics
# Total violations detected
sum(gatekeeper_violations)
# Violations by constraint
sum by (constraint_name) (gatekeeper_violations)
# Webhook latency (p95)
histogram_quantile(0.95, rate(gatekeeper_validation_request_duration_seconds_bucket[5m]))
# Admission request rate
rate(gatekeeper_validation_request_count[5m])
Results and Key Takeaways
What We Achieved
After implementing Gatekeeper in this POC:
100% policy enforcement: Zero privileged pods, zero missing resource limits in default namespace
Proactive security: Caught 12 policy violations during testing before they reached production
Audit visibility: Identified 23 existing non-compliant pods in kube-system (exempted post-review)
Developer feedback: Clear error messages help developers fix issues immediately
# Before Gatekeeper
kubectl get pods --all-namespaces -o json | \
jq '[.items[].spec.containers[] | select(.securityContext.privileged == true)] | length'
# Output: 8 privileged containers
# After Gatekeeper
# Output: 0 (all blocked or exempted)
Performance Impact
Webhook latency:
- p50: 12ms
- p95: 45ms
- p99: 120ms
Resource usage:
- Gatekeeper controller: ~200MB memory, ~100m CPU
- Audit controller: ~150MB memory, ~50m CPU
Impact on deployments: Negligible (sub-second addition to admission time).
Lessons Learned
1. Start with Audit, Not Enforcement
Mistake avoided: Deploying deny policies on day one would have broken existing workloads.
What worked:
- Week 1: Deploy templates + constraints in
dryrunmode - Week 2: Review audit logs, fix violations
- Week 3: Switch to
denymode for new resources - Week 4: Full enforcement
2. Exempt System Namespaces Carefully
Some system components require privileges that violate policies:
spec:
match:
excludedNamespaces:
- kube-system # Core components
- gatekeeper-system # Gatekeeper itself
- calico-system # CNI (if using Calico)
- monitoring # Prometheus node-exporter needs hostPath
Best practice: Exempt namespace-wide, then create specific policies for those namespaces if needed.
3. Provide Clear Error Messages
Compare these error messages:
# Bad (generic)
"Policy violation detected"
# Good (actionable)
"Container nginx must have CPU limit. Add resources.limits.cpu to your pod spec."
Developers appreciate helpful errors. We added links to documentation in our Rego code:
msg := sprintf("Container %v must have memory limit. See: https://wiki.company.com/pod-security", [container.name])
4. Test Policies in Non-Prod First
We discovered several edge cases during testing:
- Init containers need the same resource limits
- Jobs with
restartPolicy: OnFailureneed special handling - Third-party Helm charts often violate policies (requires upstream fixes)
Strategy: Roll out to dev → staging → production over 2-3 weeks.
5. Monitor Webhook Performance
We had one incident where Gatekeeper webhook latency spiked to 5 seconds, blocking all deployments.
Root cause: Rego policy had inefficient loop in audit controller.
Fix: Optimized Rego code, added webhook timeout configuration:
--set validatingWebhookTimeoutSeconds=5
Lesson: Always set reasonable timeouts. If Gatekeeper fails, the webhook should fail-open (allow) rather than block all deployments.
6. Policy-as-Code is Powerful
We integrated Gatekeeper policies into CI/CD:
# .github/workflows/policy-validation.yml
name: Validate Policies
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install gator CLI
run: |
curl -L https://github.com/open-policy-agent/gatekeeper/releases/download/v3.15.0/gator-v3.15.0-linux-amd64.tar.gz | tar xz
sudo mv gator /usr/local/bin/
- name: Validate Constraint Templates
run: gator verify policies/
- name: Test Policies Against Fixtures
run: |
gator test --filename=policies/ --filename=tests/
This catches policy errors before they hit the cluster.
Future Enhancements
1. Custom Business Logic Policies
# Require cost-center label on all namespaces
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: requirecostcenterlabel
spec:
# Rego policy requiring cost-center label
2. Integration with Azure Policy
Combine Gatekeeper with Azure Policy for defense-in-depth:
- Azure Policy: Infrastructure-level controls (network, storage)
- Gatekeeper: Application-level controls (pods, deployments)
3. Mutation Policies
Gatekeeper v3.13+ supports mutation (automatic fixes):
# Auto-add resource limits if missing
apiVersion: mutations.gatekeeper.sh/v1
kind: Assign
metadata:
name: add-default-limits
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
location: "spec.containers[name:*].resources.limits"
parameters:
assign:
value:
memory: "512Mi"
cpu: "500m"
4. External Data Integration
Query external systems during policy evaluation:
# Check if container image is approved in external registry
spec:
parameters:
registryURL: "https://registry.company.com/api/approved-images"
Conclusion
OPA Gatekeeper transforms Kubernetes admission control from a black box into a programmable, auditable policy engine. In this POC, we:
- Deployed Gatekeeper to AKS
- Created four production-ready policy templates
- Enforced pod security standards with clear error messages
- Integrated with Prometheus for monitoring
- Validated all policies in dry-run mode first
The result? Proactive security that prevents incidents before they happen, not reactive fixes after the damage is done.
If you’re running Kubernetes in production without admission policies, you’re essentially trusting every developer to be a security expert. Gatekeeper shifts that burden to automated policy enforcement—exactly where it belongs.
Quick Reference: Common Gatekeeper Commands
# List all constraint templates
kubectl get constrainttemplates
# List all constraints
kubectl get constraints
# Check constraint status
kubectl describe <constraint-kind> <constraint-name>
# View violations
kubectl get <constraint-kind> <constraint-name> -o jsonpath='{.status.violations}'
# Force audit run
kubectl delete pod -n gatekeeper-system -l control-plane=audit-controller
# Check webhook status
kubectl get validatingwebhookconfigurations gatekeeper-validating-webhook-configuration
# View Gatekeeper logs
kubectl logs -n gatekeeper-system -l control-plane=controller-manager --tail=100
# Test policy locally with gator CLI
gator test --filename=policies/ --filename=tests/
About StriveNimbus
StriveNimbus specializes in Kubernetes security, policy governance, and cloud-native architecture for Azure environments. We help organizations implement defense-in-depth security strategies that don’t slow down developer velocity.
Need help with Kubernetes security? Contact us for a security assessment and policy implementation roadmap.