The Chaos of Unrestricted Kubernetes Clusters
Setting up your first Kubernetes cluster for a side project is a breeze. You trust your team, and everyone follows the ‘unwritten rules’ of deployment. But scale that to 50 namespaces and 200 microservices, and things get messy fast. I’ve seen clusters where a single rogue pod without resource limits caused a 60% performance drop for every other service on the node. In one instance, a developer accidentally ran a container as root, leaving a wide-open door for a potential exploit.
Relying on manual code reviews at this scale is a recipe for burnout. You can’t realistically inspect every YAML file for security compliance or cost-control labels. This ‘Wild West’ environment leads to configuration drift and security holes. Worse, it can trigger a surprise $2,500 cloud bill over a single weekend. Your infrastructure remains fragile because you lack an automated way to say ‘No’ to bad configurations before they reach the cluster.
The Gap: Why RBAC Isn’t Enough
Engineers often assume that Role-Based Access Control (RBAC) is the silver bullet for security. RBAC is essential, but it only handles identity. It answers: Who can do what? For example, RBAC allows a developer to create a Deployment in the ‘staging’ namespace. However, it never checks the content of that Deployment.
Standard RBAC won’t stop someone from creating a LoadBalancer that exposes a private database to the public internet. It won’t prevent the use of unapproved, vulnerable container registries either. Kubernetes lacks a native, fine-grained way to validate specific attributes inside a resource body. Policy as Code (PaC) fills this void. You need a system that acts as a digital building inspector, rejecting any request that doesn’t meet your organizational standards.
The Solution: Open Policy Agent (OPA) and Gatekeeper
Enter Open Policy Agent (OPA), a general-purpose policy engine using a declarative language called Rego. While OPA handles everything from CI/CD pipelines to SSH authorization, Gatekeeper is the Kubernetes-native version. It plugs directly into Kubernetes Admission Controllers to enforce rules in real-time.
Imagine a gatekeeper standing in front of your API server. When you run kubectl apply -f pod.yaml, Gatekeeper intercepts the request. It compares your YAML against your defined policies. If the pod violates a rule, Gatekeeper kills the request instantly with a clear error message. This happens before a single container starts, keeping your cluster in a verified ‘good’ state.
Key Components of Gatekeeper
- ConstraintTemplates: These define the logic of the policy using Rego. Think of this as the “blueprint” or the function definition.
- Constraints: These are instances of a template. They define exactly where the policy applies—like specific namespaces—and the values to check. Think of this as the “implementation.”
Tutorial: Your First Policy in Action
I’ve deployed this setup in high-traffic production environments, and the stability gains are immediate. These steps will help you install Gatekeeper and enforce a policy that requires all Namespaces to carry a specific label.
1. Installing Gatekeeper
Using Helm is the fastest route. Run these commands to get the controller running:
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm install gatekeeper gatekeeper/gatekeeper --namespace gatekeeper-system --create-namespace
Check that the pods in the gatekeeper-system namespace are healthy before proceeding.
2. Creating a ConstraintTemplate
We need a template that checks for labels. This Rego logic compares the labels on the incoming object against the list of required labels provided in the parameters.
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])
}
Apply the template: kubectl apply -f template.yaml.
3. Defining the Constraint
Now, let’s mandate that every Namespace must have a cost-center label. This ensures you can track cloud spend by department without chasing down developers later.
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: ns-must-have-cost-center
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
labels: ["cost-center"]
Apply this constraint. Gatekeeper is now guarding your cluster.
4. Testing the Enforcement
Try creating a namespace without the label:
kubectl create namespace test-violation
The API server will block you with an error:
Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request:
[ns-must-have-cost-center] Missing required labels: {"cost-center"}
Now, add the label and try again. The request will pass. This automated loop ensures compliance without manual intervention.
Strategic Implementation in Production
Don’t start blocking requests in production immediately. You will likely break CI/CD pipelines or automated scaling events. Instead, use a phased rollout.
Gatekeeper includes an enforcementAction: dryrun field. This lets you see which resources would have been blocked in the logs without actually stopping them. Audit your cluster first. Clean up non-compliant resources. Then, flip the switch to enforce mode once you’re confident.
Common production use cases include:
- Registry Whitelisting: Only allow images from
company.azurecr.io. - Mandatory Resource Limits: Block any pod missing CPU/Memory requests.
- Ingress Collision Prevention: Stop two teams from accidentally claiming the same hostname.
The Shift to Automated Safety
Policy as Code turns security from a checkbox at the end of a sprint into a real-time guardrail. Using OPA Gatekeeper empowers developers to move fast within safe boundaries. It shifts the burden of compliance from humans to the system. In a cloud-native world, that is the only way to maintain your sanity.

