Skip to main content

Rego 101 : présentation de Rego

Écrit par
feature cloud security

2 novembre 2023

0 minutes de lecture

Cette série d’articles vous propose une introduction en douceur à Rego, le langage de politiques créé par les concepteurs du moteur Open Policy Agent (OPA). Si vous débutez et souhaitez vous lancer dans l’écriture de politiques sous forme de code avec Rego, vous êtes au bon endroit.

Dans cette série en trois parties, nous aborderons les sujets suivants :

  • Partie 1 (celle-ci !) : concepts de base de Rego et prise en main d’OPA

  • Partie 2 : syntaxe Rego intermédiaire

  • Partie 3 : types de valeurs et règles

Que sont Rego et OPA ?

Rego est un langage de requête déclaratif créé par les concepteurs du framework Open Policy Agent (OPA). La Cloud Native Computing Foundation (CNCF) a accepté OPA en tant que projet hébergé au niveau incubation en avril 2019. En 2021, OPA a obtenu son diplôme et quitté le statut d’incubation.

Rego sert à écrire des politiques sous forme de code. Cette approche applique des pratiques de programmation, comme le contrôle de version et la conception modulaire, à l’évaluation des ressources cloud et d’infrastructure as code (IaC).

OPA est le moteur qui évalue les politiques sous forme de code écrites en Rego. Il « dissocie la prise de décision en matière de politiques de leur mise en application » : il détermine si une ressource est conforme à une politique, sans que vous ayez à intégrer ces vérifications en dur dans le code de votre application.

En séparant les politiques de votre logiciel et en confiant leur vérification à OPA, vous gagnez en rapidité et en flexibilité tout au long du cycle de développement. Vous pouvez mettre à jour vos politiques à tout moment, sans avoir à recompiler votre application ni à redéployer votre service. Vous pouvez ainsi développer à grande échelle, mieux voir votre niveau de conformité et appliquer vos politiques par programmation. L’adoption de politiques sous forme de code permet aussi de décaler vers la gauche le développement cloud et IaC, c’est-à-dire de les intégrer plus tôt dans le cycle de vie.

Chez Snyk, les règles intégrées et personnalisées de Snyk IaC+ sont écrites en Rego. En coulisses, Snyk utilise le moteur OPA pour évaluer les politiques Rego et renvoyer des décisions. En fait, Snyk a effectué plus d’un milliard d’évaluations de règles de sécurité avec OPA !

Comment fonctionne Rego ?

Avec un langage de requête déclaratif comme Rego, vous décrivez les données que vous souhaitez récupérer, puis le programme recherche une correspondance dans une source de données, appelée entrée. C’est différent des langages impératifs traditionnels, où vous décrivez les étapes à suivre pour obtenir un résultat. Vous connaissez peut-être déjà des langages de requête déclaratifs : SQL est probablement le plus utilisé.

Avec Rego, vous décrivez les conditions de réussite ou d’échec d’une politique, et OPA recherche dans un document d’entrée JSON (ou YAML) les données qui correspondent à ces conditions.

Le concept est séduisant, mais à quoi ressemble une règle ? Imaginons que nous devons appliquer une politique d’entreprise selon laquelle seule Alice, administratrice réseau, a le droit de créer et de supprimer des réseaux virtuels dans l’environnement de production. Voici un exemple de règle que nous pourrions écrire :

allow := true {
  input.user == "alice"
}

Nous reviendrons bientôt sur cette règle pour expliquer son fonctionnement. Pour l’instant, admirez simplement son élégante simplicité !

Entrée

OPA peut traiter n’importe quel document JSON ou YAML comme entrée. Avez-vous remarqué l’utilisation de input.user dans l’exemple de règle ci-dessus ? input est traité comme un document JSON spécial accessible globalement : vous pouvez donc y faire référence depuis n’importe quel endroit du fichier de politique Rego.

Voici un exemple de document d’entrée associé à la règle de la section précédente :

{
  "user": "alice"
}

Imaginons que ce document représente l’utilisateur actuellement connecté. Dans un contexte réel, le document d’entrée pourrait être un manifeste Kubernetes ou le résultat d’un plan Terraform. Nous vous en présenterons un exemple dans un prochain article.

Règles

Maintenant que vous savez à quoi ressemble une entrée, examinons le concept de règle. En Rego, une règle est une affectation conditionnelle. Chaque règle se compose de deux parties :

a head {
  and a body
}
  • La tête contient une variable et une valeur qui peut lui être affectée.

  • Le corps se compose d’une ou plusieurs requêtes qui indiquent à OPA quelle(s) condition(s) doivent être remplies pour que la valeur soit affectée à la variable.

Vous pouvez lire une règle ainsi :

THIS VARIABLE    :=	HAS THIS VALUE {
    IF THESE CONDITIONS ARE MET
}

En résumé, une règle interroge l’entrée pour vérifier si une condition est remplie et, si c’est le cas, affecte une valeur à une variable.

Voici la règle d’exemple utilisée précédemment :

allow := true {
  input.user == "alice"
}

Dans l’exemple ci-dessus, la tête (variable et valeur) est allow := true, et le corps (requête) est input.user == "alice". En réunissant la tête et le corps, on obtient une règle complète qui se lit ainsi :

La variable allow prend la valeur true SI user est égal à "alice".

Notez qu’en Rego, := est l’opérateur d’affectation, parfois appelé opérateur morse. Il affecte simplement une valeur à une variable, un peu comme le signe égal dans d’autres langages.

Requêtes

Examinons maintenant le concept de requête. Comme indiqué plus haut, une requête représente une condition à vérifier : c’est en quelque sorte la première moitié d’une instruction IF.

Voici, par exemple, la requête de la règle présentée dans la section précédente :

input.user == "alice"

Cette ligne indique à OPA d’interroger le document input pour vérifier SI l’utilisateur user est égal à "alice".

Il y a une nuance : Rego est déclaratif, donc une requête se contente techniquement d’énoncer « voici comment les choses sont », et OPA recherche dans l’entrée toutes les valeurs qui rendent cette affirmation vraie. Dans cet exemple, la requête indique que « la propriété user a pour valeur alice ». OPA doit examiner la propriété user et trouver, le cas échéant, toutes les entrées qui rendent cette affirmation vraie (ici, il recherche l’utilisateur alice).

Référencer l’entrée dans une requête

Pour rédiger une requête Rego, utilisez la notation pointée pour accéder à la propriété recherchée : chaque niveau imbriqué du document d’entrée est séparé par un point. Commencez par input, puis ajoutez un point suivi du nom de la propriété de premier niveau (ici, input.user).

Pour référencer une propriété imbriquée, vous devez indiquer tous les niveaux à parcourir pour y accéder. Commencez par input, ajoutez un point et la propriété de premier niveau (input.your-property-here), puis continuez d’ajouter des points et des noms de propriétés jusqu’à atteindre la propriété imbriquée recherchée. Si la propriété recherchée est un tableau, patience : nous y reviendrons bientôt.

Imaginons plutôt que le document d’entrée ressemble à ceci :

{
  "admins": {
    "user": "alice"
  }
}

Comme user est imbriqué dans admins, lui-même imbriqué dans input, vous devez le référencer ainsi :

input.admins.user

En revanche, si la propriété d’entrée à laquelle vous faites référence est un tableau (une liste), utilisez l’opérateur générique — un trait de soulignement — pour spécifier la propriété. Par exemple, imaginons que l’entrée contienne un tableau user :

{
  "users": [ "alice", "bob", "carlotta" ]
}

Dans ce cas, pour vérifier si alice figure dans le tableau users, utilisez la syntaxe suivante :

allow := true {
  input.users[_] == "alice"
}

Ici, l’opérateur générique indique à OPA de parcourir le tableau pour vérifier si l’un de ses éléments est égal à alice. Nous aborderons l’itération dans un prochain article.

De même, supposons que vous souhaitiez vérifier si la valeur de la propriété admin est true dans l’un des éléments du tableau users ci-dessous (même si un seul élément est affiché) :

{
  "users": [
    {
      "name": "alice",
      "admin": true
    }
  ]
}

Vous utiliseriez une syntaxe comme celle-ci :

allow := true {
  input.users[_].admin == true
}

Bien sûr, les conditions IF sont peu utiles sans action conditionnelle associée. C’est là qu’il est utile de comprendre le fonctionnement des affectations en Rego : elles constituent la seconde moitié de l’instruction IF.

Évaluation des règles

Dans une règle, l’action conditionnelle consiste à affecter une variable. Dans notre exemple, nous examinons l’entrée pour déterminer si la variable allow doit prendre la valeur true. Vous pouvez lire la règle ainsi :

allow := true {
  IF THIS CONDITION IS MET
}

Que sont les variables ?

Une variable est une référence à une valeur précise. Ici, la variable x reçoit la valeur 1 :

x := 1

Vous pouvez ensuite utiliser la variable à la place de la valeur : pour Rego, cela revient au même :

x := 1
y := 2
z := x + y

Pour revenir à notre exemple, la variable est allow, et la valeur à lui affecter est true :

allow := true

En l’associant à la condition (requête) présentée précédemment, vous obtenez la règle complète :

allow := true {
  input.user == "alice"
}

En résumé, cette règle se lit ainsi :

La règle allow prend la valeur true SI input.user est égal à "alice".

En pratique, ce type de règle est très courant. Rego vous permet donc de la raccourcir ainsi :

allow {
  input.user == "alice"
}

Évaluer la règle avec OPA

Nous avons une règle et un document d’entrée. L’étape suivante consiste à utiliser OPA pour évaluer l’entrée par rapport à la règle. Vérifions si notre entrée est conforme à la politique et si l’utilisateur connecté est autorisé à créer et à supprimer des réseaux virtuels dans l’environnement de production.

Nous allons nous intéresser à deux façons d’interagir avec OPA :

  • Utiliser Rego Playground

  • Utiliser l’outil en ligne de commande OPA

Utiliser Rego Playground

Le moyen le plus simple de commencer à écrire des règles consiste à utiliser Rego Playground d’OPA. Cet outil interactif vous permet d’écrire, de tester et de partager des règles et des entrées.

Voici les bases :

  • Pour modifier une règle, utilisez le champ de texte de la règle, à gauche de la page.

  • Pour modifier l’entrée, utilisez le champ Input, en haut à droite de la page. (Elle doit être au format JSON valide.)

  • Pour évaluer une règle, sélectionnez le bouton Evaluate au-dessus de l’entrée.

  • Pour consulter les résultats de l’évaluation, regardez le champ Output, en bas à droite.

  • Pour partager une règle, sélectionnez le bouton Publish en haut à droite. OPA génère une URL que vous pouvez transmettre à n’importe qui pour lui permettre de tester ou de modifier la règle et l’entrée.

Faites autant d’expériences que vous le souhaitez et n’ayez pas peur de vous tromper ! Vous ne risquez rien : le compilateur vous indiquera si le code Rego est invalide. Rechargez la page pour rétablir l’état initial de l’environnement de test (ou son état publié si vous consultez un environnement publié).

Nous avons partagé un environnement de test avec la règle allow et le document d’entrée de notre exemple. Vous pouvez y accéder à cette URL : https://play.openpolicyagent.org/p/SH5ApmfodX 

Vous pouvez aussi ouvrir un nouvel environnement de test et coller la règle dans le champ de texte à gauche :

allow := true {
  input.user == "alice"
}

Puis coller l’entrée dans le champ de texte en haut à droite :

{
  "user": "alice"
}

Rappelons que notre politique d’entreprise stipule que seule Alice, administratrice réseau, doit avoir le droit de créer et de supprimer des réseaux virtuels dans l’environnement de production. Le document d’entrée représente l’utilisateur actuellement connecté. Notre règle vérifie si l’utilisateur du document d’entrée est alice et, si c’est le cas, la variable allow prend la valeur true.

Cliquez sur le bouton Evaluate. Vous verrez le résultat suivant :

{
  "allow": true
}

Cela signifie que allow prend bien la valeur true. OPA a déterminé que le document d’entrée est conforme à notre politique d’entreprise. L’utilisateur est Alice, ce qui signifie qu’il est autorisé à créer et à supprimer des réseaux virtuels dans l’environnement de production.

Utiliser l’outil en ligne de commande OPA

Vous pouvez également évaluer des règles avec OPA à l’aide de l’outil en ligne de commande opa. Les instructions d’installation de opa sont disponibles dans la documentation d’OPA. Une fois l’outil installé, vous aurez besoin de deux éléments :

  • Un fichier de politique .rego contenant votre règle, avec une déclaration de package telle que package rules.check_user tout en haut. Nous avons nommé notre fichier de politique check_user.rego.

  • Un fichier .json contenant l’entrée. Nous avons nommé notre fichier d’entrée input.json.

Une fois ces deux éléments en votre possession, vous pouvez utiliser la commande opa eval pour évaluer votre politique sous forme de code :

opa eval -i input.json -d check_user.rego "data.rules.check_user" --format pretty

Vous pouvez bien sûr nommer vos fichiers comme vous le souhaitez : veillez simplement à ce que la commande respecte cette structure :

opa eval -i <input file> -d <rule file> "data.<package name>" --format pretty

Si allow renvoie true, comme dans notre exemple, vous verrez le même résultat que dans Rego Playground :

{
  "allow": true
}

Une fois encore, OPA indique que l’entrée est conforme à — respecte — la politique de notre entreprise, ce qui signifie que l’utilisateur actuellement connecté est autorisé à créer et à supprimer des réseaux virtuels dans le compte de production.

Tester une entrée non conforme

À quoi ressemble un document d’entrée non conforme ? Dans le playground ou dans votre fichier local .rego, remplacez l’entrée par ce qui suit :

{
  "user": "bob"
}

Maintenant, lorsque vous évaluez la règle (en cliquant sur Evaluate dans Rego Playground ou en exécutant la commande opa eval mentionnée précédemment), vous obtenez le résultat suivant :

{}

Qu’est-ce que cela signifie ? Dans cet exemple, OPA renvoie un résultat « undefined » (c’est-à-dire un ensemble vide), car il ne trouve pas dans l’entrée de valeur correspondant à la condition input.user == "alice". L’ensemble est vide, car aucun résultat n’a été trouvé. Par conséquent, allow ne renvoie pas true, et le document d’entrée n’est pas conforme à la politique de l’entreprise. Désolé, Bob !

Et ensuite ?

N’oubliez pas de revenir sur notre blog pour découvrir la suite de notre série Rego pour débutants. Nous y explorerons la syntaxe intermédiaire des règles Rego, notamment les structures AND et OR, les messages personnalisés, les mots-clés spéciaux et bien plus encore.

En attendant, voici quelques ressources utiles :

Si vous souhaitez utiliser Rego pour écrire des règles personnalisées pour Snyk IaC, consultez notre documentation. En plus des ensembles de règles intégrés de Snyk, qui couvrent la sécurité et la conformité, les règles personnalisées IaC+ vous permettent de définir des contrôles de sécurité adaptés à chaque étape de votre SDLC.

IaC+ vous offre une vue unifiée et des contrôles pour vos problèmes de configuration, du code au cloud, grâce à une interface de gestion des problèmes, à un ensemble de règles et à un moteur de politiques couvrant les IDE, les SCM, les CLI, les environnements CI/CD, Terraform Cloud et les environnements cloud déployés tels qu’AWS, Azure et Google Cloud.

Une sécurité IaC pensée pour les développeurs

Snyk sécurise votre infrastructure en tant que code, du cycle de développement logiciel à l’exécution dans le cloud, grâce à un moteur unifié de politiques sous forme de code. Chaque équipe peut ainsi développer, déployer et exploiter ses applications en toute sécurité.