5 Tipps zur Verwendung der Rego-Sprache für Open Policy Agent (OPA)
18. September 2020
0 Min. LesezeitAnmerkung der Redaktion
Dieser Blogbeitrag erschien ursprünglich auf fugue.co. Fugue wurde 2022 Teil von Snyk und ist ein wichtiger Bestandteil von Snyk IaC.
Bei Fugue sind wir große Fans von Open Policy Agent (OPA) und haben viel Rego-Code geschrieben, um Cloud-Ressourcen zu schützen. Deshalb haben wir die wertvollsten Erkenntnisse aus unserer Arbeit zusammengetragen. Mit OPA und der Rego-Sprache können Sie Policy as Code nutzen, um Richtlinien automatisch durchzusetzen.
1. Die Gesetze von De Morgan sind Ihre neuen besten Freunde
Wenn ich diese Liste auf einen einzigen Punkt beschränken müsste, wäre es dieser. Die Gesetze von De Morgan sind zwei Umformungsregeln. Auf Rego angewendet, ermöglichen sie Ihnen, eine Regel umzuformen und dabei ihr Verhalten exakt beizubehalten.
Sie werden üblicherweise so geschrieben:
Oder als Code:
Ein Beispiel macht es vielleicht verständlicher. Die folgenden beiden Aussagen sind gleichbedeutend und veranschaulichen die Anwendung des ersten Gesetzes:
Das ist keine Pizza mit Schinken und Pilzen.
Auf dieser Pizza ist kein Schinken oder es sind keine Pilze.
Mit ihnen können Sie UND-Verknüpfungen in ODER-Verknüpfungen umwandeln und umgekehrt, ohne das Verhalten des Codes zu verändern. Moment mal – könnten Sie jetzt fragen: Wenn diese Umformungen das Verhalten meines Codes nicht verändern, wozu sind sie dann gut?
Der entscheidende Punkt ist, dass Rego als Abfragesprache stark auf Disjunktionen (also ODER-Aussagen) ausgerichtet ist. Zum Beispiel lässt sich prüfen, ob jemand in der Gruppe qualifiziert ist, eine Pizza zu schneiden:
Wie prüfen wir aber, ob alle Personen in der Gruppe qualifiziert sind, Pizzen zu schneiden? Könnten wir, ähm, zuerst eine Gruppe mit den qualifizierten Personen erstellen und dann deren Anzahl überprüfen?
Das ist nicht ideal. Abgesehen davon, dass die Aussage schwer zu lesen ist, lässt sie sich nur schwer durch einen Abfrageplaner optimieren, da die Mengen gezählt werden müssen – obwohl wir bereits abbrechen könnten, sobald wir eine minderjährige Person in der Gruppe finden.
Hier kommen die Gesetze von De Morgan ins Spiel: Sie zeigen uns, dass wir Abfragen immer als Disjunktionen schreiben können, indem wir einige Teile negieren. Dazu führen wir einfach eine Hilfsaussage ein, die die ursprüngliche Aussage negiert:
Dann ist es einfach, die Richtlinie zu schreiben:
Und fertig! Effizient und leicht verständlich.
Fazit: Wenn eine Rego-Richtlinie schwer zu schreiben scheint, prüfen Sie immer, ob sich die negierte Richtlinie leichter formulieren lässt, und negieren Sie sie anschließend wieder! Logik ist toll!
2. or oder any?
Wie wir gerade gesehen haben, lässt sich Rego leichter lesen und schreiben, wenn die ODER-Aussagen ganz oben stehen und die UND-Aussagen ordentlich nacheinander in den Regelkörpern angeordnet sind. Doch als Programmiererinnen und Programmierer stoßen wir meist schnell auf einen Fall, in dem die idiomatische Vorgehensweise nicht gut funktioniert.
In Rego kommt das häufig vor, wenn ein Regelkörper bereits etliche Abfragen enthält, zum Beispiel:
Was aber, wenn wir auch Pizzastücke zulassen möchten? Wir könnten den gesamten Regelkörper duplizieren:
Das ist nicht ideal. In den meisten Fällen können wir eine Hilfsregel einführen:
Das funktioniert in diesem Fall gut. Manchmal haben wir jedoch so viele Hilfsregeln, dass es schwierig wird, überhaupt passende Namen dafür zu finden! In den seltenen Fällen können wir einfach so tun, als gäbe es in Rego eine ODER-Anweisung namens any:
So lässt sich das gut lesbar schreiben. In Maßen verwendet, kann jede beliebige Option Ihnen das Leben erheblich erleichtern.
3. Comprehensions mit any und all
Bleiben wir bei diesem Ansatz: any und all sind auch an vielen anderen Stellen äußerst nützlich, insbesondere bei List-Comprehensions. Diese beiden Funktionen ergänzen sich so gut, dass das Ganze mehr ist als die Summe seiner Teile.
Kehren wir zum ersten Beispiel zurück: So können wir all zusammen mit einer List-Comprehension verwenden, um dieselbe Richtlinie auszudrücken:
Ich finde, diese Variante ist genauso gut lesbar wie die Version, die wir mithilfe der Gesetze von De Morgan erstellt haben. Welche Variante idiomatischer ist, hängt meist vom Kontext ab.
Lässt sich die negierte Richtlinie leichter nachvollziehen? Dann verwenden wir die Umformung nach De Morgan.
Oder ist es einfacher, die Dinge, die wir überprüfen wollen, als Liste oder Sammlung zu betrachten? Dann ist eine List-Comprehension passend.
4. Mit walk ganze Dokumentbäume durchsuchen
Rego-Abfragen werden immer beendet – das ist gut, hat aber seinen Preis: Rekursive Regeln lassen sich im Allgemeinen nicht schreiben.
Warum brauchen wir rekursive Regeln? Sie sind sehr nützlich, wenn es um rekursive Eingabedokumente geht, etwa tief verschachtelte Bäume. Zum Glück bietet OPA einige integrierte Funktionen, die dabei helfen.
Zum Beispiel könnten wir eine Richtlinie schreiben, um Diskussionsthreads zu überwachen:
Wie rufen wir die Nachrichten in Rego ab? Wir könnten es mit Folgendem versuchen:
Bei endlich vielen Verschachtelungsebenen funktioniert das gut. Darauf können wir uns aber nicht immer verlassen, und außerdem ist der Code nicht besonders leicht zu lesen. Stattdessen können Sie mit walk den Baum rekursiv durchlaufen und jedes Feld message abrufen:
Sobald wir diese Nachrichten in einer regulären Rego-Regel haben, können wir eine idiomatische Richtlinie schreiben, die Diskussionsthreads mit Schimpfwörtern zurückweist.
Ein Beispiel aus der Praxis sind untergeordnete Terraform-Module: Hier verwenden wir walk in Regula, um die untergeordneten Module abzuflachen – ganz ähnlich wie die Nachrichten im Diskussionsthread.
5. Pakete und Regeln dynamisch laden
Ein interessanter Aspekt des Rego-Designs ist, dass das gesamte „Universum“ aus Regeln und Daten unter demselben Dokument verschachtelt ist. Ob Sie auf Benutzereingaben, Daten aus JSON- oder YAML-Dateien oder Regeln aus Ihren Paketen zugreifen – es sind immer einfach Referenzen:
Da wir in Rego mithilfe von Variablen oder Platzhaltern alle Referenzen durchlaufen können, können wir die geladenen Pakete dynamisch ermitteln. So lässt sich eine Architektur aufbauen, bei der Sie einfach neue Richtliniendateien hinzufügen und sie alle in einem Bericht zusammenfassen.
Wenn Sie die Richtlinien in separate Pakete legen, lassen sie sich außerdem einzeln debuggen, und es besteht keine Gefahr, dass sich Regelnamen überschneiden. Wir legen alle Richtlinien im Namespace data.policies ab. Die erste verwendet die Gesetze von De Morgan, um zu prüfen, ob sich Käse auf einer Pizza befindet:
Die zweite Richtlinie stellt sicher, dass zwei Personen sich nicht wegen des letzten Stücks in die Haare geraten:
Sobald wir uns auf einen Namespace für unsere Richtlinien geeinigt haben, können wir sie relativ einfach zusammenfassen: Wir rufen jedes deny in data.policies ab und geben einen für Menschen verständlichen Bericht formatiert aus.
Zusammen mit etwas jq, um die tatsächlichen Werte zu extrahieren, lässt sich direkt in Ihrem Terminal ein übersichtlicher Bericht erstellen. Hier verwenden wir -I, um die Eingabe aus stdin zu lesen.
Pakete als Daten zu verwenden, eröffnet viele weitere Möglichkeiten für fortgeschrittene Tricks – zum Beispiel können Sie input in ein anderes Format umwandeln, je nachdem, ob ein Paket input_version = 1 oder input_version = 2 deklariert. Und das alles in Rego!



