Aller au contenu
Développement medium

Rego avancé : métadonnées, bundles OPA et bibliothèques de politiques

14 min de lecture

Les quatre guides précédents couvrent l’écriture et l’exécution de politiques Rego. Ce dernier guide aborde la question de l’industrialisation : comment structurer, documenter, versionner et distribuer des politiques à l’échelle d’une organisation avec plusieurs équipes et plusieurs clusters.

  • Migrer un projet existant vers rego.v1 et intégrer opa fmt en pre-commit
  • Annoter les règles avec # METADATA pour documenter et filtrer
  • Construire, signer et distribuer un bundle OPA via un registre OCI
  • Structurer une bibliothèque organisationnelle avec séparation code / données
  • Utiliser every pour exprimer une propriété universelle sur une collection

rego.v1 est le mode d’écriture recommandé depuis OPA 0.59. Un seul import active toutes les fonctionnalités modernes et lève les ambiguïtés syntaxiques des versions précédentes.

import rego.v1

Ce que rego.v1 change concrètement :

ComportementAvant rego.v1Avec rego.v1
Mot-clé inOptionnel (import séparé)Activé par défaut
Mot-clé everyOptionnel (import séparé)Activé par défaut
if dans les têtes de règleFacultatifObligatoire
contains pour règles partiellesFacultatifObligatoire
Variables non utiliséesAvertissementErreur

OPA peut réécrire automatiquement les fichiers vers rego.v1 :

Fenêtre de terminal
opa fmt --rego-v1 --write policies/

opa fmt reformate aussi l’indentation et l’espacement. Lancez-le systématiquement avant chaque commit — ou mieux, intégrez-le comme hook pre-commit.

.pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: opa-fmt
name: opa fmt
entry: opa fmt --write
language: system
files: \.rego$
- id: opa-check
name: opa check
entry: opa check --strict
language: system
files: \.rego$

Les annotations Rego sont des blocs de commentaires structurés en YAML placés juste avant une règle ou un package. Elles servent à la documentation, aux outils d’analyse statique et à la génération automatique de documentation.

# METADATA
# title: Image doit provenir d'un registre autorisé
# description: |
# Vérifie que toutes les images des conteneurs proviennent d'un
# registre interne approuvé. Le tag `latest` est interdit en production.
# authors:
# - stephane-robert
# custom:
# severity: HIGH
# category: supply-chain
# remediation: "Utiliser registry.example.com/<image>:<version>"
package kubernetes.policies.images
import rego.v1

Les annotations peuvent aussi s’appliquer à une règle individuelle :

# METADATA
# title: Tag latest interdit
# description: Le tag latest ne permet pas de garantir la reproductibilité des déploiements.
# related_resources:
# - ref: https://docs.docker.com/develop/dev-best-practices/
# custom:
# severity: HIGH
deny contains msg if {
some c in input.spec.template.spec.containers
endswith(c.image, ":latest")
msg := sprintf("conteneur '%v' : tag latest interdit", [c.name])
}
Fenêtre de terminal
# Lister toutes les règles annotées d'un répertoire
opa inspect --annotations --format json policies/ | jq '.[].annotations'
# Extraire les règles de sévérité HIGH
opa inspect --annotations --format json policies/ \
| jq '[.[] | select(.annotations.custom.severity == "HIGH")]'

Les annotations sont exploitées par Gatekeeper (affichage dans les messages de refus), par les outils de documentation automatique comme Regal et par les pipelines qui veulent filtrer les politiques par catégorie ou sévérité.

Regal est le linter officiel pour Rego. Il détecte les erreurs de style, les patterns dépréciés et les problèmes de performance.

Fenêtre de terminal
# Installation
brew install styrainc/packages/regal
# Linter un répertoire
regal lint policies/
# Avec configuration personnalisée
regal lint --config .regal/config.yaml policies/

Exemple de configuration .regal/config.yaml :

rules:
style:
opa-fmt:
level: error
use-assignment-operator:
level: error
imports:
prefer-package-imports:
level: warning
testing:
test-outside-test-package:
level: error

Intégrez Regal dans le pre-commit et la CI aux côtés de opa fmt et opa test.

Un bundle est une archive .tar.gz qui regroupe des fichiers .rego et des données JSON/YAML. C’est le format standard pour distribuer des politiques à des instances OPA distantes.

bundle/
├── .manifest # métadonnées du bundle
├── lib/
│ └── kubernetes.rego
├── policies/
│ ├── images.rego
│ ├── security.rego
│ └── labels.rego
└── data/
└── registres.json # données de référence

Le fichier .manifest déclare la version et les racines :

{
"revision": "v1.4.2",
"roots": ["lib", "policies", "data"],
"metadata": {
"required_capabilities": {
"builtins": [{ "name": "rego.v1" }]
}
}
}
Fenêtre de terminal
# Construire le bundle
opa build \
--bundle \
--output bundle-v1.4.2.tar.gz \
lib/ policies/ data/
# Vérifier le contenu
tar tzf bundle-v1.4.2.tar.gz

OPA supporte nativement la distribution de bundles depuis un registre compatible OCI (Harbor, GHCR, Docker Hub…).

Fenêtre de terminal
# Publier sur GHCR
opa push \
--bundle bundle-v1.4.2.tar.gz \
ghcr.io/mon-org/opa-policies:v1.4.2
# Vérifier
opa pull ghcr.io/mon-org/opa-policies:v1.4.2
opa-config.yaml
bundles:
politique-organisation:
resource: ghcr.io/mon-org/opa-policies:latest
polling:
min_delay_seconds: 60
max_delay_seconds: 120
signing:
keyid: "cle-verification"
decision_logs:
console: true
Fenêtre de terminal
opa run --server --config-file opa-config.yaml

OPA télécharge le bundle au démarrage et le recharge périodiquement. Les instances distantes reçoivent automatiquement les nouvelles versions sans redémarrage.

En production, signez vos bundles pour garantir leur intégrité. OPA vérifie la signature avant de charger un bundle.

Fenêtre de terminal
# Générer une paire de clés
opa keys generate --algorithm ES256 --id cle-prod
# Signer le bundle
opa build \
--bundle \
--signing-key cle-prod \
--output bundle-signe.tar.gz \
lib/ policies/
# Vérifier la signature
opa bundle verify \
--verification-key cle-prod.pub \
bundle-signe.tar.gz

La clé publique est embarquée dans la configuration OPA des instances consommatrices. Tout bundle non signé ou signé avec une clé inconnue est rejeté.

À l’échelle d’une organisation, une bibliothèque de politiques suit généralement cette structure :

policies-org/
├── .manifest
├── lib/
│ ├── kubernetes/
│ │ ├── helpers.rego # fonctions utilitaires génériques
│ │ ├── images.rego # helpers liés aux images
│ │ └── security.rego # helpers liés au securityContext
│ └── terraform/
│ └── helpers.rego
├── policies/
│ ├── kubernetes/
│ │ ├── images/
│ │ │ ├── required_registry.rego
│ │ │ ├── required_registry_test.rego
│ │ │ └── no_latest_tag.rego
│ │ └── security/
│ │ ├── no_privileged.rego
│ │ └── readonly_rootfs.rego
│ └── terraform/
│ └── no_public_bucket.rego
└── data/
├── registres_autorises.json
└── namespaces_sensibles.json

Les données qui changent sans modifier le code (listes blanches, paramètres par environnement) vivent dans des fichiers JSON chargés dans data.

data/registres_autorises.json
{
"registres_autorises": [
"registry.example.com",
"ghcr.io/mon-org"
]
}
# Référencement dans une politique
deny contains msg if {
some c in input.spec.template.spec.containers
registre := split(c.image, "/")[0]
not registre in data.registres_autorises
msg := sprintf("registre '%v' non autorisé", [registre])
}

Modifier la liste blanche ne nécessite pas de toucher au code Rego — seulement au JSON, puis de publier un nouveau bundle.

.github/workflows/release.yml
name: Release bundle
on:
push:
tags: ["v*"]
permissions:
contents: read
packages: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- name: Install OPA
run: |
curl -Lo opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64
chmod +x opa && sudo mv opa /usr/local/bin/
- name: Lint
run: regal lint policies/ lib/
- name: Format check
run: opa fmt --fail policies/ lib/
- name: Tests
run: opa test policies/ lib/ --coverage
- name: Build bundle
run: |
opa build --bundle \
--signing-key ${{ secrets.OPA_SIGNING_KEY }} \
--output bundle-${{ github.ref_name }}.tar.gz \
lib/ policies/ data/
- name: Push to GHCR
run: |
opa push \
--bundle bundle-${{ github.ref_name }}.tar.gz \
ghcr.io/${{ github.repository_owner }}/opa-policies:${{ github.ref_name }}
opa push \
--bundle bundle-${{ github.ref_name }}.tar.gz \
ghcr.io/${{ github.repository_owner }}/opa-policies:latest

every est disponible avec rego.v1. Il exprime qu’une condition doit être vraie pour tous les éléments d’une collection — l’inverse de l’itération implicite qui exprime qu’il existe un élément satisfaisant la condition.

# Vrai si TOUS les conteneurs ont une image conforme
toutes_images_conformes if {
every c in input.spec.template.spec.containers {
startswith(c.image, "registry.example.com/")
not endswith(c.image, ":latest")
}
}

Sans every, il faudrait combiner une compréhension et count :

# Équivalent sans every — moins lisible
toutes_images_conformes if {
non_conformes := [c |
some c in input.spec.template.spec.containers
not startswith(c.image, "registry.example.com/")
]
count(non_conformes) == 0
}

every est réservé aux cas où vous voulez explicitement affirmer une propriété universelle. Pour collecter les violations et les reporter, la compréhension reste plus adaptée.

  • Adoptez rego.v1 sur tous les nouveaux fichiers et migrez les existants avec opa fmt --rego-v1 --write.
  • Les annotations # METADATA documentent les règles de façon exploitable par opa inspect, Regal et Gatekeeper.
  • Un bundle est la brique de distribution standard : une archive versionnée, signée, publiée sur un registre OCI.
  • Séparez code Rego (dans .rego) et paramètres variables (dans data/*.json) pour changer les listes blanches sans toucher aux politiques.
  • every pour les assertions universelles, compréhensions pour les violations — les deux sont complémentaires.
  • Regal + opa fmt + opa test en pre-commit et CI garantissent la qualité du code avant distribution.

Le parcours Rego est terminé. Pour aller plus loin :

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