Cette documentation est obsolète

Mise en oeuvre d'un Attribute Provider Shibboleth utilisant le référentiel de Sympa

Logiciels utilisés et pré-requis

  • Sympa 6.0 ou plus récent
  • IdP Shibboleth 2.4 ou plus récent
  • SP Shibboleth 2.4 ou plus récent

Noms utilisés

Pour ces instructions nous utilisons les noms d'hôte suivants:

  • IdP/Sympa: sympa.example.org
  • SP: sp.example.org

Nous assumons que l'IdP est installé dans le dossier /opt/shibboleth-idp/ par défaut.

Documentation

Architecture mise en oeuvre

L'architecture doit permettre d'étoffer plus simplement les outils de groupware (ou en général un SP protégeant une application de web) associés à un serveur de listes de diffusion. Pour cela le SP Shibboleth doit remonter l'information sur les groupes auxquels appartient l'utilisateur connecté. On évite ainsi un développement spécifique consistant à interroger le serveur SOAP de Sympa.

Le SP Shibboleth va interroger deux IdP :

  1. l'IdP de rattachement de l'utilisateur. L'utilisateur s'authentifie auprès de cet IdP ; certains de ses attributs utilisateur sont transmis au SP (l'attribut mail est requis) ;
  2. l'IdP couplé au serveur de listes. Cet IdP est connecté au référentiel (base SQL) du serveur de listes. Cet IdP est contacté en SOAP, sans interaction avec l'utilisateur. Cette requête permet d'enrichir le profil de l'utilisateur avec les groupes (=listes) auxquels il appartient.

Le serveur de listes devient ainsi un référentiel de gestion de groupes.
Nous utiliserons indifféremment les termes liste et groupe, équivalents.

Schéma de l'architecture

Limites

  • Si votre serveur Sympa gère plusieurs hôtes virtuels, vous devrez configurer votre IdP pour chaque hôte virtuel (sinon l'appartenance aux listes des différents hôtes virtuels seront mélangés) ?
  • Cette solution répond bien aux besoins d'applications qui ont juste besoin de connaître/vérifier les groupes d'appartenance de l'utilisateur connecté. Cependant elle ne permet pas, par exemple, de fournir à une application cliente de Shibboleth, la liste des membres d'un groupe. Pour ce type de besoin, il sera nécessaire d'interroger le serveur de listes (via le protocole SOAP) ou son référentiel.

Configuration de l'IdP Shibboleth

Cet IdP n'est pas utilisé pour la partie authentification, uniquement pour sa fonction AttributeAuthority (autorisation).

Installation d'un connecteur JDBC

Vous devez installer un connecteur JDBC pour permettre à l'IdP de se connecter à la base de données de Sympa. Nous vous donnons la marche à suivre si votre base de données est MySQL.

Site de téléchargement du connecteur MySQL : http://www.mysql.com/downloads/connector/j/

Copiez mysql-connector-java-5.1.25-bin.jar parmi les sources de l'IdP et exécutez le script d'installation de l'IdP Shibboleth :

$ cd  /usr/local/src/shibboleth-identityprovider-2.4.0
$ cp /usr/local/src/mysql-connector-java-5.1.25/mysql-connector-java-5.1.25-bin.jar lib/
$ ./install.sh (répondre 'no' à la question 'Overwrite?')

La nouvelle version de idp.war contiendra les librairies MySQL.

attribute-resolver.xml

Etant donné l'adresse email d'un utilisateur, l'IdP doit retourner la liste des groupes auxquels il est abonné. Pour cela nous utilisons l'attribut isMemberOf du schema eduMember. Nous créons cet attribut, avec une dépendance vis à vis de la source de données sympaDB que nous créons juste après. Comme aucun OID n'est disponible pour cet attribut, nous utiliserons la forme URN pour les transports SAML1 et SAML2.

<resolver:AttributeDefinition id="isMemberOf" xsi:type="ad:Simple" sourceAttributeID="isMemberOf">
  <resolver:Dependency ref="sympaDB" />
  <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:mace:dir:attribute-def:isMemberOf" />
  <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.5.1.1" friendlyName="isMemberOf" />
</resolver:AttributeDefinition>

L'IdP est configuré pour interroger la base de données de sympa ; l'identifiant de l'utilisateur sera l'adresse email.

<resolver:DataConnector id="sympaDB" xsi:type="dc:RelationalDatabase">
  <dc:ApplicationManagedConnection jdbcDriver="com.mysql.jdbc.Driver"
                   jdbcURL="jdbc:mysql://localhost/sympa" 
                   jdbcUserName="sympa" 
                   jdbcPassword="apmys" />
  <dc:QueryTemplate>
    <![CDATA[
      SELECT CONCAT('https://sympa.example.org/sympa/info/', list_subscriber) 
      AS isMemberOf 
      FROM subscriber_table 
      WHERE user_subscriber='$requestContext.principalName'
    ]]>
  </dc:QueryTemplate>
 
  <dc:Column columnName="isMemberOf" attributeID="isMemberOf" />
</resolver:DataConnector>

Dans l'exemple ci-dessus les valeurs pour l'attribut isMemberOf sont composées comme des liens qui pointent vers la page d'accueil de la liste de diffusion correspondante. Mais il serait aussi possible de composer les valeurs comme des adresse email avec une requête SQL comme suit :

SELECT CONCAT(list_subscriber, '@sympa.example.org') 
AS isMemberOf 
FROM subscriber_table 
WHERE user_subscriber='$requestContext.principalName'

Les requêtes du SP utiliseront un nameID (identifiant de session) qui est une adresse email. Il est donc nécessaire de configurer ce type de nameID. Dans la requête SQL ci-dessus on utilise une concaténation pour former une URL pour constituer les valeurs de l'attribut isMemberOf.

<resolver:PrincipalConnector xsi:type="pc:Direct" 
        xmlns="urn:mace:shibboleth:2.0:resolver:pc" id="mail"
        nameIDFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" />

attribute-filter.xml

L'IdP doit être configuré pour transmettre l'attribut isMemberOf au(x) SP qui vont l'interroger.

<AttributeFilterPolicy>
  <PolicyRequirementRule xsi:type="basic:AttributeRequesterRegex" value="https://sp.example.org/.*" />
 
  <AttributeRule attributeID="isMemberOf">
    <PermitValueRule xsi:type="basic:ANY" />
  </AttributeRule>
</AttributeFilterPolicy>

/opt/shibboleth-idp/metadata/idp-metadata.xml

Le SP utilise le binding SAML 2 urn:oasis:names:tc:SAML:2.0:bindings:SOAP pour contacter le service AttributeQuery de l'IdP. Or le formulaire d'enregistrement dans la fédération Education-Recherche ne propose pas la déclaration de ce profil. Il est donc nécessaire de fournir au SP un fichier de méta-données de l'IdP spécifique, incluant ce point d'accès. Depuis la version 2.1.5 l'IdP publie ses propres méta-données, mais elle ne sont pas dynamiques ; il est donc nécessaire de mettre à jour ce fichier de méta-données en fonction de la configuration de l'IdP. Parcourez le fichier de méta-données pour vérifier si tous les éléments sont corrects. Attention à maintenir les URLs d'accès SOAP sur le port 8443 ; nous allons activer cet hôte virtuel Apache ci-dessous mais est aussi possible de seulement utiliser Tomcat (pour l'IdP) sans Apache.

<?xml version="1.0" encoding="UTF-8"?>
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" entityID="https://sympa.example.org/idp/shibboleth">
  <AttributeAuthorityDescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol">
    <Extensions>
      <shibmd:Scope regexp="false">sympa.example.org</shibmd:Scope>
    </Extensions>
    <KeyDescriptor>
      <ds:KeyInfo>
        <ds:X509Data>
          <ds:X509Certificate>
[X.509 Certificate]
          </ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </KeyDescriptor>
    <AttributeService Binding="urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding" Location="https://sympa.example.org:8443/idp/profile/SAML1/SOAP/AttributeQuery"/>
    <AttributeService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sympa.example.org:8443/idp/profile/SAML2/SOAP/AttributeQuery"/>
    <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
  </AttributeAuthorityDescriptor>
</EntityDescriptor>

L’élément IDPSSODescriptor n'est plus nécessaire et peut être supprimé.

L'identifiant de session SAML (NameID) utilisé pour interroger notre IdP (associé au gestionnaire de groupes) sera une adresse email. Il est donc nécessaire d'ajouter ce format de nameID dans ses méta-données, dans l'élément AttributeAuthorityDescriptor.

relying-party.xml

Le SP fait probablement partie d'une fédération (puisqu'il est amené à contacter les IdP d'établissement), donc vous pouvez ajouter la confiance dans les méta-données de la fédération Education-Recherche. On peut également pointer vers les méta-données dynamiques générées par le SP puisque cet IdP ne sera contacté que par un seul SP.

<!-- Metadata du SP de listes -->
<MetadataProvider id="SP-VO" xsi:type="FileBackedHTTPMetadataProvider" xmlns="urn:mace:shibboleth:2.0:metadata" 
      metadataURL="https://sp.example.org/Shibboleth.sso/Metadata"
      backingFile="/opt/shibboleth-idp/metadata/metadata-sp.xml">
  <MetadataFilter xsi:type="ChainingFilter" xmlns="urn:mace:shibboleth:2.0:metadata">
    <MetadataFilter xsi:type="EntityRoleWhiteList" xmlns="urn:mace:shibboleth:2.0:metadata">
      <RetainedRole>samlmd:SPSSODescriptor</RetainedRole>
    </MetadataFilter>
  </MetadataFilter>
</MetadataProvider>

ssl.conf (Apache)

La requête SAML de demande d'attribut provenant du SP sera adressée au service SOAP AttributeQuery de l'IdP. Nous vous recommandons de mettre en oeuvre un hôte virtuel dédié (port 8443) pour les besoins de ce service, ce qui vous permet de configurer cet hôte virtuel pour présenter le même certificat que celui utilisé par la brique IdP Shibboleth (ce certificat est auto-signé, à durée de vie longue). En effet, le SP vérifie le certificat serveur de l'IdP ; il doit correspondre à celui publié dans les méta-données de l'IdP (AttributeAuthorityDescriptor/KeyDescriptor). Il serait hasardeux de publier dans ces méta-données le certificat TCS utilisé par Apache, celui-ci expirant rapidement.

## Définition du VHost 8443 pour accès SOAP à l'AttributeQuery
Listen 8443
 
<VirtualHost sympa.example.org:8443>
DocumentRoot "/var/www/html"
ServerName sympa.example.org:8443
 
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3s:+EXP
 
## On utilise le certificat et la clef privée de l'IdP
SSLCertificateFile /opt/shibboleth-idp/credentials/idp.crt
SSLCertificateKeyFile /opt/shibboleth-idp/credentials/idp.key
 
## Requis pour l'authentification client X.509
SSLCACertificateFile /etc/shibboleth/shib-sp.crt
 
</VirtualHost>

server.xml (Tomcat)

Il est aussi possible d'utiliser Tomcat directement (sans Apache en front) sur port 8443 avec un Tomcat Connector comme celui-ci:

<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
  maxThreads="150" scheme="https" secure="true"
  keystoreFile="/opt/shibboleth-idp/credentials/idp.jks" keystorePass="changeit"
  clientAuth="false" sslProtocol="TLS" />

Configuration du SP Shibboleth

shibboleth2.xml

Le principe de base de cette architecture est que le SP contacte deux IdP successivement, le second fournissant un complément d'attributs utilisateurs. Cette aggrégation d'attributs est configurée comme suit.

Adaptez le fichier shibboleth2.xml:

Pour interroger un second IdP, l'élément AttributeResolver doit être changer à:

<AttributeResolver type="Chaining">
   <AttributeResolver type="Query" subjectMatch="true" />
   <AttributeResolver type="SimpleAggregation" attributeId="mail" 
            format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
     <Entity>https://sympa.example.org/idp/shibboleth</Entity>
   </AttributeResolver>
</AttributeResolver>

L'IdP des groupes, couplé au référentiel Sympa, ne fait pas partie de la fédération. Le SP doit donc pointer vers les méta-données de l'IdP.

<!-- IdP couplé au serveur Sympa -->
<MetadataProvider type="XML" uri="http://sympa.example.org/idp/profile/Metadata/SAML" 
    backingFilePath="/etc/shibboleth/metadata-idp-sympa.xml" reloadInterval="7200">
</MetadataProvider>

Il faut aussi adapter l'élément ApplicationDefaults et tous les éléments ApplicationOverride. Pour activer le Message Signing des rêquetes SAML et pour déactiver le X.509 Client Authenticatioin, ces element doivent contenir:

signing="back" requireTransportAuth="false"

attribute-map.xml

L'attribut isMemberOf utilisé pour transmettre l'appartenance d'un utilisateur à des groupes n'est pas un attribut standard (ni dans eduPerson, ni dans SupAnn). Il faut donc que vous définissiez à quel champ d'entête HTTP il sera associé.

<!-- MemberOf -->
<Attribute name="urn:mace:dir:attribute-def:isMemberOf" id="isMemberOf"/>
<Attribute name="urn:oid:1.3.6.1.4.1.5923.1.5.1.1" id="isMemberOf"/>

attribute-policy.xml

Pour éviter que l'adresse mail peut être usurpée, il est recommandé d'appliquer également une règle de cadrage pour 'mail' l'attribut. Cela permettra d'assurer que les adresses mail ne sont acceptées que s'il existe une définition de cadrage (par exemple <Scope regexp=“false”>univ-rennes1.fr </Scope>) dans les métadonnées du fournisseur d'identité.

Dans le fichier attribute-policy.xml il faut ajouter:

<!-- Mail Scoping Rules -->
<afp:AttributeRule attributeID="mail">
    <afp:PermitValueRuleReference ref="ScopingRules"/>
</afp:AttributeRule>

Dans le fichier attribute-map.xml il faut remplacer la définition pour l'attribut mail par:

    <Attribute name="urn:oid:0.9.2342.19200300.100.1.3" id="mail">
       <AttributeDecoder xsi:type="ScopedAttributeDecoder" />
    </Attribute>
L'approche décrit en haut ne marche pas si un service a aussi des utilisateurs avec un Compte CRU parce que le fournisseur d'identité pour les Comptes CRU ne publie pas un scope dans ses meta données.

Tester

Pour tester le bon fonctionnement de cette architecture, tentez une authentification auprès d'une application protégée par le SP Shibboleth. Consultez les logs du SP pour identifier d'éventuelles erreurs. Pour s'assurer que l'attribut isMemberOf est diffusé par l'IdP, on peut d'abord se connecter sur le SP via l'URL https://sp.example.org/Shibboleth.sso/Login ou https://sp.example.org/secure. Après la connection, il faut consulter le Shibboleth Session Handler via https://sp.example.org/Shibboleth.sso/Session pour examiner les attributs.

Alternativement, pour s'assurer que l'attribut est bien remonté à l'application shibbolisée il peut être utile d'utiliser une petite application de test dont le seul but consiste à lister les variables d'environnement.

## Application env.cgi
## A configurer comme suit
##  <Location /cgi-bin/env.cgi>
##   AuthType shibboleth
##   ShibRequireSession On
##   require shibboleth
##  </Location>
 
#!/usr/bin/perl
 
print "Content-type: text/plain\n\n";
foreach my $v (keys %ENV) {
    printf "%s = %s\n", $v, $ENV{$v};
}

Il est aussi possible de faire un teste avec l'application resolvertest qui est distribué avec le SP Shibboleth. La commande pour tester l'architecture sur le SP est:

resolvertest -saml2 -f 'urn:oid:0.9.2342.19200300.100.1.3' -i 'https://sympa.renater.fr/idp/shibboleth' -n 'adresse.email@example.org' 

Naturellement il faut remplacer l'adresse email par une adresse qui est membre d'une liste de diffusion.

Le resultat devrait être à peu près:

isMemberOf: https://sympa.example.org/sympa/info/liste-1;https://sympa.example.org/sympa/info/liste-2

Autoriser des Utilisateurs avec des attributs de groupe

Si on a un serveur web avec un Shibboleth Service Provider configuré comme décrit ci-dessus, on peut protéger des documents ou des dossier avec une règle comme la suivante:

AuthType Shibboleth
ShibRequestSetting requireSession true
require isMemberOf <adresse-mail-de-la-liste>

Ou alternativement plus courte avec une règle qui utilise une expression régulière:

AuthType Shibboleth
ShibRequestSetting requireSession true
require isMemberOf ~ <nom-de-liste>

Configuration Avancée

Utiliser les Attributs d'Utilisateur Personnalisés

Sympa permet d'ajouter des 'attributs d'utilisateur personnalisés' pour les utilisateurs ('Configurer la liste' - 'Divers'). Avec la solution décrite ci-dessus il serait aussi possible d'utiliser ces attributs personnalisés comme attributs SAML. Il faudrait, pour cela, adapter le DataConnector et la requête SQL comme suit :

<resolver:DataConnector id="sympaDB" xsi:type="dc:RelationalDatabase">
  <dc:ApplicationManagedConnection jdbcDriver="com.mysql.jdbc.Driver"
                   jdbcURL="jdbc:mysql://localhost/sympa" 
                   jdbcUserName="sympa" 
                   jdbcPassword="apmys" />
  <dc:QueryTemplate>
    <![CDATA[
SELECT 
  isMemberOf,
  if(isMemberOf1<>'',concat(isMemberOf,'?',isMemberOf1n,'=',isMemberOf1),'') as isMemberOf1, 
  if(isMemberOf2<>'',concat(isMemberOf,'?',isMemberOf2n,'=',isMemberOf2),'') as isMemberOf2, 
  if(isMemberOf3<>'',concat(isMemberOf,'?',isMemberOf3n,'=',isMemberOf3),'') as isMemberOf3
FROM (
  SELECT 
      ExtractValue(custom_attribute_subscriber, '/custom_attributes/custom_attribute[1]/@id') AS isMemberOf1n, 
  ExtractValue(custom_attribute_subscriber, '/custom_attributes/custom_attribute[2]/@id') AS isMemberOf2n,
  ExtractValue(custom_attribute_subscriber, '/custom_attributes/custom_attribute[3]/@id') AS isMemberOf3n, 
  ExtractValue(custom_attribute_subscriber, '/custom_attributes/custom_attribute[1]/value[1]') AS isMemberOf1, 
  ExtractValue(custom_attribute_subscriber, '/custom_attributes/custom_attribute[2]/value[1]') AS isMemberOf2,
  ExtractValue(custom_attribute_subscriber, '/custom_attributes/custom_attribute[3]/value[1]') AS isMemberOf3, 
  CONCAT(list_subscriber, '@sympa.example.org') AS isMemberOf, list_subscriber
  FROM subscriber_table 
WHERE user_subscriber='$requestContext.principalName') as temporaryTable
    ]]>
  </dc:QueryTemplate>
 
  <dc:Column columnName="isMemberOf" attributeID="isMemberOf" />
  <dc:Column columnName="isMemberOf1" attributeID="isMemberOf" />
  <dc:Column columnName="isMemberOf2" attributeID="isMemberOf" />
  <dc:Column columnName="isMemberOf3" attributeID="isMemberOf" />
</resolver:DataConnector>

Le format des liens avec l'exemple plus haut serait:

<addresse-mail-de-la-liste>?<attribut1>=<valeur1>&<attribut2>=<valeur2>&<attribut3>=<valeur3>**

Autoriser l'accès des SPs

Avec la configuration décrit ci-dessus, tous les SPs d'une fedération peuvent faire des AttributeQueries pour obtenir les attributs de groupes d'un utilisateur. Si on veut limiter l'accès à quelques SPs particuliers, il faut adapter les règles dans le fichier attribute-filter.xml comme dans l'exemple suivant:

 <afp:AttributeFilterPolicy id="allowedVOServices">
  <afp:PolicyRequirementRule xsi:type="basic:OR">
      <basic:Rule xsi:type="basic:AttributeRequesterString" 
             value="https://sp1.example.org/shibboleth" />
      <basic:Rule xsi:type="basic:AttributeRequesterString" 
             value="https://sp2.example.org/shibboleth" />
      <basic:Rule xsi:type="basic:AttributeRequesterString" 
             value="https://sp3.example.org/shibboleth" />
  </afp:PolicyRequirementRule>
 
  <afp:AttributeRule attributeID="isMemberOf">
    <afp:PermitValueRule xsi:type="basic:ANY" />
  </afp:AttributeRule>
 
</afp:AttributeFilterPolicy>

Une solution plus générique pourrait permettre, pour chaque liste de diffusion, de déclarer les SPs qui sont autorisés à reçevoir les attributs contenant la liste des membres de cette liste de diffusion. Pour plus de détails sur cette solution, veuillez contacter equipe-federation@listes.renater.fr.