Sparrow: un acte de piraterie sur LDAP

 Si vous cherchez pour vos maquettes un service d’identité SCIM complet et protégé par OAuth 2.0 vous pouvez embarquer Sparrow.

 

Refonder LDAP sur un service d’identité :

LDAP est un héritage du 20ème siècle et des grandes normes ISO X400 et X500. C’est donc une vieille dame dont il est question. Bien que révisée au 21ème siècle dans sa version v3, nous sommes encore sur des principes d’informatique distribuée reposant sur du client / serveur.

Il était temps de refonder ce service d’annuaire.

Le travail de Kiran Ayyagari avec Sparrow est une complète réécriture du service en Go. Mais il ne garde de LDAP que la notion de session (bind, unbind), recherche d’objet utilisateur et changement de mot de passe.

De fait il ne supporte pas tout un tas de fonction comme le Referral, l’Update … Pour la gestion des objets, il met à disposition une API basée sur SCIM. Cette refondation sur un service d’identité pur est la principale innovation de ce projet.

Sparrow gère trois type d’entité :

  • Des utilisateurs
  • Des groupes
  • Des objets

Toute la logique de schéma repose sur des fichiers JSON, par exemple un enregistrement d’identité avec ses attributs :

 

{
    "schemas":[
        "urn:ietf:params:scim:schemas:core:2.0:User"
    ],
    "userName":"bjensen",
    "externalId":"bjensen",
    "name":{
        "formatted":"Ms. Barbara J Jensen III",
        "familyName":"Jensen",
        "givenName":"Barbara"
    }
}

Sparrow est donc fondamentalement un serveur SCIM exposant quelques fonctions en mode LDAP pour des utilisateurs et des groupes.

Ce serveur est multi domaines, ce qui permet de gérer plusieurs natures de populations disjointes.

 

Identifier vs Autoriser :

Dans les concepts d’OpenId Connect il est assez difficile de faire la part de ce qui revient au serveur d’autorisation et au fournisseur d’identité.

OpenID Connect implements authentication as an extension to the OAuth 2.0 authorization process. Use of this extension is requested by Clients by including the openid scope value in the Authorization Request.

Donc j’obtiens des informations d’identification au travers d’une autorisation sur un objet qui me demande une authentification. Il faut suivre 😉

Au moins avec Sparrow c’est plus simple c’est un serveur d’autorisation OAuth 2.0 qui permet l’enregistrement dynamique des clients et supporte l’Authorization code flow. De fait c’est même trop simple de devenir Client du serveur, il suffit d’un compte sur l’annuaire.

Toute demande sur une ressource SCIM passe par un client qui aura obtenu une autorisation sur les données. Sparrow dispose d’un modèle de rôle RBAC basique (pas de hiérarchie) qui est associée à la session et permet de contrôler les droits de modification, création et suppression. Dans le code ci- dessous, on notera que seul un administrateur ou le propriétaire de la données peut modifier cette dernière.

//http.go func createResource(hc *httpContext)
// Cet appel au fournisseur de données (Provider) peut retourner une erreur
 createCtx := base.CreateContext{InRes: rs, OpContext: hc.OpContext}
 insertedRs, err := hc.pr.CreateResource(&createCtx)
 if err != nil {
  writeError(hc.w, err)
 return
}

//Provider.go
//Cette fonction évalue l’autorisation Rbac de l’utilisateur
func (prv *Provider) CreateResource(crCtx *base.CreateContext) (res *base.Resource, err error) {
 if !crCtx.Session.IsAllowCreate() {
 return nil, base.NewForbiddenError("Insufficent privileges to create a resource")
 }
 return prv.sl.Insert(crCtx.InRes)
}
//fonction de modification des données limitée à l'administrateur ou le propriétaire
func (prv *Provider) Replace(replaceCtx *base.ReplaceContext) (res *base.Resource, err error) {
 // allow an admin always, but normal user when the resource belongs to self
  if !replaceCtx.Session.IsAllowUpdate() && (replaceCtx.Rid != replaceCtx.Session.Sub) {
   return nil, base.NewForbiddenError("Insufficent privileges to replace the resource")
  }
  return prv.sl.Replace(replaceCtx.InRes, replaceCtx.IfNoneMatch)
}

 

Les « dessous de la mariée » :

La force d’un annuaire repose sur sa capacité à gérer des données. Pour cela Kiran à fait le choix de github.com/coreos/bbolt un fork de bolt, lui-même hérité de LMDB. En gros un dictionnaire de données dans un seul fichier avec un index de type B+tree. Cela assure de bonnes performances en lecture, un peu moins en écriture.

Pour classer les données, on utilise la bonne vieille méthode: “une table (bucket) par objet et et chaque attribut sa table (bucket)”. Le système n’est pas relationnel et c’est à l’application de faire les boucles de recherche pour trouver l’ensemble des informations. Chaque domaine de données dispose de sa base (ou silo dans la conception de Sparrow) et de sa persistance des jetons OAuth.

Sparrow ne propose pas encore de système de réplication comme pour un annuaire LDAP standard. De plus une base bbolt ne supporte pas plusieurs accès en écriture, ni plusieurs ouverture en mode écriture. Cela rend donc problématique le partage de fichier entre plusieurs instance de Sparrow. C’est clairement une limitation de la version actuelle. Kiran a prévu de développer cette réplication Multi-Maître.

Personnellement je pense qu’il aurait pu tirer parti de fonction système comme HDFS ou des bases qui fonctionnent déjà en cluster comme etcd par exemple. Si cette option vous tente, il faut simplement modifier la “classe” silo et mettre en place une nouvelle gestion de données.

3 captures d’écran fait avec boltdbweb:

  • Les tables (bucket) contenant des attributs (ici les objets)
  • La table des attributs mail (une seule clé pas de valeur)
  • La table des membres de groupe (le premier est l’administrateur)

 

Sparrow est un petit projet, mais avec une vision claire de ce que doit être un service d’identité au 21ème siècle.


Stéphane
Architecte Sécurité

  • Google Plus
  • LinkedIn
  • Viadeo