Kyverno: Mutate, Validate, Generate
Policy-as-Code ohne Rego für den Alltag.
von PlatPrep
Kyverno kennt fast jeder als Validation-Tool. Eine Policy, die verhindert, dass Images ohne Tag deployed werden. Eine Policy, die latest-Tags verbietet. Das ist der Einstieg — und für viele bleibt es dabei.
Aber Kyverno kann mutieren und generieren, und da wird es wirklich interessant. Technisch ist Kyverno ein Admission-Controller: Es hängt sich als Webhook in den API-Server und sieht jede Ressource, bevor sie gespeichert wird. Policies schreibst du als ganz normale Kubernetes-YAML mit match-Selektoren — kein Rego, keine eigene Sprache. Das ist der Hauptgrund, warum Teams Kyverno oft OPA Gatekeeper vorziehen: Die Person, die die Policy reviewed, muss keine neue Sprache lernen.
Mutate: Defaults, die niemand vergessen kann
Mutate heißt: Eingehende Ressourcen werden verändert, bevor sie im Cluster landen. Das Paradebeispiel ist der Security-Context — statt jedem Team zu erklären, was runAsNonRoot bedeutet, setzt du es einfach, wenn es fehlt:
mutate:
patchStrategicMerge:
spec:
template:
spec:
securityContext:
+(runAsNonRoot): true
+(seccompProfile):
type: RuntimeDefault
Das +()-Präfix ist Kyvernos “add if not present”-Anchor: Der Wert wird nur gesetzt, wenn das Team nichts Eigenes definiert hat. Teams können also bewusst abweichen, aber der Default ist sicher. patchStrategicMerge liest sich dabei wie das Zielobjekt selbst — deutlich wartbarer als JSON-Patch mit seinen Pfad-Operationen, das du nur für Fälle brauchst, die Merge nicht ausdrücken kann (etwa Einfügen an einer bestimmten Listenposition).
Dasselbe Muster funktioniert für Resource-Defaults, ImagePullSecrets, Tolerations oder das automatische Setzen von Team-Labels aus dem Namespace. Die Gefahr dabei ist Intransparenz: Wenn ein Deployment im Cluster anders aussieht als in Git, irritiert das GitOps-Diffs und Entwickler gleichermaßen. Argo CD braucht dann passende ignoreDifferences, und die Faustregel lautet: mutieren nur, was Plattform-Belang ist — nie etwas, das Teams bewusst steuern sollen.
Generate: Namespaces, die sich selbst konfigurieren
Generate ist das mächtigste und am wenigsten bekannte Feature. Wenn ein Namespace erstellt wird, erzeugt Kyverno automatisch die Begleit-Ressourcen: Default-Deny-NetworkPolicy, ResourceQuota, LimitRange, RoleBindings. Jeder neue Team-Namespace ist damit ab Sekunde eins korrekt konfiguriert — das ist der Unterschied zwischen “wir haben eine Checkliste für neue Namespaces” und “neue Namespaces konfigurieren sich selbst”. Genau dieses Pattern ist das Rückgrat sauberer Namespace-Isolation.
Der entscheidende Schalter dabei ist synchronize: true: Kyverno legt die generierte Ressource nicht nur einmal an, sondern bewacht sie. Löscht ein Team seine NetworkPolicy (“die hat beim Debuggen gestört”), stellt Kyverno sie wieder her. Ohne Synchronize ist Generate nur ein Template-Mechanismus; mit Synchronize ist es Enforcement.
Einführung ohne Flurschaden
Der Fehler, den fast jeder einmal macht: eine Validate-Policy direkt im Enforce-Modus auf einen Bestandscluster loslassen. Ergebnis: blockierte Deployments quer durch alle Teams, und das Vertrauen in die Plattform ist erst mal beschädigt.
Der zweite Klassiker: Ausnahmen landen als wachsende exclude-Listen direkt in der Policy, bis niemand mehr weiß, warum Namespace legacy-batch-7 ausgenommen ist. Dafür gibt es mit PolicyException eine eigene Ressource — die Ausnahme lebt als separates, reviewbares Objekt mit klarem Bezug (“diese eine Policy, dieser eine Workload”) und kann im GitOps-Repo des betroffenen Teams liegen, inklusive Begründung im Commit. Das macht aus Wildwuchs einen auditierbaren Prozess.
Der bessere Weg für die Einführung selbst führt über den Audit-Modus. Kyverno prüft dann, blockiert aber nicht, und schreibt die Ergebnisse als PolicyReport-Ressourcen in die jeweiligen Namespaces — auch für bereits existierende Objekte, dank Background-Scans. Damit hast du eine Liste: Welche Workloads würden gegen die geplante Policy verstoßen? Diese Liste arbeitest du mit den Teams ab (oder definierst begründete exclude-Ausnahmen), und erst wenn die Reports sauber sind, schaltest du auf Enforce. Das dauert je nach Clustergröße Wochen — und das ist okay.
Zwei Betriebsthemen gehören in jede ernsthafte Antwort zum Thema:
failurePolicy ist eine Verfügbarkeitsentscheidung. Steht der Webhook auf Fail und Kyverno ist down, nimmt der API-Server keine passenden Ressourcen mehr an — im schlimmsten Fall blockiert das auch Pods, die Kyverno selbst wieder hochbringen würden. Ignore hingegen heißt: Bei Kyverno-Ausfall greifen keine Policies. Für Security-kritische Validierungen willst du Fail plus hochverfügbares Kyverno (mehrere Replicas, PDB, Ressourcen-Reserven); für Convenience-Mutations reicht Ignore.
Webhook-Latenz ist real. Jede matchende Admission-Anfrage wartet auf Kyverno. Mit engen match-Selektoren (statt “alle Ressourcen, überall”) hältst du die Last klein und verhinderst, dass ein Policy-Engine-Problem das ganze Cluster-API-Erlebnis verlangsamt.
Wer im Interview über Kyverno spricht und neben Validate auch Generate mit Synchronize, den Audit-zuerst-Rollout und die failurePolicy-Abwägung nennt, zeigt Betriebserfahrung statt Feature-Wissen. Vertiefen kannst du das im Kyverno-Lernpfad.