CoursesPolicy-as-code at scaleDeny-by-default patterns that scale

Deny-by-default patterns that scale

Partial sets, helpers, and reusable libraries.

In short. At scale, the only policy posture that survives contact with production is deny-by-default for the controls you care about: privileged pods, public exposures, unapproved registries, missing labels.

Advanced30 min · lesson 4 of 13

At scale, the only policy posture that survives contact with production is deny-by-default for the controls you care about: privileged pods, public exposures, unapproved registries, missing labels. Allow lists beat endless deny special cases. Partial sets in Rego (deny contains msg if { ... }) collect every violation message instead of short-circuiting on the first.

Copy-pasting the same startswith image check into twenty templates is how drift is born. Build a small helper library package and import it. Keep messages actionable: what failed and what good looks like.

Partial sets and helpers

lib/pod.rego (sketch)
package lib.pod
is_privileged(container) if {
container.securityContext.privileged == true
}
unapproved_image(image, registry) if {
not startswith(image, registry)
}
terminal
printf '%s\n' 'package kubernetes.admission' 'import data.lib.pod' 'deny contains msg if {' ' some c in input.spec.containers' ' lib.pod.is_privileged(c)' ' msg := sprintf("privileged container %v", [c.name])' '}' > /tmp/priv.rego
echo '{"spec":{"containers":[{"name":"x","securityContext":{"privileged":true}}]}}' \
| opa eval -f raw -I -d /tmp/priv.rego -d /tmp/lib 'count(data.kubernetes.admission.deny)'
output
1
Library-centered policy
1lib.* helpers
pure predicates
2admission deny
composes helpers
3tests
fixture per helper + entry
4bundle
ship versioned
When a helper changes, tests should fail loudly before any cluster sees it.

Message quality

deny without context trains developers to bypass. Include the field, the forbidden value, and the allowed pattern. Prefer one clear message per violation object so UIs and CI logs stay readable.

Not every rule should fail closed on day one
Use audit/warn modes while measuring false positives — but do not live in warn forever. Schedule enforcement.

Going deeper

Prefer allowlists for registries, ingress classes, and privileged capabilities. Denylist catalogs go stale the day a new dangerous API lands. When you must denylist, pair with an allowlist default for the same field.

Structured errors beat strings alone. Some engines support denial reason objects — use them so dashboards can group by rule ID. Stable rule IDs also make exceptions precise.

Review helpers like any shared library: breaking change = major bundle bump. Semantic version your policy libraries and note breaking changes in CHANGELOG for app teams.

Keep deny messages under a readable length and free of internal jargon. Link to an internal doc URL for the standard. The message is part of the developer experience of your platform.

Fuzz helpers with unexpected types (string where you expected object). Rego's undefined behavior is a common source of silent allows — tests must include malformed fixtures.

Share helper packages across Gatekeeper templates and Conftest by keeping Rego in one repo path imported both ways. Duplicating helpers into ConstraintTemplate strings guarantees drift within a quarter.

Benchmark pathological inputs occasionally — a crafted Pod with thousands of containers can turn a careless comprehension into an admission timeout. Set engine resource limits and write policies that short-circuit.

Try this

Refactor a duplicated image-registry check into a helper and prove both a bad and good Pod fixture.

terminal
opa test ./policy -v
output
PASS: 6/6

Takeaway

Deny-by-default with partial sets, shared helpers, and actionable messages is how Rego stays maintainable past a handful of rules.

Next: choose Kyverno vs Gatekeeper with eyes open.

Quick check
01deny contains msg if { … } builds…
Incorrect — Partial sets collect multiple messages.
Correct —
Incorrect — CRDs are engine-side.
Incorrect — Wrong ecosystem.
02Helper libraries help primarily by…
Incorrect — No.
Correct —
Incorrect — No.
Incorrect — Unrelated.

Related