Dans le guide précédent, on a accédé au premier conteneur d’un pod avec containers[0]. En production, un pod en a souvent plusieurs. Si une seule image utilise le tag latest, la politique doit le détecter — peu importe sa position dans la liste.
C’est là qu’intervient l’itération en Rego : non pas une boucle for explicite, mais un mécanisme d’unification implicite qui évalue une règle pour toutes les valeurs possibles d’une variable.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Distinguer les trois structures de données Rego : tableau, objet, ensemble
- Itérer implicitement avec une variable libre ou
some ... in - Construire des collections filtrées avec les compréhensions
- Utiliser les fonctions built-in sur les collections (
count,sum,object.get…) - Écrire une politique qui liste toutes les infractions d’un manifest
Prérequis
Section intitulée « Prérequis »- Syntaxe de base de Rego : packages, règles,
default, opérateurs
Les structures de données
Section intitulée « Les structures de données »Rego manipule trois types de collections, chacune avec une syntaxe et une sémantique distinctes.
Tableaux (arrays)
Section intitulée « Tableaux (arrays) »Séquence ordonnée, indexée à partir de 0, qui peut contenir des doublons.
images := ["nginx:latest", "redis:7", "postgres:15"]
# Accès par indexpremiere_image := images[0] # "nginx:latest"Objets (objects)
Section intitulée « Objets (objects) »Map de clé → valeur. Les clés sont des chaînes ou des nombres, les valeurs peuvent être de n’importe quel type.
labels := { "app": "api-server", "env": "production", "team": "platform"}
# Accès par cléenv := labels["env"] # "production"env := labels.env # syntaxe équivalenteEnsembles (sets)
Section intitulée « Ensembles (sets) »Collection non ordonnée, sans doublon. La syntaxe utilise des accolades sans séparateur clé/valeur — c’est ce qui les distingue des objets.
namespaces_autorises := {"production", "staging", "monitoring"}
# Opérations d'ensemble"production" in namespaces_autorises # trueunion_ns := namespaces_autorises | {"preprod"}inter_ns := namespaces_autorises & {"production", "dev"}Itération implicite
Section intitulée « Itération implicite »En Rego, une variable libre dans un accès de collection itère automatiquement sur tous ses éléments. Il n’y a pas de mot-clé for.
# `i` est une variable libre — cette expression est vraie pour chaque index# où la valeur du conteneur contient "latest"image_latest if { conteneur := input.spec.template.spec.containers[i] endswith(conteneur.image, ":latest")}OPA évalue cette règle pour toutes les valeurs de i. Si un seul conteneur a une image en :latest, la règle est vraie.
C’est le changement de paradigme central : vous ne demandez pas « pour chaque élément, fais ceci » — vous déclarez « il existe un élément tel que ».
Le mot-clé some
Section intitulée « Le mot-clé some »some déclare explicitement une variable comme libre. C’est facultatif dans certains contextes mais obligatoire avec rego.v1 pour les variables utilisées comme index d’itération.
import rego.v1
image_latest if { some i conteneur := input.spec.template.spec.containers[i] endswith(conteneur.image, ":latest")}some peut aussi déclarer plusieurs variables d’un coup :
some i, jsome ... in
Section intitulée « some ... in »La forme moderne combine déclaration et itération en une seule expression :
image_latest if { some conteneur in input.spec.template.spec.containers endswith(conteneur.image, ":latest")}C’est la syntaxe à privilégier avec rego.v1 : plus lisible, et elle évite de manipuler les index manuellement.
Le wildcard _
Section intitulée « Le wildcard _ »Quand l’index ne vous intéresse pas, utilisez _ (underscore). Il itère comme une variable libre mais son nom est jetable — vous ne pouvez pas y accéder après.
image_latest if { endswith(input.spec.template.spec.containers[_].image, ":latest")}Compréhensions
Section intitulée « Compréhensions »Les compréhensions construisent une nouvelle collection à partir d’une itération filtrée. Rego en propose trois formes.
Compréhension de tableau
Section intitulée « Compréhension de tableau »Syntaxe : [expression | corps]
# Liste de toutes les images des conteneurstoutes_les_images := [c.image | some c in input.spec.template.spec.containers]
# Liste des images non conformes (sans préfixe de registre interne)images_non_conformes := [ c.image | some c in input.spec.template.spec.containers not startswith(c.image, "registry.example.com/")]Compréhension d’ensemble
Section intitulée « Compréhension d’ensemble »Syntaxe : {expression | corps} — le résultat ne contient pas de doublons.
# Ensemble de tous les namespaces référencés dans plusieurs ressourcesnamespaces_utilises := {r.metadata.namespace | some r in input.items}
# Ensemble des noms de conteneurs sans readOnlyRootFilesystemconteneurs_non_readonly := { c.name | some c in input.spec.template.spec.containers not c.securityContext.readOnlyRootFilesystem}Compréhension d’objet
Section intitulée « Compréhension d’objet »Syntaxe : {clé: valeur | corps}
# Map nom_conteneur → imageimages_par_conteneur := { c.name: c.image | some c in input.spec.template.spec.containers}Fonctions intégrées sur les collections
Section intitulée « Fonctions intégrées sur les collections »OPA fournit un ensemble de fonctions built-in pour opérer sur les collections sans itération manuelle.
conteneurs := input.spec.template.spec.containers
nb_conteneurs := count(conteneurs) # nombre d'élémentstoutes_les_images := {c.image | some c in conteneurs}
# Sur des tableaux de nombresreplicas := [1, 3, 2, 5]total := sum(replicas) # 11maximum := max(replicas) # 5minimum := min(replicas) # 1trie := sort(replicas) # [1, 2, 3, 5]Pour les objets :
labels := input.metadata.labelscles := object.keys(labels) # ensemble des clésvaleur := object.get(labels, "app", "inconnu") # accès avec valeur par défautFil rouge : détecter tous les conteneurs non conformes
Section intitulée « Fil rouge : détecter tous les conteneurs non conformes »On enrichit la politique du guide précédent. Cette fois, on veut non seulement détecter qu’un problème existe, mais aussi lister tous les conteneurs en infraction pour un message d’erreur exploitable.
package kubernetes.validation
import rego.v1
default allow := false
allow if { count(violations) == 0}
# Ensemble de tous les messages de violationviolations := {msg | some msg in violations_image} | {msg | some msg in violations_readonly}
# Violation : image sans tag explicite ou avec tag `latest`violations_image := {msg | some c in input.spec.template.spec.containers image_non_conforme(c.image) msg := sprintf("conteneur '%v' : image '%v' interdite (tag latest ou absent)", [c.name, c.image])}
# Violation : système de fichiers racine non en lecture seuleviolations_readonly := {msg | some c in input.spec.template.spec.containers not c.securityContext.readOnlyRootFilesystem msg := sprintf("conteneur '%v' : readOnlyRootFilesystem doit être true", [c.name])}
# Fonction helper — image non conforme si tag absent ou égal à "latest"image_non_conforme(image) if { not contains(image, ":")}
image_non_conforme(image) if { endswith(image, ":latest")}Évaluez la politique sur un manifest avec plusieurs conteneurs problématiques :
opa eval \ --input manifest.json \ --data politique.rego \ "data.kubernetes.validation.violations"Résultat :
{ "result": [{ "expressions": [{ "value": [ "conteneur 'sidecar' : image 'busybox:latest' interdite (tag latest ou absent)", "conteneur 'init' : readOnlyRootFilesystem doit être true" ] }] }]}Ce pattern — allow qui vérifie que violations est vide, et violations construit par compréhension — est le standard dans les politiques OPA et Gatekeeper. Il vous donne à la fois le résultat booléen et le détail des infractions en un seul passage.
À retenir
Section intitulée « À retenir »- Rego itère implicitement : une variable libre dans
collection[i]s’évalue pour tous les éléments. - Préférez
some conteneur in collection(forme moderne) àcollection[_]pour la lisibilité. - Les compréhensions (
[... | ...],{... | ...},{k: v | ...}) construisent des collections filtrées en une expression. count(violations) == 0comme condition deallowest le pattern de référence pour des erreurs lisibles.object.getpour les champs optionnels, plutôt que de laisser l’unification échouer silencieusement.