Security ~4 Min.

OPA Gatekeeper in der Praxis

ConstraintTemplates, Audit-Mode und wann Rego sich lohnt.

von PlatPrep

Rego ist eine Programmiersprache. Das ist das Erste, was man akzeptieren muss, wenn man mit OPA Gatekeeper arbeitet. Nicht ein YAML-Format mit Schlüsselwörtern, nicht ein Annotation-System — eine eigene, logik-deklarative Sprache, die ein Umdenken erfordert.

Das macht den Einstieg schwerer als bei Kyverno. Aber es macht OPA auch mächtiger für komplexe Policy-Logik.

Template und Constraint: die Zwei-Schichten-Architektur

Eine ConstraintTemplate definiert zwei Dinge: den kind der neuen Constraint-Ressource (z.B. K8sRequiredLabels) und die Rego-Logik, die entscheidet, ob eine Ressource die Policy erfüllt. Gatekeeper erzeugt aus dem Template eine eigene CRD — deine Policies werden zu first-class Kubernetes-Objekten.

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("fehlende Labels: %v", [missing])
}

input.review.object ist die eingehende Ressource, input.parameters kommen aus der Constraint-Instanz. Die violation-Regel feuert, wenn alle Bedingungen im Body wahr sind — das ist die logik-deklarative Denkweise: du beschreibst, wann etwas falsch ist, nicht wie geprüft wird.

Eine zugehörige Constraint-Instanz:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-team-label
spec:
  enforcementAction: dryrun
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels: ["team", "environment"]

Die Trennung ist clever: das Template ist die wiederverwendbare Policy-Logik, die Constraint ist die Instanz mit Parametern und Scope. Das gleiche Template läuft in mehreren Constraints — etwa strenge Label-Pflichten in Produktions-Namespaces, lockere in Dev.

enforcementAction: der wichtigste Hebel beim Rollout

Das Feld enforcementAction kennt drei Werte, und die Reihenfolge, in der du sie nutzt, entscheidet über die Akzeptanz im Unternehmen:

dryrun blockiert nichts, sammelt aber Violations im Status der Constraint. warn lässt die Ressource durch, gibt dem User aber eine Warnung im kubectl-Output zurück — unterschätzt nützlich, weil Entwickler die Meldung direkt sehen. deny (der Default) blockiert hart.

Parallel prüft der Audit-Controller bestehende Ressourcen periodisch gegen alle Constraints (Standard-Intervall: 60 Sekunden) und schreibt Violations in den Constraint-Status. Der typische Rollout-Pfad: Policy in dryrun deployen, eine Woche Audit-Ergebnisse beobachten, betroffene Teams informieren, Altlasten bereinigen, dann auf deny schalten. Wer den Schritt überspringt und direkt blockiert, produziert den klassischen Incident: ein CI-Deployment schlägt freitags um 17 Uhr fehl, weil ein drei Jahre altes Deployment kein team-Label hat.

Ein Detail, das in Interviews gern abgefragt wird: Gatekeepers Admission-Webhook steht per Default auf failurePolicy: Ignore. Wenn die Gatekeeper-Pods nicht erreichbar sind, gehen Requests ungeprüft durch. Fail-open ist die richtige Default-Entscheidung für Verfügbarkeit — aber es bedeutet, dass deine Policies keine harte Sicherheitsgarantie sind, solange du nicht bewusst auf fail-closed umstellst und die Konsequenzen (Gatekeeper-Ausfall = Cluster-weiter Deployment-Stopp) trägst.

Referential Policies: wenn eine Ressource nicht reicht

Manche Policies brauchen Kontext über die geprüfte Ressource hinaus: “Kein Ingress darf einen Hostnamen verwenden, den ein anderer Ingress schon belegt.” Dafür repliziert Gatekeeper auf Wunsch Cluster-Ressourcen in den OPA-Cache — konfiguriert über eine Config-Ressource (oder neuere SyncSet-Objekte). In Rego greifst du dann über data.inventory auf den Cache zu.

Das ist mächtig, hat aber Kosten: jede replizierte Ressourcenart erhöht den Memory-Verbrauch der Gatekeeper-Pods. Wer alle Secrets in den Cache synct, hat außerdem ein Sicherheitsproblem geschaffen, kein gelöstes.

Testen, bevor es in den Cluster geht

Der eigentliche Grund, die Rego-Lernkurve zu akzeptieren: Policies sind voll testbar. opa test führt Unit-Tests gegen die Rego-Logik aus, und das gator-CLI testet komplette ConstraintTemplates samt Constraints gegen Beispiel-Manifeste — in CI, vor dem Merge:

gator verify ./policies/...

Das ist eine andere Qualität als manuelle YAML-Reviews. Eine Policy-Änderung durchläuft denselben Review-und-Test-Zyklus wie Applikationscode. Bei zwanzig Policies und fünf Teams, die Änderungen vorschlagen, ist das der Unterschied zwischen beherrschbar und chaotisch.

Seit einigen Versionen kann Gatekeeper übrigens auch mutieren — Assign, AssignMetadata und ModifySet sind eigene CRDs, die ohne Rego auskommen. Wer aber primär mutieren und generieren will, ist bei Kyverno meist schneller am Ziel.

Wann sich Rego lohnt — und wann nicht

Die ehrliche Abwägung: für “alle Pods brauchen Resource-Limits” und ähnliche Standard-Checks ist die Rego-Investition übertrieben; das löst Kyverno mit zehn Zeilen YAML. OPA spielt seine Stärke aus, wenn Policy-Logik echte Logik ist — Mengenoperationen, Cross-Resource-Checks, Ausnahmeregeln mit mehreren Bedingungen. Und wenn Policies über Kubernetes hinaus wiederverwendet werden sollen: OPA läuft auch in CI-Pipelines, API-Gateways und Terraform-Checks mit derselben Sprache.

Für große Organisationen mit komplexen Anforderungen ist Gatekeeper die mächtigste Option. Die Lernkurve ist real — aber Policy-as-Code mit Tests zahlt sich aus, sobald Policies geschäftskritisch werden. Die Grundlagen von Rego und OPA kannst du im OPA-Lernpfad strukturiert durcharbeiten.