Rego est un langage déclaratif : vous décrivez ce qui doit être vrai, pas comment le calculer. C’est le premier changement mental à opérer avant d’écrire quoi que ce soit. Si vous venez de Python, Go ou Bash, certaines constructions vont sembler étranges au premier abord — elles deviennent évidentes dès que le modèle d’évaluation est clair.
Ce guide couvre la structure d’un fichier .rego, les types de règles, les opérateurs et la façon dont Rego accède aux données d’entrée. Tout est illustré avec un exemple fil rouge : valider qu’un manifest Kubernetes respecte des contraintes de base.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Déclarer un
packageet importer les fonctionnalités modernes de Rego - Écrire une règle complète, booléenne ou avec valeur par défaut
- Comparer, tester l’appartenance et négler avec les bons opérateurs
- Accéder aux données d’un manifest Kubernetes via
input - Identifier les pièges liés à la sémantique undefined de Rego
Prérequis
Section intitulée « Prérequis »- Installation d’OPA prévue dans le guide OPA en pratique (optionnelle pour la lecture)
- Un éditeur avec support Rego est utile mais non nécessaire
Structure d’un fichier .rego
Section intitulée « Structure d’un fichier .rego »Tout fichier Rego commence par une déclaration de package. C’est l’espace de noms qui regroupe vos règles.
package kubernetes.validationLe nom de package suit une convention pointée, libre mais habituellement alignée sur le domaine traité. Par convention dans les projets OPA, on utilise des noms hiérarchiques : org.politique, kubernetes.admission, terraform.security.
Rego fournit deux espaces de noms globaux accessibles sans import : input (les données évaluées) et data (les données de référence chargées dans OPA). Pour le reste, on importe explicitement :
package kubernetes.validation
import future.keywords.in # active le mot-clé `in`import future.keywords.every # active le mot-clé `every`package kubernetes.validation
import rego.v1Commentaires
Section intitulée « Commentaires »Les commentaires commencent par #. Il n’y a pas de commentaires multi-lignes.
# Politique de validation des Deployments Kubernetes# Auteur : stephane-robertpackage kubernetes.validation
import rego.v1Les règles
Section intitulée « Les règles »Une règle associe un nom à une valeur. C’est l’unité de base de Rego.
Règle complète (valeur scalaire)
Section intitulée « Règle complète (valeur scalaire) »Une règle complète attribue une valeur unique à un nom si les conditions du corps sont satisfaites.
# La règle `app_label_requis` vaut true si la condition est vraieapp_label_requis := true if { input.metadata.labels.app}La syntaxe nom := valeur if { corps } se lit : « cette règle a cette valeur si toutes les expressions du corps sont vraies ». Si le corps est absent, la règle a toujours la valeur assignée.
# Règle constante — toujours vraieversion_api_supportee := "apps/v1"Règle booléenne
Section intitulée « Règle booléenne »Une règle booléenne est une règle dont la valeur implicite est true. C’est la forme la plus courante dans les politiques d’admission.
# Vrai si le Deployment est dans un namespace autorisénamespace_autorise if { namespaces_autorises := {"production", "staging", "monitoring"} input.metadata.namespace in namespaces_autorises}La règle default
Section intitulée « La règle default »default définit la valeur de repli quand aucune autre règle du même nom ne s’évalue à vrai. C’est le filet de sécurité indispensable.
default allow := false
allow if { namespace_autorise image_valide}Sans default allow := false, si allow n’est pas définie, OPA retourne undefined et non false. Selon le contexte d’intégration (Gatekeeper, Conftest, API REST), ce comportement est différent — autant être explicite.
Expressions et opérateurs
Section intitulée « Expressions et opérateurs »Le corps d’une règle est une séquence d’expressions séparées par des virgules ou des sauts de ligne. Toutes les expressions doivent être vraies pour que la règle soit vraie (c’est un ET implicite).
allow if { input.kind == "Deployment" # égalité input.metadata.namespace != "default" # inégalité count(input.spec.template.spec.containers) > 0}Opérateurs de comparaison
Section intitulée « Opérateurs de comparaison »| Opérateur | Signification |
|---|---|
== | égalité (avec unification) |
!= | différence |
< | inférieur strict |
<= | inférieur ou égal |
> | supérieur strict |
>= | supérieur ou égal |
L’opérateur in
Section intitulée « L’opérateur in »in teste l’appartenance à une collection (tableau, ensemble ou objet).
# Vérifie que le namespace est dans la liste autoriséeinput.metadata.namespace in {"production", "staging"}
# Vérifie que "app" est une des clés du map labels"app" in input.metadata.labelsNégation avec not
Section intitulée « Négation avec not »not inverse le résultat d’une expression. Attention : not opère sur une règle entière ou une expression, pas sur une comparaison directe.
# Refus si le namespace est interditdeny if { not namespace_autorise}Référencer input
Section intitulée « Référencer input »input est le document JSON soumis à évaluation. OPA le fournit automatiquement au moment de l’évaluation — pas besoin de le déclarer.
Pour un manifest Kubernetes comme celui-ci :
{ "apiVersion": "apps/v1", "kind": "Deployment", "metadata": { "name": "api-server", "namespace": "production", "labels": { "app": "api-server", "env": "prod" } }, "spec": { "replicas": 3, "template": { "spec": { "containers": [ { "name": "api", "image": "registry.example.com/api:1.4.2" } ] } } }}Accédez aux champs avec la notation pointée :
input.kind # "Deployment"input.metadata.name # "api-server"input.metadata.labels.app # "api-server"input.spec.template.spec.containers[0].image # "registry.example.com/api:1.4.2"L’accès à un champ inexistant ne lève pas d’erreur : l’expression devient simplement undefined et le corps de la règle échoue silencieusement. C’est le comportement voulu dans un langage déclaratif.
Variables locales et unification
Section intitulée « Variables locales et unification »En Rego, une variable non encore liée peut être unifiée avec une valeur. L’opérateur := est une affectation unique — une variable ne peut pas être réassignée dans le même bloc.
check_image if { image := input.spec.template.spec.containers[0].image # `image` est maintenant liée à la valeur du champ startswith(image, "registry.example.com/")}Si vous essayez d’assigner deux fois la même variable dans le même corps, OPA lève une erreur de compilation. C’est une contrainte volontaire qui garantit que chaque règle est pure et sans effet de bord.
Fil rouge : première politique de validation
Section intitulée « Fil rouge : première politique de validation »Voici une politique complète qui intègre tout ce qui précède. Elle refuse un Deployment si le label app est absent ou si le namespace est default.
package kubernetes.validation
import rego.v1
# Valeur par défaut : refus si aucune règle `allow` ne s'évalue à vraidefault allow := false
# Namespaces interdits pour les Deployments de productionnamespaces_interdits := {"default", "kube-system"}
# La règle principaleallow if { input.kind == "Deployment" label_app_present not namespace_interdit}
# Le label `app` doit être présent dans les metadatalabel_app_present if { input.metadata.labels.app}
# Le namespace ne doit pas être dans la liste interditenamespace_interdit if { input.metadata.namespace in namespaces_interdits}Pour évaluer cette politique sur le manifest JSON de l’exemple précédent :
opa eval \ --input manifest.json \ --data politique.rego \ "data.kubernetes.validation.allow"Résultat attendu :
{ "result": [ { "expressions": [ { "value": true, "text": "data.kubernetes.validation.allow" } ] } ]}Modifiez le namespace en default dans le JSON et relancez : le résultat passe à false. C’est ça, une politique as code.
À retenir
Section intitulée « À retenir »- Tout fichier Rego est ancré dans un
package; utilisezimport rego.v1pour les projets récents. - Une règle associe un nom à une valeur ; le corps est une séquence de conditions reliées par un ET implicite.
- Mettez toujours un
defaultsur les règles d’admission — ne laissez jamaisallowpotentiellement undefined. inputest le document évalué ; l’accès à un champ inexistant rend l’expression undefined sans erreur.- Une variable est assignée une seule fois par bloc (
:=) ; Rego est sans état et sans effet de bord.