RBAC avec API Gateway et Open Policy Agent (OPA)

Bobur Umurzokov

Bobur Umurzokov

May 15, 2023

Technology

Avec divers modèles de contrôle d'accès et méthodes d'implémentation disponibles, la construction d'un système d'autorisation pour les API de services backend peut encore être un défi. Cependant, l'objectif ultime est de s'assurer que la bonne personne a un accès approprié à la ressource concernée. Dans cet article, nous discuterons comment activer le modèle d'autorisation basé sur les rôles (RBAC) pour votre API avec la passerelle API open-source Apache APISIX et Open Policy Agent (OPA).

Objectifs d'apprentissage

Vous apprendrez les éléments suivants tout au long de l'article :

  • Qu'est-ce que le RBAC et comment fonctionne-t-il ?
  • Qu'est-ce que l'OPA et comment fonctionne-t-il ?
  • Comment implémenter le RBAC avec OPA et Apache APISIX ?
  • Comment définir et enregistrer la politique dans OPA.
  • Comment créer un upstream, une route et activer le plugin OPA.
  • Comment le rôle et les permissions de l'utilisateur sont extraits du payload du token JWT ou des données du consommateur.

Qu'est-ce que le RBAC ?

Le contrôle d'accès basé sur les rôles (RBAC) et le contrôle d'accès basé sur les attributs (ABAC) sont deux modèles de contrôle d'accès couramment utilisés pour gérer les permissions et contrôler l'accès aux ressources dans les systèmes informatiques. Le RBAC attribue des permissions aux utilisateurs en fonction de leur rôle au sein d'une organisation. Dans le RBAC, les rôles sont définis en fonction des fonctions ou responsabilités des utilisateurs, et les permissions sont attribuées à ces rôles. Les utilisateurs sont ensuite assignés à un ou plusieurs rôles, et ils héritent des permissions associées à ces rôles. Dans le contexte des API, par exemple, un rôle de développeur pourrait avoir la permission de créer et de mettre à jour des ressources d'API, tandis qu'un rôle d'utilisateur final pourrait uniquement avoir la permission de lire ou d'exécuter des ressources d'API.

En gros, le RBAC attribue des permissions basées sur les rôles des utilisateurs, tandis que l'ABAC attribue des permissions basées sur les attributs associés aux utilisateurs et aux ressources.

Dans le RBAC, une politique est définie par la combinaison du rôle assigné à un utilisateur, des actions qu'il est autorisé à effectuer, et des ressources sur lesquelles il peut effectuer ces actions.

Qu'est-ce que l'OPA ?

OPA (Open Policy Agent) est un moteur de politique et un ensemble d'outils qui fournissent une approche unifiée pour l'application des politiques à travers un système distribué entier. Il vous permet de définir, gérer et appliquer des politiques de manière centralisée à partir d'un seul point. En définissant les politiques sous forme de code, OPA facilite la révision, l'édition et le retour en arrière des politiques, permettant une gestion efficace des politiques.

Exemple d'image d'Open Policy Agent

OPA fournit un langage déclaratif appelé Rego, qui vous permet de créer et d'appliquer des politiques à travers votre pile. Lorsque vous demandez une décision de politique à OPA, il utilise les règles et les données que vous avez fournies dans un fichier .rego pour évaluer la requête et produire une réponse. Le résultat de la requête vous est ensuite renvoyé comme décision de politique. OPA stocke toutes les politiques et les données dont il a besoin dans son cache en mémoire. Par conséquent, OPA renvoie des résultats rapidement. Voici un exemple d'un simple fichier Rego OPA :

package example

default allow = false

allow {
    input.method == "GET"
    input.path =="/api/resource"
    input.user.role == "admin"
}

Dans cet exemple, nous avons un package appelé "example" qui définit une règle appelée "allow". La règle "allow" spécifie que la requête est autorisée si la méthode d'entrée est "GET", le chemin demandé est /api/resource, et le rôle de l'utilisateur est "admin". Si ces conditions sont remplies, alors la règle "allow" sera évaluée comme "true", permettant à la requête de continuer.

Pourquoi utiliser OPA et une passerelle API pour le RBAC ?

Une passerelle API fournit un emplacement centralisé pour configurer et gérer les API et les consommateurs d'API. Elle peut être utilisée comme une passerelle d'authentification centralisée en évitant que chaque service individuel implémente la logique d'authentification dans le service lui-même. D'autre part, OPA ajoute une couche d'autorisation et découple la politique du code en créant un avantage distinct pour l'autorisation. Avec cette combinaison, vous pouvez ajouter des permissions pour une ressource d'API à un rôle. Les utilisateurs peuvent être associés à un ou plusieurs rôles. Chaque rôle d'utilisateur définit un ensemble de permissions (GET, PUT, DELETE) sur des ressources RBAC (définies par des chemins d'URI). Dans la section suivante, apprenons comment atteindre le RBAC en utilisant ces deux éléments.

Open Policy Agent avec la passerelle API Apache APISIX

Comment implémenter le RBAC avec OPA et Apache APISIX

Dans Apache APISIX, vous pouvez configurer des routes et des plugins pour définir le comportement de votre API. Vous pouvez utiliser le plugin opa d'APISIX pour appliquer des politiques RBAC en redirigeant les requêtes vers OPA pour la prise de décision. Ensuite, OPA prend une décision d'autorisation en fonction des rôles et des permissions des utilisateurs en temps réel.

Supposons que nous avons une API de conférence où vous pouvez récupérer/modifier des sessions d'événements, des sujets et des informations sur les intervenants. Un intervenant ne peut que lire ses propres sessions et sujets, tandis que l'administrateur peut ajouter/modifier plus de sessions et de sujets. Ou les participants peuvent laisser leurs commentaires sur la session d'un intervenant via une requête POST à /speaker/speakerId/session/feedback et l'intervenant ne peut voir que par une requête GET de la même URI. Le diagramme ci-dessous illustre l'ensemble du scénario :

Open Policy Agent avec Apache APISIX

  1. Le consommateur d'API demande une route sur la passerelle API avec ses informations d'identification telles qu'un token JWT dans l'en-tête d'autorisation.
  2. La passerelle API envoie les données du consommateur avec un en-tête JWT au moteur OPA.
  3. OPA évalue si le consommateur a le droit d'accéder à la ressource en utilisant les politiques (rôles et permissions) que nous spécifions dans le fichier .rego.
  4. Si la décision d'OPA est autorisée, alors la requête sera redirigée vers le service en amont de la conférence.

Ensuite, nous installons, configurons APISIX et définissons les politiques dans OPA.

Prérequis

  • Docker est utilisé pour installer etcd et APISIX conteneurisés.
  • curl est utilisé pour envoyer des requêtes à l'API Admin d'APISIX. Vous pouvez également utiliser des outils tels que Postman pour interagir avec l'API.

Étape 1 : Installer Apache APISIX

APISIX peut être facilement installé et démarré avec le script de démarrage rapide suivant :

curl -sL https://run.api7.ai/apisix/quickstart | sh

Étape 2 : Configurer le service backend (upstream)

Pour rediriger les requêtes vers le service backend de l'API de conférence, vous devrez le configurer en ajoutant un serveur upstream dans Apache APISIX via l'API Admin.

curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -X PUT -d '
{
   "name":"Conferences API upstream",
   "desc":"Register Conferences API as the upstream",
   "type":"roundrobin",
   "scheme":"https",
   "nodes":{
      "conferenceapi.azurewebsites.net:443":1
   }
}'

Étape 3 : Créer un consommateur d'API

Ensuite, nous créons un consommateur (un nouvel intervenant) avec le nom d'utilisateur jack dans Apache APISIX. Il configure le plugin jwt-auth pour le consommateur avec la clé et le secret spécifiés. Cela permettra au consommateur de s'authentifier en utilisant un JSON Web Token (JWT).

curl http://127.0.0.1:9180/apisix/admin/consumers -X PUT -d '
{
    "username": "jack",
    "plugins": {
        "jwt-auth": {
            "key": "user-key",
            "secret": "my-secret-key"
        }
    }
}'

Étape 4 : Créer un point de terminaison public pour générer un token JWT

Vous devez également configurer une nouvelle Route qui génère et signe le token en utilisant le plugin public-api. Dans ce scénario, la passerelle API agit comme un serveur de fournisseur d'identité pour créer et vérifier le token avec la clé de notre consommateur jack. Le fournisseur d'identité peut également être un autre service tiers tel que Google, Okta, Keycloak, et Ory Hydra.

curl http://127.0.0.1:9180/apisix/admin/routes/jas -X PUT -d '
{
    "uri": "/apisix/plugin/jwt/sign",
    "plugins": {
        "public-api": {}
    }
}'

Étape 5 : Réclamer un nouveau token JWT pour le consommateur d'API

Maintenant, nous pouvons obtenir un nouveau token pour notre intervenant Jack à partir de la passerelle API en utilisant le point de terminaison public que nous avons créé. La commande curl suivante génère un nouveau token avec les informations d'identification de Jack et attribue un rôle et une permission dans le payload.

curl -G --data-urlencode 'payload={"role":"speaker","permission":"read"}' http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key -i

Après avoir exécuté la commande ci-dessus, vous recevrez un token en réponse. Enregistrez ce token quelque part, nous allons l'utiliser plus tard pour accéder à notre nouveau point de terminaison de la passerelle API.

Étape 6 : Créer une nouvelle configuration de plugin

Cette étape consiste à configurer les 3 plugins d'APISIX, proxy-rewrite, jwt-auth et opa.

curl "http://127.0.0.1:9180/apisix/admin/plugin_configs/1" -X PUT -d '
{
   "plugins":{
      "jwt-auth":{
      },
      "proxy-rewrite":{
         "host":"conferenceapi.azurewebsites.net"
      }
   }
}'
  • Le plugin proxy-rewrite est configuré pour rediriger les requêtes vers l'hôte conferenceapi.azurewebsites.net.
  • Le plugin d'authentification OPA est configuré pour utiliser le moteur de politique OPA fonctionnant à http://localhost:8181/v1/data/rbacExample. De plus, APISIX envoie toutes les informations relatives au consommateur à OPA. Nous ajouterons ce fichier de politique .rego dans la section de configuration d'Opa.

Étape 7 : Créer une Route pour les sessions de conférence

La dernière étape consiste à créer une nouvelle route pour les sessions des intervenants de l'API de conférence :

curl "http://127.0.0.1:9180/apisix/admin/routes/1" -X PUT -d '
{
    "name":"Conferences API speaker sessions route",
    "desc":"Create a new route in APISIX for the Conferences API speaker sessions",
    "methods": ["GET", "POST"],
    "uris": ["/speaker/*/topics","/speaker/*/sessions"],
    "upstream_id":"1",
    "plugin_config_id":1
}'

Le payload contient des informations sur la route, telles que son nom, sa description, les méthodes, les URIs, l'ID de l'upstream et l'ID de la configuration du plugin. Dans ce cas, la route est configurée pour gérer les requêtes GET et POST pour deux URIs différents, /speaker/topics et /speaker/sessions. Le champ "upstream_id" spécifie l'ID du service upstream qui gérera les requêtes entrantes pour cette route, tandis que le champ "plugin_config_id" spécifie l'ID de la configuration du plugin à utiliser pour cette route.

Étape 8 : Tester la configuration sans OPA

Jusqu'à présent, nous avons configuré tout ce qui est nécessaire pour qu'APISIX dirige les requêtes entrantes vers les points de terminaison de l'API de conférence, en n'autorisant que les consommateurs d'API autorisés. Maintenant, chaque fois qu'un consommateur d'API souhaite accéder à un point de terminaison, il doit fournir un token JWT pour récupérer les données du service backend de la conférence. Vous pouvez vérifier cela en accédant au point de terminaison et l'adresse de domaine que nous demandons maintenant est notre passerelle API personnalisée et non le service de conférence réel :

curl -i http://127.0.0.1:9080/speaker/1/topics -H 'Authorization: {API_CONSUMER_TOKEN}'

Étape 9 : Exécuter le service OPA

Les deux autres étapes consistent à exécuter le service OPA en utilisant Docker et à télécharger notre définition de politique en utilisant son API, qui peut être utilisée pour évaluer les politiques d'autorisation pour les requêtes entrantes.

docker run -d --network=apisix-quickstart-net --name opa -p 8181:8181 openpolicyagent/opa:latest run -s

Cette commande Docker exécute un conteneur de l'image OPA avec la dernière version. Il crée un nouveau conteneur sur le réseau existant d'APISIX apisix-quickstart-net avec le nom opa et expose le port 8181. Ainsi, APISIX peut envoyer des requêtes de vérification de politique à OPA directement en utilisant l'adresse [http://opa:8181](http://opa:8181) Notez qu'OPA et APISIX doivent fonctionner dans le même réseau Docker.

Étape 10 : Définir et enregistrer la politique

La deuxième étape côté OPA consiste à définir les politiques qui seront utilisées pour contrôler l'accès aux ressources de l'API. Ces politiques doivent définir les attributs requis pour l'accès (Quels utilisateurs ont quels rôles) et les permissions (Quels rôles ont quelles permissions) qui sont autorisés ou refusés en fonction de ces attributs. Par exemple, dans la configuration ci-dessous, nous disons à OPA de vérifier dans la table user_roles quel est le rôle de jack. Cette information est envoyée par APISIX dans input.consumer.username. De plus, nous vérifions la permission du consommateur en lisant le payload JWT et en extrayant token.payload.permission de là. Les commentaires décrivent clairement les étapes.

curl -X PUT '127.0.0.1:8181/v1/policies/rbacExample' \
    -H 'Content-Type: text/plain' \
    -d 'package rbacExample

# Assignation des rôles utilisateurs

user_roles := {
    "jack": ["speaker"],
    "bobur":["admin"]
}

# Assignation des permissions des rôles
role_permissions := {
    "speaker": [{"permission": "read"}],
    "admin":   [{"permission": "read"}, {"permission": "write"}]
}

# Fonctions JWT d'aide
bearer_token := t {
 t := input.request.headers.authorization
}

# Décoder le token d'autorisation pour obtenir un rôle et une permission
token = {"payload": payload} {
 [_, payload, _] := io.jwt.decode(bearer_token)
}

# Logique qui implémente le RBAC
default allow = false

allow {
    # Rechercher la liste des rôles pour l'utilisateur
    roles := user_roles[input.consumer.username]

    # Pour chaque rôle dans cette liste
    r := roles[_]

    # Rechercher la liste des permissions pour le rôle r
    permissions := role_permissions[r]

    # Pour chaque permission
    p := permissions[_]

    # Vérifier si la permission accordée à r correspond à la demande de l'utilisateur
    p == {"permission": token.payload.permission}
}'

Étape 11 : Mettre à jour la configuration de plugin existante avec le plugin OPA

Une fois que nous avons défini les politiques sur le service OPA, nous devons mettre à jour la configuration de plugin existante pour la route afin d'utiliser le plugin OPA. Nous spécifions dans l'attribut policy du plugin OPA.

curl "http://127.0.0.1:9180/apisix/admin/plugin_configs/1" -X PATCH -d '
{
   "plugins":{
      "opa":{
         "host":"http://opa:8181",
         "policy":"rbacExample",
         "with_consumer":true
      }
   }
}'

Étape 12 : Tester la configuration avec OPA

Maintenant, nous pouvons tester toutes les configurations que nous avons faites avec les politiques OPA. Si vous essayez d'exécuter la même commande curl pour accéder au point de terminaison de la passerelle API, il vérifie d'abord le token JWT comme processus d'authentification et envoie les données du consommateur et du token JWT à OPA pour vérifier le rôle et la permission comme processus d'autorisation. Toute requête sans token JWT ou avec des rôles non autorisés sera refusée.

curl -i http://127.0.0.1:9080/speaker/1/topics -H 'Authorization: {API_CONSUMER_TOKEN}'

Conclusion

Dans cet article, nous avons appris comment implémenter le RBAC avec OPA et Apache APISIX. Nous avons défini une logique de politique personnalisée simple pour autoriser/interdire l'accès aux ressources de l'API en fonction du rôle et des permissions de notre consommateur d'API. De plus, ce tutoriel a démontré comment nous pouvons extraire les informations relatives au consommateur d'API dans le fichier de politique à partir du payload du token JWT ou de l'objet consommateur envoyé par APISIX.

Ressources connexes

Contenu recommandé

Tags: