Modéliser un contrôle d’accès basé sur les attributs avec OPA

Magazine
Marque
SysOps Pratique
Numéro
147
Mois de parution
janvier 2025
Spécialité(s)


Résumé

Le contrôle d’accès est un mécanisme essentiel au fonctionnement des systèmes d’exploitation, d’autant plus dans la mesure où ils sont pour la plupart multi-utilisateurs. Dans cet article, nous allons introduire le framework OPA par l’exemple d’une modélisation d’ABAC, un contrôle d’accès offrant une gestion dynamique des droits pour à peu près n’importe quelles entités.


Body

Le contrôle d’accès réglemente les actions permises entre différentes entités. Une entité peut être un sujet ou un objet selon qu’il effectue ou subit l’action. C’est ce qui se produit lorsqu’un processus tente d’accéder à un fichier. Le processus se confronte à une matrice de contrôle d’accès nommée ACL (Access Control List) contenant des ACE (Access Control Entry). La même entité peut être tour à tour sujet et objet. Par exemple, lorsqu’un administrateur change le niveau d’accréditation d’un utilisateur. Le sujet est l’administrateur et l’objet est l’utilisateur. Ensuite, l’utilisateur se comportera comme un sujet lorsqu’il accédera aux différents objets de son système muni de ses nouveaux accès. Nous commencerons avec un état des lieux des contrôles d’accès pour introduire le fonctionnement d’ABAC (Attribute Based Access Control). Ensuite, nous présenterons OPA (Open Policy Agent) qui est le moteur de décision des politiques de sécurité. Nous poursuivrons en exhibant une implémentation d’ABAC se basant sur OPA pour contrôler les interactions sur les structures de données d’un code Python. Enfin, nous proposerons une solution de déploiement de notre architecture sous forme de conteneurs.

1. Les contrôles d’accès en (très) bref

Il existe une quantité phénoménale de contrôle d’accès en circulation dans le monde en informatique. Nous n’allons discuter que de ceux permettant de donner les éléments de compréhension de la philosophie d’ABAC. Le premier contrôle d’accès que tout le monde connaît (et subit) est le modèle IBAC (Identity Based Access Control). Dans ce modèle, pour chaque action qu’un sujet peut réaliser sur un objet, il existe une ACE correspondante. La matrice de contrôle d’accès peut donc devenir assez conséquente. De plus, chaque ajout d’entité dans le système nécessite une mise à jour de cette matrice. C’est par exemple la raison d’être des règles d’héritage de propriétaire et du umask dans le modèle discrétionnaire UGO (User Group Other) utilisé par les systèmes de fichiers Linux. Lorsqu’un nouveau fichier est créé, il nécessite des accès par défaut pour s’insérer dans la matrice. L’utilisateur peut ensuite changer ces accès du fait que UGO est discrétionnaire (c’est-à-dire que les droits sur les objets sont laissés à la discrétion du propriétaire). Nous noterons que la notion de groupe permet, dans une certaine mesure, de limiter la taille de la matrice, mais le problème de 1 nouvelle entité = 1 ajout de N ACE subsiste.

Le RBAC (Role Based Access Control) propose aux sujets d’embrasser un rôle avant de réaliser une interaction. C’est la modélisation de l’expression française « Je mets ma casquette de X ». Dans ce modèle, la matrice de contrôle d’accès est implémentée en considérant des rôles côté sujet. Nous pouvons donc avoir X entités masquées derrière un rôle dans la mesure où ils ont le droit de l’embrasser (ce qui nécessite une autre politique dédiée à la gestion des rôles). Ce modèle rend la matrice plus lisible, car factorisée. Cependant, lorsqu’un utilisateur possède un rôle, il détient l’intégralité des droits associés aux rôles. Ainsi, l’application du principe du moindre privilège peut rapidement multiplier la liste des rôles qu’il faudra étendre pour évaluer les droits réels du sujet.

Le dernier modèle à considérer avant ABAC est DTE (Domain Type Enforcement). Dans ce modèle, les sujets disposent d’un domaine et les objets un type. La matrice de contrôle d’accès définit les droits des domaines sur les types. C’est un contrôle d’accès qui vient du monde du système. C’est pour ça que les domaines et les types sont des singletons. L’évaluation des accès doit être rapide. Le gain de lisibilité est du même ordre que pour RBAC. Chaque sujet est masqué derrière un domaine et chaque objet derrière un type. Cette factorisation de chaque côté de l’ACE améliore grandement la lisibilité de la matrice de contrôle d’accès. Cependant, la même observation que RBAC relative à la gestion du moindre privilège s’applique. SELinux est une implémentation mandataire (c’est-à-dire que même les processus ayant l’identité « root » doivent s’y conformer) de DTE basée sur l’évaluation des appels système invoqués par les sujets en fonction de leurs domaines respectifs.

Le modèle ABAC (Attribute Based Access Control) reprend d’une certaine façon l’idée de DTE en introduisant la notion d’attributs sur les entités. Dans ce modèle, chaque entité possède une liste d’attributs. Dans la plupart des cas, les attributs sont des couples clé / valeur. On pourra également retrouver des singletons, rien n’est réellement précisé dans le guide du NIST sur le sujet [1]. C’est d’ailleurs le côté un peu déroutant d’ABAC. Il n’existe pas d’implémentation référence. C’est à chacun de monter sa trousse à outils dans le cadre présenté par le document référence susmentionné. Le bénéfice de ce contrôle d’accès est que la granularité du contrôle d’accès est plus fine que les modèles DTE et RBAC du fait de l’éclatement des privilèges en attributs en conservant la factorisation de la matrice. En effet, elle n’est mise à jour que si un nouvel attribut est créé. Avec ABAC, la difficulté est plutôt de maintenir la cohérence des accès autorisés par les attributs.

Pour finir, nous notons que pour tous ces modèles, le résultat de l’évaluation de la même ACE peut changer selon son contexte (heure de la journée, emplacement, compte bloqué, etc.). Dans la section suivante, nous allons voir OPA qui est un framework très générique de modélisation de contrôle d’accès.

2. Open Policy Agent (OPA)

OPA est un logiciel libre permettant de modéliser un grand nombre de contrôles d’accès. C’est une pâte à modeler que nous allons façonner pour modéliser notre contrôle accès ABAC sur les structures de données d’un code Python. Cette section est une introduction à OPA pour en saisir le fonctionnement. OPA prend en compte une politique de contrôle d’accès rédigée au format Rego. OPA s’appuie sur Flask pour recevoir les demandes de contrôle d’accès des programmes clients via une API REST (REpresentational State Transfer). Lorsqu'une requête est envoyée à OPA, elle est comparée aux règles de politique pour déterminer si l'action demandée est autorisée ou non. Commençons par installer OPA. Nous prenons la version compilée statiquement, de cette façon nous pouvons poser l’exécutable directement dans notre répertoire utilisateur sans gérer de quelconques dépendances :

$ wget https://github.com/open-policy-agent/opa/releases/download/v0.69.0/opa_linux_amd64_static

Ajoutons les droits d’exécution :

$ chmod a+x opa_linux_amd64_static

Enfin, exécutons-le sur l’interface de rebouclage en lui passant la politique Rego définie ci-dessous :

$ ./opa_linux_amd64_static run --server --addr localhost:8181 ./policy.rego
{"addrs":["localhost:8181"],"diagnostic-addrs":[],"level":"info","msg":"Initializing server.","time":"2024-10-27T10:30:49+01:00"}

La réponse d'OPA est généralement un booléen (true ou false) ou un objet JSON (JavaScript Object Notation) plus complexe selon la politique définie. Voici une politique Rego basique :

# policy.rego
package policy
 
default allow = false
 
allow {
        test_niveau
}
 
test_niveau {
        input.subj.niveau >= input.obj.niveau
}

Dans cet exemple, nous définissons une politique allow par défaut à false, c’est-à-dire que par défaut l’action demandée est interdite. Nous voyons que dans la section définissant allow il existe une fonction test_niveau. Dans le code de celle-ci, nous voyons que la condition testée est de savoir si le niveau du sujet est supérieur ou égal à celui de l’objet. Éprouvons maintenant cette politique avec curl :

$ curl -X POST http://localhost:8181/v1/data/policy/allow -d '{ "input": { "subj" : { "niveau" : 7 }, "obj" : { "niveau" : 8 } } }'
{"result":false}

Cette requête contacte le serveur OPA et lui demande d’évaluer les paramètres encodés en JSON passés en brut par le paramètre ‘-d’. Il s’agit des niveaux de confidentialité du sujet et de l’objet. Ces paramètres sont passés à la fonction allow du package policy défini en en-tête du fichier Rego. Nous voyons que le résultat est false, car le niveau du sujet est de 7 et celui de l’objet de 10. Faisons varier le niveau du sujet pour le mettre à 10 :

$ curl -X POST http://localhost:8181/v1/data/policy/allow -d '{ "input": { "subj" : { "niveau" : 10 }, "obj" : { "niveau" : 8 } } }'
{"result":true}

L’exemple ci-dessus est assez naïf, mais permet de bien comprendre le fonctionnement d’OPA. Nous allons monter en puissance en proposant un code Python s’appuyant sur le package Requests [2]. Notre code comprend deux variables « sujet » et « objet » qui sont deux dict, c’est-à-dire une liste d’attributs clé / valeur. Pour le test, il n’y a qu’un seul attribut, mais évidemment on peut en mettre autant que l’on veut. Nous concaténons ces deux dict dans un un autre dict input que nous encodons en JSON. Enfin, nous nous appuyons ensuite sur la librairie requests pour interroger le serveur Flask d’OPA. Voici le code :

# app.py
import requests
import json
 
if __name__ == '__main__':
 
        opa_url = "http://localhost:8181/v1/data/policy/allow"
        sujet = { 'niveau' : 7 }
        objet = { 'niveau' : 10 }
 
        input = { 'input' : { 'subj' : { 'niveau' : sujet['niveau'] } , 'obj' : { 'niveau' : objet['niveau'] } } }
        json_input = json.dumps(input, indent = 4)
        response = requests.post(opa_url, data=json_input)
        clean_response = response.content.strip().decode( "utf-8" )
        print(clean_response)

Exécutons-le :

opatest@debian:~$ python3 app.py
{"result":false}

Dans cette section, nous avons vu un exemple basique d’implémentation d’ABAC. Cependant, dans un « vrai » déploiement d’ABAC, il est inconcevable de modifier le code à chaque fois que l’on modifie les attributs associés à une entité. Dans la section suivante, nous donnerons quelques pistes pour rendre les choses plus dynamiques.

3. Persistance et déploiement d’ABAC

Dans cette section, nous allons voir comment rendre les données de notre cas test indépendantes du code et aussi comment le déployer dans des conteneurs Docker. Cette section n’a pas pour but de détailler les étapes, mais plus de donner au lecteur des pistes sur la direction d’un développement dans ce sens.

3.1 Persistance des attributs d’entités

L’enjeu d’ABAC est de déterminer comment stocker les attributs des différentes entités. Pour donner quelques idées au lecteur, nous allons évoquer plusieurs suggestions. La première chose à faire est de bien déterminer les limites de chaque composant de votre infrastructure. Si vous vous appuyez sur un annuaire LDAP pour vos utilisateurs, vous pouvez utiliser un schéma pour stocker les attributs à la façon de Authzforce [3]. SI vous avez besoin de stocker des attributs pour une application, une base de données telle que MongoDB embarquée avec l’application peut être un bon choix. Nous allons faire évoluer notre application pour qu’elle stocke les attributs du sujet et de l’objet dans MongoDB. Pour cela, nous nous appuierons sur le package Python Pymongo qui intègre les fonctions Python permettant de requêter une base MongoDB [4]. Créons la base de données et ajoutons les attributs de nos entités dans une collection dédiée :

$ mongosh --eval 'use db_test'
$ mongoimport --db db_test --collection attributs --file data.json

Avec le fichier data.json suivant :

# data.json
{ "nom" : "sujet", "niveau": 7 }
{ "nom" : "objet", "niveau": 10 }

Modifions notre code pour qu’il aille chercher ses attributs dans la base MongoDB. Nous voyons que lorsque nous détachons le stockage des attributs de l’application il est nécessaire de pouvoir rattacher le jeu d’attribut à la structure de données dans le code. Nous introduisons donc un attribut « nom » en plus de « niveau ». Nous mettrons cet attribut à la valeur sujet pour le sujet et objet pour l’objet. Voici comment récupérer les attributs dans notre code :

# collection.py
from pymongo import MongoClient
 
if __name__ == "__main__":
 
        client = MongoClient('mongodb://localhost:27017/')
        db = client['db_test']
        collection = db['attributs']
        for document in collection.find():
                print(document)

Exécutons le code :

$ python3 collection.py
{'_id': ObjectId('671e3b9758455cf02f32c89c'), 'name': 'sujet', 'niveau': 7}
{'_id': ObjectId('671e3b9758455cf02f32c89d'), 'name': 'objet', 'niveau': 10}

Notre code est bien connecté à la base de données. Pour la suite, il suffit d’interpréter la variable « document » du code d’exemple avec un parser JSON pour récupérer les attributs. À l’issue de cette étape, les données et le code sont séparés. Nous pouvons maintenant encapsuler notre cas test dans des conteneurs pour un déploiement en mode Cloud.

3.2 Déploiement dans des conteneurs

Dans cette dernière section, nous proposons un déploiement de notre infrastructure ABAC à la mode des microservices, c’est-à-dire en utilisant des conteneurs pour séparer les privilèges. Dans notre infrastructure, nous avons trois composants : l’application nécessitant un contrôle d’accès, OPA et le service MongoDB. Les deux derniers composants doivent absolument s’attacher à un volume afin que la politique Rego et la base de données des attributs soient persistantes. Commençons par le Dockerfile de notre application :

#app.Dockerfile
FROM python:3.8
RUN mkdir /app
COPY entity_api.py /app/

Ce Dockerfile est extrêmement simple, il se base sur une image Python et ajoute le code de notre application de test dans le répertoire /app. Le second Dockerfile embarque OPA :

#opa.Dockerfile
FROM openpolicyagent/opa:latest
COPY policy.rego /policy/policy.rego
CMD ["run", "--server", "/policy/policy.rego"]

La construction de cette image est un peu plus complexe. Elle se base sur l’image fournie par OPA. Nous y copions la politique de contrôle d’accès Rego dans le répertoire policy. Enfin, nous démarrons le serveur Flask exécutant OPA lors de l’instanciation du conteneur. Pour le dernier composant, rien de particulier. Nous utiliserons l’image MongoDB standard du Docker Hub. Il ne nous reste plus qu’à orchestrer tout ça dans un fichier Docker Compose :

#copose.yml
services:
  app:
    build:
      context: ./app
    environment:
      - MONGODB_URL=mongodb://mongodb:27017/
 
  opa:
    build:
      context: ./opa
    volumes:
      - policy_volume:/policy
 
  mongodb:
    image: mongo:latest
    volumes:
      - mongo_data:/data/db
 
volumes:
  mongo_data:
  policy_volume:

Dans ce fichier Compose, nous instancions nos trois conteneurs (le conteneur app aurait pu rester en dehors si on expose le port de MongoDB). On notera que les conteneurs opa et mongodb utilisent des volumes afin de stocker les fichiers de données définis dans les Dockerfile respectivement la politique de sécurité Rego et la base de données MongoDB.

Conclusion

Dans cet article, nous avons vu comment mettre en œuvre un contrôle d’accès basé sur les attributs au sein d’une application. Au vu des mécaniques mises en œuvre dans l’article, nous pensons avoir montré au lecteur que ce contrôle d’accès modélisé à l’aide d’OPA était particulièrement intéressant dans un contexte d’application dans le Cloud comme le montre son adoption par les géants du Cloud qui proposent des implémentations d’ABAC intégrées à leurs services [5, 6]. En effet, l’utilisation d’API REST sur HTTP et les communications induites par le découpage en microservices nécessaire pour garantir la séparation de privilèges dans un contexte d’application exposée font de ce contrôle d’accès un mauvais candidat pour du bas niveau. C’est carrément l’autre côté de l’échiquier par rapport à des choses comme les LSM (Linux Security Module) intégrées au noyau Linux.

Références

[1] Guide d’implémentation d’ABAC : https://csrc.nist.gov/pubs/sp/800/162/upd2/final

[2] Python Requests : https://fr.python-requests.org/en/latest/

[3] Authzforce : https://authzforce.ow2.org/

[4] Pymongo : https://www.mongodb.com/docs/languages/python/pymongo-driver/current/

[5] Azure ABAC : https://learn.microsoft.com/fr-fr/azure/role-based-access-control/conditions-overview

[6] AWS ABAC : https://docs.aws.amazon.com/fr_fr/IAM/latest/UserGuide/introduction_attribute-based-access-control.html



Article rédigé par

Par le(s) même(s) auteur(s)

Intégration d’ownCloud pour servir un partage de fichiers existant

Magazine
Marque
SysOps Pratique
Numéro
136
Mois de parution
mars 2023
Spécialité(s)
Résumé

Le logiciel libre ownCloud est une solution de partage et de stockage des fichiers en mode web. Avec la profusion des plateformes commerciales de ce type (Google Drive, iCloud, Dropbox, etc.), l’accès web aux fichiers est devenu le nouveau standard. Dans cet article, nous allons explorer une méthode pour interfacer cette méthode d’accès aux fichiers avec un serveur NFS (Network File System) existant.

Équilibrage de charge avec IPVS

Magazine
Marque
SysOps Pratique
HS n°
Numéro
55
Mois de parution
octobre 2022
Spécialité(s)
Résumé

IP Virtual Server (IPVS) est un équilibreur de charge agissant au niveau 4 du modèle OSI. Il est implémenté sous forme d’un module noyau s’appuyant sur le framework Netfilter, ce qui le rend efficace sur l’équilibrage des services par rapport à leurs ports TCP/UDP, mais totalement agnostique aux protocoles applicatifs transportés (LDAP, HTTP, etc.).

Libre-service de machines virtuelles avec OpenNebula

Magazine
Marque
SysOps Pratique
Numéro
130
Mois de parution
mars 2022
Spécialité(s)
Résumé

Dans un article précédent, nous avons mis en place une infrastructure OpenNebula basique [1]. Nous allons maintenant configurer cette plateforme pour proposer aux utilisateurs un guichet de libre-service de machines virtuelles. L’idée est que les utilisateurs puissent créer eux-mêmes leurs machines virtuelles.

Les derniers articles Premiums

Les derniers articles Premium

Bun.js : l’alternative à Node.js pour un développement plus rapide

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Dans l’univers du développement backend, Node.js domine depuis plus de dix ans. Mais un nouveau concurrent fait de plus en plus parler de lui, il s’agit de Bun.js. Ce runtime se distingue par ses performances améliorées, sa grande simplicité et une expérience développeur repensée. Peut-il rivaliser avec Node.js et changer les standards du développement JavaScript ?

PostgreSQL au centre de votre SI avec PostgREST

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Dans un système d’information, il devient de plus en plus important d’avoir la possibilité d’échanger des données entre applications. Ce passage au stade de l’interopérabilité est généralement confié à des services web autorisant la mise en œuvre d’un couplage faible entre composants. C’est justement ce que permet de faire PostgREST pour les bases de données PostgreSQL.

La place de l’Intelligence Artificielle dans les entreprises

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

L’intelligence artificielle est en train de redéfinir le paysage professionnel. De l’automatisation des tâches répétitives à la cybersécurité, en passant par l’analyse des données, l’IA s’immisce dans tous les aspects de l’entreprise moderne. Toutefois, cette révolution technologique soulève des questions éthiques et sociétales, notamment sur l’avenir des emplois. Cet article se penche sur l’évolution de l’IA, ses applications variées, et les enjeux qu’elle engendre dans le monde du travail.

Petit guide d’outils open source pour le télétravail

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Ah le Covid ! Si en cette période de nombreux cas resurgissent, ce n’est rien comparé aux vagues que nous avons connues en 2020 et 2021. Ce fléau a contraint une large partie de la population à faire ce que tout le monde connaît sous le nom de télétravail. Nous avons dû changer nos habitudes et avons dû apprendre à utiliser de nombreux outils collaboratifs, de visioconférence, etc., dont tout le monde n’était pas habitué. Dans cet article, nous passons en revue quelques outils open source utiles pour le travail à la maison. En effet, pour les adeptes du costume en haut et du pyjama en bas, la communauté open source s’est démenée pour proposer des alternatives aux outils propriétaires et payants.

Les listes de lecture

11 article(s) - ajoutée le 01/07/2020
Clé de voûte d'une infrastructure Windows, Active Directory est l'une des cibles les plus appréciées des attaquants. Les articles regroupés dans cette liste vous permettront de découvrir l'état de la menace, les attaques et, bien sûr, les contre-mesures.
8 article(s) - ajoutée le 13/10/2020
Découvrez les méthodologies d'analyse de la sécurité des terminaux mobiles au travers d'exemples concrets sur Android et iOS.
10 article(s) - ajoutée le 13/10/2020
Vous retrouverez ici un ensemble d'articles sur les usages contemporains de la cryptographie (whitebox, courbes elliptiques, embarqué, post-quantique), qu'il s'agisse de rechercher des vulnérabilités ou simplement comprendre les fondamentaux du domaine.
Voir les 136 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous