Stage 5 · Platform
GitOps Operations
Policy as Code
Enforcing Kubernetes guardrails with OPA Gatekeeper, Kyverno, Conftest, and admission reports.
Why Policy as Code?
Policy as Code enforces organizational rules automatically. Instead of documenting policies in wikis and hoping developers follow them, policies are expressed as code and enforced at admission time. If a resource violates the policy, it is rejected before it reaches the cluster.
Policies cover security (no privileged containers), cost control (resource limits required), compliance (labels required), and operational standards (health checks required). They prevent misconfigurations before they cause incidents.
OPA Gatekeeper
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}] {
required := input.parameters.labels[_]
not input.review.object.metadata.labels[required]
msg := sprintf("Missing required label: %v", [required])
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: require-app-labels
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment", "StatefulSet"]
namespaces: ["production"]
parameters:
labels:
- "app"
- "team"
- "environment"
- "cost-center"Gatekeeper uses Rego (OPA) to define policies. The ConstraintTemplate defines the policy logic. The Constraint resource applies the policy to specific resources. This policy requires labels on all Deployments in production.
Kyverno
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
annotations:
policies.kyverno.io/title: Require Resource Limits
policies.kyverno.io/category: Best Practices
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
spec:
validationFailureAction: Enforce
background: true
rules:
- name: check-resource-limits
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Resource limits are required"
pattern:
spec:
containers:
- resources:
limits:
memory: "?*"
cpu: "?*"
requests:
memory: "?*"
cpu: "?*"
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-privileged
spec:
validationFailureAction: Enforce
rules:
- name: deny-privileged
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Privileged containers are not allowed"
pattern:
spec:
containers:
- =(securityContext):
=(privileged): falseKyverno uses YAML-based policies instead of Rego. The validate rule checks that resource limits are set on all containers. The second policy denies privileged containers. validationFailureAction: Enforce rejects violating resources.
Conftest for CI
jobs:
policy-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Conftest
run: |
curl -sSL https://github.com/open-policy-agent/conftest/releases/latest/download/conftest_Linux_x86_64.tar.gz | tar xz
sudo mv conftest /usr/local/bin/
- name: Generate manifests
run: |
kubectl kustomize overlays/production > manifests.yaml
- name: Run policy checks
run: |
conftest test manifests.yaml -p policies/ --all-namespaces
- name: Policy report
if: always()
run: |
conftest test manifests.yaml -p policies/ -o json > policy-report.json
conftest test manifests.yaml -p policies/ -o markdown > policy-report.mdConftest runs OPA policies against Kubernetes manifests in CI. This catches policy violations before they reach the cluster. The markdown output can be posted as a PR comment for visibility.
package main
deny[msg] {
input.kind == "Deployment"
not input.spec.template.spec.containers[0].resources.limits.cpu
msg := "Container must have CPU limits"
}
deny[msg] {
input.kind == "Deployment"
not input.spec.template.spec.containers[0].resources.limits.memory
msg := "Container must have memory limits"
}
deny[msg] {
input.kind == "Deployment"
not input.spec.template.spec.containers[0].readinessProbe
msg := "Container must have readiness probe"
}
deny[msg] {
input.kind == "Deployment"
input.spec.template.spec.containers[0].securityContext.privileged == true
msg := "Privileged containers are not allowed"
}Conftest policies use Rego. Each deny rule checks a specific requirement. The policy runs against every manifest in the pipeline. Violations are reported as errors with descriptive messages.
Admission Reports
apiVersion: kyverno.io/v1alpha2
kind: AdmissionReport
metadata:
name: myapp-report
namespace: production
spec:
summary:
pass: 8
fail: 2
warn: 1
error: 0
skip: 0
results:
- policy: require-resource-limits
rule: check-resource-limits
status: fail
message: "Resource limits are required"
resource:
kind: Deployment
name: myapp
namespace: production
- policy: disallow-privileged
rule: deny-privileged
status: pass
message: "Validation passed"
resource:
kind: Pod
name: myapp-pod
namespace: productionAdmissionReports aggregate policy check results for a resource. They show which policies passed, failed, or warned. Use them in CI to report policy compliance before deployment.
Common Policy Patterns
- Require labels — ensure all resources have team, cost-center, and environment labels.
- Require resource limits — prevent unbounded resource consumption.
- Deny privileged containers — enforce security baselines.
- Require health checks — ensure all pods have readiness and liveness probes.
- Restrict image registries — only allow images from trusted registries.
- Require annotations — enforce documentation and ownership metadata.
New policies should start in audit mode — they report violations but do not block deployments. Once the team fixes existing violations and understands the impact, switch to enforce mode. This prevents mass breakage.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.