Policy Cookbook: acting on AIBOM facts
k8s-aibom emits facts, never judgments — that boundary is a design rule. This cookbook shows where the judgments go: policy engines that consume the facts. Every example below is audit/advisory mode; enforce only after you’ve watched the audit results in your own clusters.
1. “AI runtimes must be inventoried” (Kyverno)
Namespaces running recognizable AI-serving images should carry the opt-in label — otherwise those workloads are invisible to inventory. This audit surfaces the gap without touching the workload:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: ai-workloads-should-be-inventoried
spec:
validationFailureAction: Audit
background: true
rules:
- name: known-ai-runtime-needs-optin
match:
any:
- resources:
kinds: [Deployment, StatefulSet]
preconditions:
all:
# Matches the common serving-runtime image families the
# controller detects; extend to taste.
- key: ""
operator: GreaterThan
value: 0
context:
- name: nsLabels
apiCall:
urlPath: "/api/v1/namespaces/"
jmesPath: "metadata.labels"
validate:
message: >-
Namespace runs an AI serving runtime
but is not opted in to runtime inventory. Label it:
kubectl label ns aibom.k8saibom.dev/enabled=true
deny:
conditions:
all:
- key: ""
operator: NotEquals
value: "true"
2. “Production models should be verified” (Kyverno, over AIBOM CRs)
With spec.verification enabled (v1.5.0+), each model summary carries signed: unsigned|claimed|verified. Audit AIBOMs in production namespaces whose models aren’t verified:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: prod-models-should-be-verified
spec:
validationFailureAction: Audit
background: true
rules:
- name: unverified-model-in-prod
match:
any:
- resources:
kinds: [AIBOM]
namespaceSelector:
matchLabels:
environment: production
validate:
message: >-
serves a model whose
signature is not verified (see status.summary.models[].signed
and the signature.* properties in the BOM document).
deny:
conditions:
any:
- key: ""
operator: GreaterThan
value: 0
Advisory by design: an unverified model may be perfectly legitimate (unsigned upstream weights). The policy’s job is to make that a conscious, visible state — not to block serving.
3. Gatekeeper variant of recipe 1
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: aibomoptinrequired
spec:
crd:
spec:
names:
kind: AIBOMOptInRequired
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package aibomoptin
ai_image(img) {
patterns := ["vllm", "triton", "text-generation-inference", "nvcr.io/nim/"]
some p
contains(img, patterns[p])
}
# A missing label (or no labels at all) is undefined in Rego,
# so "label != true" would never fire for a namespace that was
# never opted in. Test for opt-in and negate it instead.
opted_in(ns) {
ns.metadata.labels["aibom.k8saibom.dev/enabled"] == "true"
}
violation[{"msg": msg}] {
some c
ai_image(input.review.object.spec.template.spec.containers[c].image)
ns := data.inventory.cluster.v1.Namespace[input.review.object.metadata.namespace]
not opted_in(ns)
msg := sprintf("namespace %v runs an AI runtime without inventory opt-in", [input.review.object.metadata.namespace])
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: AIBOMOptInRequired
metadata:
name: ai-workloads-should-be-inventoried
spec:
enforcementAction: warn # audit posture; switch to deny only deliberately
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment", "StatefulSet"]
(Gatekeeper needs Namespace in its sync/replication config for the data.inventory lookup.)
Composition notes
- These compose with, and never replace, image-signature admission (sigstore policy-controller, Kyverno verifyImages): those gate container images at admission; k8s-aibom records model facts at runtime. Different subjects, complementary controls.
kubectl aibom summaryis the human-shaped view of the same facts these policies consume; use it to sanity-check a policy before enabling it.- Recipe images-pattern lists here are illustrative. The controller’s own detection allowlist (
v1-runtime-patterns.yaml, extensible viaspec.discovery) is the authoritative set; keep policies aligned with what you actually configure.