Aller au contenu
Développement medium

Syntaxe de base de Rego : packages, règles et expressions

11 min de lecture

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.

  • Déclarer un package et 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
  • 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

Tout fichier Rego commence par une déclaration de package. C’est l’espace de noms qui regroupe vos règles.

package kubernetes.validation

Le 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.v1

Les commentaires commencent par #. Il n’y a pas de commentaires multi-lignes.

# Politique de validation des Deployments Kubernetes
# Auteur : stephane-robert
package kubernetes.validation
import rego.v1

Une règle associe un nom à une valeur. C’est l’unité de base de Rego.

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 vraie
app_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 vraie
version_api_supportee := "apps/v1"

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
}

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.

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érateurSignification
==égalité (avec unification)
!=différence
<inférieur strict
<=inférieur ou égal
>supérieur strict
>=supérieur ou égal

in teste l’appartenance à une collection (tableau, ensemble ou objet).

# Vérifie que le namespace est dans la liste autorisée
input.metadata.namespace in {"production", "staging"}
# Vérifie que "app" est une des clés du map labels
"app" in input.metadata.labels

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 interdit
deny if {
not namespace_autorise
}

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.

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.

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 à vrai
default allow := false
# Namespaces interdits pour les Deployments de production
namespaces_interdits := {"default", "kube-system"}
# La règle principale
allow if {
input.kind == "Deployment"
label_app_present
not namespace_interdit
}
# Le label `app` doit être présent dans les metadata
label_app_present if {
input.metadata.labels.app
}
# Le namespace ne doit pas être dans la liste interdite
namespace_interdit if {
input.metadata.namespace in namespaces_interdits
}

Pour évaluer cette politique sur le manifest JSON de l’exemple précédent :

Fenêtre de terminal
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.

  • Tout fichier Rego est ancré dans un package ; utilisez import rego.v1 pour 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 default sur les règles d’admission — ne laissez jamais allow potentiellement undefined.
  • input est 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.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn