IAM https://www.vincentliefooghe.net/ fr OpenLDAP - LastBind Overlay https://www.vincentliefooghe.net/content/openldap-lastbind-overlay <span property="dc:title" class="field field--name-title field--type-string field--label-hidden">OpenLDAP - LastBind Overlay</span> <span rel="sioc:has_creator" class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/vincentl" typeof="schema:Person" property="schema:name" datatype="">vincentl</span></span> <span property="dc:date dc:created" content="2021-05-31T14:00:20+00:00" datatype="xsd:dateTime" class="field field--name-created field--type-created field--label-hidden">lun 31/05/2021 - 16:00</span> Mon, 31 May 2021 14:00:20 +0000 vincentl 211 at https://www.vincentliefooghe.net OpenIDM - Sécuriser un Connecteur Database Table https://www.vincentliefooghe.net/content/openidm-s%C3%A9curiser-un-connecteur-database-table <span class="field field--name-title field--type-string field--label-hidden">OpenIDM - Sécuriser un Connecteur Database Table</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Dans un projet client qui utilise IDM 7 (après une montée de version OpenIDM 3.1 vers IDM 7 qui s'est plutôt bien passée), nous avons mis en place la gestion du cycle de vie et notamment la suppression automatique de comptes 30 jours après la fin de contrat, comme ce que l'on voit souvent.</p> <p>Il a été décidé d'archiver quelques données des utilisateurs, histoire d'avoir un peu de traces.</p> <p>Pour ce faire nous avons mis en place un connecteur Database Table, qui pointe sur une table d'une base PostgreSQL. La base utilisée est la même que celle qui héberge le <em>repository</em> IDM.</p> <p>Tout fonctionnait correctement, jusqu'au moment où une insertion dans la table est tombée en erreur.</p> <p>Le message d'erreur :</p> <pre> [122] janv. 07, 2021 9:06:00.069 AM org.identityconnectors.framework.impl.api.local.operations.CreateImpl create GRAVE: buildSelectBasedAttributeInfo in SQL: 'SELECT * FROM openidm.archive_managed_user WHERE objectid IS NULL' org.postgresql.util.PSQLException: FATAL: terminating connection due to administrator command at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2532) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2267) [122] janv. 07, 2021 9:06:00.071 AM org.apache.tomcat.jdbc.pool.PooledConnection clearWarnings AVERTISSEMENT: Unable to clear Warnings, connection will be closed. org.postgresql.util.PSQLException: This connection has been closed. at org.postgresql.jdbc.PgConnection.checkClosed(PgConnection.java:865) at org.postgresql.jdbc.PgConnection.clearWarnings(PgConnection.java:759) at org.apache.tomcat.jdbc.pool.PooledConnection.clearWarnings(PooledConnection.java:837) at org.apache.tomcat.jdbc.pool.ConnectionPool.returnConnection(ConnectionPool.java:977) [122] janv. 07, 2021 9:06:00.073 AM org.forgerock.openidm.quartz.SchedulerServiceJob execute AVERTISSEMENT: Scheduled service "scheduler-service-group.delete" invocation reported failure: org.forgerock.json.resource.ForbiddenException: {code=500, reason=Internal Server Error, message=Operation CREATE failed with ConnectorException on system object} org.forgerock.openidm.quartz.ExecutionException: org.forgerock.json.resource.ForbiddenException: {code=500, reason=Internal Server Error, message=Operation CREATE failed with ConnectorException on system object} at org.forgerock.openidm.script.impl.ScriptRegistryService.execute(ScriptRegistryService.java:772) at org.forgerock.openidm.quartz.SchedulerServiceJob.execute(SchedulerServiceJob.java:123)</pre><p> </p> <h2>Analyse de la cause</h2> <p>En regardant dans les logs nous nous sommes aperçus que la base était indisponible périodiquement, pour une sauvegarde <em>offline</em>.</p> <p>Autant le "<em>moteur</em>" IDM était capable de reprendre son activité une fois la sauvegarde terminée et la base redémarrée, autant le connecteur n'arrivait pas à relancer une connexion.</p> <h2>Résolution</h2> <p>Après différentes recherches sur le Web et l'ouverture d'un ticket "How do I ? " chez Forgerock, j'ai pu modifier le fichier de configuration <em>provisioner.openicf-archiveDB.json</em> pour y modifier les paramètres suivants :</p> <ul> <li>"validationInterval" : "30000"  (permet de limiter la fréquence de validation)</li> <li>"validationQueryTimeout" : "30" (time-out pour les requêtes de validation)</li> <li>"validationQuery" : "SELECT 1 FROM archive_managed_user" (requête SQL utilisée pour la validation)</li> <li>"testOnConnect" : true (permet de valider avant la connection)</li> <li>"testOnBorrow" : true (permet de valider avant d'utiliser une connection dans le <em>pool</em>)</li> </ul> <p>Ce sont surtout les paramètres <strong>validationQuery</strong> et <strong>testOnBorrow</strong> qui permettent de gérer les cas de déconnexion.</p> <p>Une fois la nouvelle configuration mise en place, le connecteur devient <em>fault-tolerant</em> et peut reprendre son activité même après une perte de connexion à la base de données.</p> <p> </p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/vincentl" typeof="schema:Person" property="schema:name" datatype="">vincentl</span></span> <span class="field field--name-created field--type-created field--label-hidden">jeu 07/01/2021 - 09:54</span> <div class="field field--name-field-categorie field--type-entity-reference field--label-above"> <div class="field__label">Catégorie</div> <div class="field__item"><a href="/cat%C3%A9gorie/iam" hreflang="fr">IAM</a></div> </div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tag</div> <div class="field__items"> <div class="field__item"><a href="/tags/forgerock" hreflang="fr">forgerock</a></div> <div class="field__item"><a href="/taxonomy/term/3" hreflang="fr">openidm</a></div> <div class="field__item"><a href="/tags/connecteur" hreflang="fr">connecteur</a></div> </div> </div> <section class="field field--name-comment-node-book field--type-comment field--label-hidden comment-wrapper"> <h2 class="title comment-form__title">Ajouter un commentaire</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=209&amp;2=comment_node_book&amp;3=comment_node_book" token="7WaxU_wvuZ5r5sBmYS8TtoL5QqM9MAXmAWnuTkMTqFc"></drupal-render-placeholder> </section> Thu, 07 Jan 2021 08:54:49 +0000 vincentl 209 at https://www.vincentliefooghe.net Forgerock OpenDJ / DS - Supprimer les contrôles de syntaxe https://www.vincentliefooghe.net/content/forgerock-opendj-ds-supprimer-les-contr%C3%B4les-syntaxe <span class="field field--name-title field--type-string field--label-hidden">Forgerock OpenDJ / DS - Supprimer les contrôles de syntaxe</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Par défaut les nouveaux annuaires LDAP issus des sources OpenDS (tels que OpenDJ / DS chez Forgerock, mais aussi Ping Directory) renforcent les contrôles sur la syntaxe des attributs, le schéma, etc.</p> <p>Ceci empêche par exemple d'avoir un objet avec de multiples classes structurelles non hiérarchiques (par exemple <em>inetorgperson</em> et <em>country</em>).</p> <p>Par contre lorsqu'on importe des données d'un annuaire un peu moins strict (type Sun / Oracle DSEE), on se heurte souvent à des erreurs.</p> <p>La solution la plus propre est de modifier le fichier LDIF pour remettre au carré les données :</p> <ul> <li>supprimer / modifier les attributs non conformes</li> <li>modifier le schéma pour éviter les héritages multiples</li> </ul> <p>Mais ceci n'est pas toujours faisable.</p> <p>On peut alors relâcher les contrôles effectuer, en utilisant l'utilitaire <em>dsconfig </em>(testé depuis la version OpenDJ 2.6 jusque DS 7) :</p> <pre> #!/bin/bash PORT=5444 HOST=djlyreco BINDPASS="SuperPa$$w0rd" cd /path/to//opendj # Do not check schema bin/dsconfig set-global-configuration-prop --hostname $HOST --port ${PORT} --bindDN "cn=Directory Manager" \ --bindPassword ${BINDPASS} --set check-schema:false --trustAll --no-prompt # Allows multiple structural object classes (warning only) bin/dsconfig set-global-configuration-prop --hostname $HOST --port ${PORT} --bindDN "cn=Directory Manager" \ --bindPassword ${BINDPASS} --set single-structural-objectclass-behavior:warn --trustAll --no-prompt # Allows invalid attribute syntax bin/dsconfig set-global-configuration-prop --hostname $HOST --port ${PORT} \ --bindDN "cn=Directory Manager" --bindPassword ${BINDPASS} --set invalid-attribute-syntax-behavior:warn --trustAll --no-prompt # Allow pre-encoded passwords : pour l'import de compte avec des mots de passe existant bin/dsconfig -p ${PORT} -h $HOST -D "cn=Directory Manager" -w ${BINDPASS} \ set-password-policy-prop --policy-name "Default Password Policy" --set allow-pre-encoded-passwords:true -X -n</pre><p> </p> <p>La dernière ligne permet également d'importer des mots de passe pré-encodés. C'est utile en cas de migration de données entre annuaires, pour garder les mêmes mots de passe.</p> <p> </p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="">Vincent</span></span> <span class="field field--name-created field--type-created field--label-hidden">mer 09/12/2020 - 11:30</span> <div class="field field--name-field-categorie field--type-entity-reference field--label-above"> <div class="field__label">Catégorie</div> <div class="field__item"><a href="/cat%C3%A9gorie/iam" hreflang="fr">IAM</a></div> </div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tag</div> <div class="field__items"> <div class="field__item"><a href="/tags/ldap" hreflang="fr">ldap</a></div> <div class="field__item"><a href="/tags/opendj" hreflang="fr">opendj</a></div> <div class="field__item"><a href="/tags/forgerock" hreflang="fr">forgerock</a></div> </div> </div> <section class="field field--name-comment-node-book field--type-comment field--label-hidden comment-wrapper"> <h2 class="title comment-form__title">Ajouter un commentaire</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=208&amp;2=comment_node_book&amp;3=comment_node_book" token="FCUJHl0s_eMjKa8Cw3DvkSQUZ310pub5HrSmdbeosj4"></drupal-render-placeholder> </section> Wed, 09 Dec 2020 10:30:28 +0000 Vincent 208 at https://www.vincentliefooghe.net IBM Directory Server : gestion des mots de passe https://www.vincentliefooghe.net/content/ibm-directory-server-gestion-des-mots-passe <span property="dc:title" class="field field--name-title field--type-string field--label-hidden">IBM Directory Server : gestion des mots de passe</span> <div property="content:encoded" class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>L'installation par défaut de l'annuaire ISDS ne semble pas "terminée" en ce qui concerne la gestion des mots de passe : pas de politique de mot de passe par défaut, pas d'activation non plus. Et une modification par <em>ldapmodify</em> stocke le mot de passe en clair (tout au moins, il ressort en clair dans un <em>ldapsearch</em>).</p> <h2>Autoriser le changement de mot de passe par l'utilisateur</h2> <p>Par défaut, l'utilisateur ne peut pas modifier lui-même son mot de passe. Par exemple, en utilisant la commande <em>ldapchangepwd</em> renvoie une erreur :</p> <pre> /opt/ibm/ldap/V6.3/bin/ldapchangepwd -D uid=jdoe,ou=people,o=example.com -w secret -n SuperSecret ldap_simple_bind: Error, Password must be changed after reset changing password for entry uid=jdoe,ou=people,o=example.com Insufficient access --- Error, Password may not be modified</pre><p>Pour résoudre ce problème, il faut :</p> <ul> <li>ajouter une ACL permettant le changement de mot de passe en mode "Self"</li> <li>permettre le changement de mot de passe par l'utilisateur lui-même dans la politique de mot de passe (qu'il faut créer)</li> </ul> <h3>Ajout d'une politique de mot de passe par défaut</h3> <p>On va créer un fichier LDIF qui contient la définition de la politique globale :</p> <pre> dn: cn=pwdpolicy,cn=ibmpolicies objectclass: container objectclass: pwdPolicy objectclass: ibm-pwdPolicyExt objectclass: ibm-pwdGroupAndIndividualPolicies objectclass: top cn: pwdPolicy pwdAttribute: userPassword pwdGraceLoginLimit: 0 pwdLockoutDuration: 0 pwdFailureCountInterval: 0 passwordMaxRepeatedChars: 0 passwordMaxConsecutiveRepeatedChars: 0 pwdMaxAge: 0 pwdMinAge: 0 pwdExpireWarning: 0 passwordMinAlphaChars: 0 passwordMinOtherChars: 0 passwordMinDiffChars: 0 pwdLockout: false pwdAllowUserChange: true pwdMustChange: true pwdSafeModify: false ibm-pwdGroupAndIndividualEnabled: false ibm-pwdPolicy: true pwdCheckSyntax: 1 pwdInHistory: 5 pwdMaxFailure: 3 pwdMinLength: 6</pre><p> </p> <p>Le fichier sera ensuite ajouté à l'annuaire via <em>ldapadd</em> avec un compte administrateur.</p> <h3>Ajout d'une ACL permettant le changement</h3> <p>On va également ajouter une ACL sur l'arborescence qui contient les comptes</p> <pre> dn: ou=people,o=example.com changetype: modify add: aclentry aclentry: access-id:CN=THIS:at.userPassword:rwsc:normal:rsc</pre><p>Et on l'ajoute avec un <em>ldapmodify</em></p> <p>Suite à cela, la modification du mot de passe fonctionne correctement, par l'utilisateur lui-même.</p> <pre> /opt/ibm/ldap/V6.3/bin/ldapchangepwd -D uid=jdoe,ou=people,o=example.com -w secret -n SuperSecret changing password for entry uid=jdoe,ou=people,o=example.com</pre><p>Par contre, si on récupère avec un ldapsearch, la valeur est renvoyée en clair...</p> <pre> /opt/ibm/ldap/V6.3/bin/ldapsearch -D uid=jdoe,ou=people,o=example.com -w SuperSecret -b o=example.com uid=jdoe userpassword uid=jdoe,ou=people,o=example.com userpassword=SuperSecret</pre><h2>Modifier l'algorithme d'encryption</h2> <p>Par défaut, les mots de passe sont chiffrées avec l'algorithme AES256, qui est symétrique. Du coup, le mot de passe est décodé par le moteur de l'annuaire avant d'être affiché.</p> <p>Il vaut mieux donc utiliser un algorithme asymétrique type SSHA ou SHA2-x.</p> <pre> dn: cn=configuration changetype: modify replace: ibm-slapdPwEncryption ibm-slapdPwEncryption: ssha</pre><p>Si on redémarre l'annuaire (ou si on utilise la commande <em>idsldapexop</em>), on voit que le mot de passe est alors stocké de manière irréversible.</p> <pre> /opt/ibm/ldap/V6.3/bin/ldapchangepwd -D uid=jdoe,ou=people,o=example.com -w SuperSecret-n Pa55word changing password for entry uid=jdoe,ou=people,o=example.com /opt/ibm/ldap/V6.3/bin/ldapsearch -D uid=jdoe,ou=people,o=example.com -w SuperSecret -b o=example.com uid=jdoe userpassword uid=jdoe,ou=people,o=example.com userpassword={SSHA}4EqpfNQFq1aWqT+ouD5bEKFPyrIiFSd7AEQORENiet0xYiqgJLi73Q==</pre><h2>Import de mots de passe hachés</h2> <p>Si on migre les données d'un autre annuaire, via un fichier LDIF par exempe, avec un mot de passe chiffré dans un autre algorithme, ITDS utilise alors cet algorithme, et pas celui défini par défaut.</p> <p><strong>Attention</strong>, si on a activé l'option <em>pwdMustChange</em>, dans la politique de mot de passe, l'utilisateur sera obligé de modifier son mot de passe à la première connexion.</p> <p>Ce paramètre est dynamique, on peut donc le repasser à False si nécessaire.</p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> </div> <span rel="sioc:has_creator" class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/vincentl" typeof="schema:Person" property="schema:name" datatype="">vincentl</span></span> <span property="dc:date dc:created" content="2020-04-13T15:42:22+00:00" datatype="xsd:dateTime" class="field field--name-created field--type-created field--label-hidden">lun 13/04/2020 - 17:42</span> <div class="field field--name-field-categorie field--type-entity-reference field--label-above"> <div class="field__label">Catégorie</div> <div class="field__item"><a href="/cat%C3%A9gorie/iam" hreflang="fr">IAM</a></div> </div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tag</div> <div class="field__items"> <div class="field__item"><a href="/tags/itds" hreflang="fr">itds</a></div> <div class="field__item"><a href="/tags/ibm" hreflang="fr">ibm</a></div> <div class="field__item"><a href="/tags/ldap" hreflang="fr">ldap</a></div> </div> </div> <section class="field field--name-comment-node-blog field--type-comment field--label-hidden comment-wrapper"> <h2 class="title comment-form__title">Ajouter un commentaire</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=200&amp;2=comment_node_blog&amp;3=comment_node_blog" token="CguhFyApblCiIABXQL6ywDk-91Pf663FSaAfAxz5ikg"></drupal-render-placeholder> </section> Mon, 13 Apr 2020 15:42:22 +0000 vincentl 200 at https://www.vincentliefooghe.net Connexion OpenIDM / AD SSL https://www.vincentliefooghe.net/content/connexion-openidm-ad-ssl <span property="dc:title" class="field field--name-title field--type-string field--label-hidden">Connexion OpenIDM / AD SSL </span> <div property="content:encoded" class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p><em>De l'importance d'utiliser un nom de serveur correspondant au CN du certificat...</em></p> <h2>Contexte</h2> <p>Afin de pouvoir tester la connexion d'un OpenIDM sur un serveur AD (et par la suite la mise en place du plugin de synchronisation de mot de passe) j'ai mis en place une maquette simple avec :</p> <ul> <li>un serveur OpenIDM sous CentOS</li> <li>un serveur AD sous Windows 2008R2</li> </ul> <p>J'ai mis en place un certificat sur le serveur AD, qui est aussi le contrôleur de domaine principal, avec un certificat qu'il a généré.</p> <p>J'ai importé le certificat dans le Keystore OpenIDM, comme pour toute connexion sécurisée.</p> <h2>Problème</h2> <p>Mais cependant, impossible d'avoir une connexion opérationnelle. La connexion en LDAPS ne fonctionne pas (port 636 / ssl = "true"), alors qu'elle est opérationnelle en LDAP (port 389 / ssl="false")</p> <p>Au démarrage, on trouve ceci dans les logs :</p> <pre>SEVERE: OpenICF connector test of SystemIdentifier{ uri='system/AD/'} failed! org.identityconnectors.framework.common.exceptions.ConnectionFailedException: javax.naming.CommunicationException: mytest-dc.local:636 [Root exception is java.net.SocketException: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.security.ssl.SSLContextImpl$DefaultSSLContext)] </pre><p>Même chose en testant via une requête REST :</p> <pre>http://engine.local:8180/openidm/system/AD/?_action=test Caused by: javax.naming.CommunicationException: mytest-dc.local:636 [Root exception is java.net.SocketException: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.security.ssl.SSLContextImpl$DefaultSSLContext)] </pre><h2>Cause et résolution</h2> <p>Au final, il s'agissait d'un problème de nom DNS. En effet, le serveur AD a son propre nom DNS, et ce n'est pas celui que j'utilisais dans ma connexion (mytest-dc.local).</p> <p>Les logs me donnaient bien une piste :</p> <pre>WARNING: Resource exception: 500 Internal Server Error: "javax.naming.CommunicationException: simple bind failed: lyreco-dc.local:636 [Root exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching lyreco-dc.local found.]" </pre><p>J'ai donc modifié le fichier <em>conf/boot/boot.properties</em> pour positionner le nom de machine égal au CN du certificat du serveur, que l'on peut récupérer avec la commande suivante :</p> <pre>openssl x509 -in adldaps.pem -noout -text |grep Subject Subject: CN=<strong>WIN-RLQ30AF9URF.group.id-num.com</strong> Subject Public Key Info: X509v3 Subject Key Identifier: X509v3 Subject Alternative Name:</pre><p>Une fois le paramétrage du nom effectué, et après avoir modifié mon fichier /etc/hosts pour que mon serveur OpenIDM arrive à faire le lien entre l'adresse IP et le nom, tout est allé beaucoup mieux.</p> <p>Pour que la modification soit prise en compte, il a fallu redémarrer OpenIDM (puisque mon paramétrage se trouve dans le fichier boot.properties).</p> <p>Il a également fallu que je passe par la console d'admin, et que je valide la connexion AD pour que cela soit opérant.</p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> </div> <span rel="sioc:has_creator" class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/vincentl" typeof="schema:Person" property="schema:name" datatype="">vincentl</span></span> <span property="dc:date dc:created" content="2019-11-11T14:17:56+00:00" datatype="xsd:dateTime" class="field field--name-created field--type-created field--label-hidden">lun 11/11/2019 - 15:17</span> <div class="field field--name-field-categorie field--type-entity-reference field--label-above"> <div class="field__label">Catégorie</div> <div class="field__item"><a href="/cat%C3%A9gorie/iam" hreflang="fr">IAM</a></div> </div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tag</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/3" hreflang="fr">openidm</a></div> <div class="field__item"><a href="/tags/active-directory" hreflang="fr">active directory</a></div> <div class="field__item"><a href="/tags/ssl" hreflang="fr">ssl</a></div> </div> </div> <section class="field field--name-comment-node-blog field--type-comment field--label-hidden comment-wrapper"> </section> Mon, 11 Nov 2019 14:17:56 +0000 vincentl 199 at https://www.vincentliefooghe.net Sailpoint : surcharger une classe Java spécifique https://www.vincentliefooghe.net/content/sailpoint-surcharger-une-classe-java-sp%C3%A9cifique <span property="dc:title" class="field field--name-title field--type-string field--label-hidden">Sailpoint : surcharger une classe Java spécifique</span> <div property="content:encoded" class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><h2 style="margin: 12px 0px; text-indent: 0px;">Contexte</h2> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Dans notre environnement <a href="https://www.sailpoint.com/">Sailpoint</a> IIQ, notre intégrateur développe un ensemble de classes Java pour étendre la solution, en utilisant les nombreuses API.</p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Les différentes livraisons se font sous la forme d'un <span style=" font-style:italic;">gros</span> fichier jar, reprenant l'ensemble des classes Java, qui est déposé dans le répertoire <span style=" font-style:italic;">IIQ_HOME/WEB-INF/lib</span>.</p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Par contre il arrive que l'on souhaite modifier le comportement d'une classe spécifique, sans attendre la prochaine livraison.</p> <p>Ceci est possible, en surchargeant juste la classe concernée.</p> <h2>Solution</h2> <p>Pour surcharger une classe Java, il faut recréer l'arborescence correspondante, sous le répertoire <span style=" font-style:italic;">IIQ_HOME/WEB-INF/classes</span>.</p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Par exemple, si la classe <span style=" font-style:italic;">MaSuperClasse.class</span> se trouve dans le package <span style=" font-style:italic;">com.mycompany.identityiq.utils</span>, on peut surcharger cette classe en créant la structure de répertoires : <span style=" font-style:italic;">IIQ_HOME/WEB-INF/classes/com/mycompany/identityiq/utils</span>, et en y copiant le fichier <span style=" font-style:italic;">MaSuperClasse.class</span>.</p> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Après un redémarrage du serveur d'application, la nouvelle classe Java va surcharger celle qui est présente dans le répertoire <span style=" font-style:italic;">lib</span>.</p> </div> <span rel="sioc:has_creator" class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/vincentl" typeof="schema:Person" property="schema:name" datatype="">vincentl</span></span> <span property="dc:date dc:created" content="2019-08-12T13:48:28+00:00" datatype="xsd:dateTime" class="field field--name-created field--type-created field--label-hidden">lun 12/08/2019 - 15:48</span> <div class="field field--name-field-categorie field--type-entity-reference field--label-above"> <div class="field__label">Catégorie</div> <div class="field__item"><a href="/cat%C3%A9gorie/iam" hreflang="fr">IAM</a></div> </div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tag</div> <div class="field__items"> <div class="field__item"><a href="/tags/sailpoint" hreflang="fr">Sailpoint</a></div> </div> </div> <section class="field field--name-comment-node-blog field--type-comment field--label-hidden comment-wrapper"> </section> Mon, 12 Aug 2019 13:48:28 +0000 vincentl 198 at https://www.vincentliefooghe.net https://www.vincentliefooghe.net/content/sailpoint-surcharger-une-classe-java-sp%C3%A9cifique#comments Upgrade Forgerock OpenDJ / DS https://www.vincentliefooghe.net/content/upgrade-forgerock-opendj-ds <span class="field field--name-title field--type-string field--label-hidden">Upgrade Forgerock OpenDJ / DS</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Deux scénarios de migration / upgrade seront étudiés :</p> <ul> <li>upgrade "en place" d'un annuaire en version inférieure</li> <li>ajout dans la topologie de réplication d'un annuaire en version supérieure.</li> </ul> <p>Dans cet exemple, on va procéder à une migration de la version OpenDJ 3.5.1 à la version DS 6.5.</p> <h2>Environnement</h2> <p>L'environnement de test est le suivant :</p> <p>3 serveurs : <em>dj1.id-num.com</em>, <em>dj2.id-num.com</em>, <em>dj3.id-num.com</em>.</p> <p>Dans un premier temps, seuls les serveurs dj1 et dj2 sont installés, en mode multi-maîtres :</p> <ul> <li>O.S. CentOS 7</li> <li>Java jre 1.8</li> <li>OpenDJ 3.5.1, avec 2 annuaires répliqués</li> <li>Schéma de données <em>custom</em> avec attributs et classes d'objets spécifiques</li> <li>Annuaire installé dans le répertoire <code>/opt/opendj</code></li> <li>Index créé sur le serveur dj2, sur l'attribut <em>company</em></li> </ul> <h3>Script d'installation</h3> <p>L'installation sur les 2 serveurs a été faite avec le script suivant :</p> <pre><code>#!/bin/sh # # Installation Forgerock OpenDJ 3.5.1 # #--------------------------------------- LDAPPORT=1389 PASS=ldapMaster REPLADM=replicationAdmin REPLPASS=replicationPassword ADMINPORT=4445 HOST1=dj1.id-num.com HOST2=dj2.id-num.com HOST3=dj3.id-num.com JAVAHOME=/opt/jre1.8.0_144 BASEDN="o=example" BASEDIR=/opt/opendj BINDIR=${BASESDIR}/bin export JAVA_HOME=${JAVAHOME} export OPENDJ_JAVA_HOME=${JAVAHOME} ${BASEDIR}/setup \ --cli \ --baseDN ${BASEDN} \ --addBaseEntry \ --ldapPort ${LDAPPORT} \ --adminConnectorPort ${ADMINPORT} \ --rootUserDN cn=Directory\ Manager \ --rootUserPassword ${PASS} \ --acceptLicense \ --no-prompt \ --noPropertiesFile </code></pre><h3>Script de réplication</h3> <p>La réplication est mise en place entre les serveurs <strong>dj1</strong> et <strong>dj2</strong>, via la commande :</p> <pre><code>${BINDIR}/dsreplication enable \ --host1 ${HOST1} --port1 ${ADMINPORT} \ --bindDN1 "cn=Directory Manager" \ --bindPassword1 ${PASS} \ --replicationPort1 8989 \ --host2 ${HOST2} --port2 ${ADMINPORT} \ --bindDN2 "cn=Directory Manager" \ --bindPassword2 ${PASS} \ --replicationPort2 8989 \ --adminUID ${REPLADM} \ --adminPassword ${REPLPASS} \ --baseDN ${BASEDN} \ --trustAll \ --no-prompt </code></pre><p>On peut ensuite lancer l'init, pour tous les serveurs de la topologie :</p> <pre><code>${BINDIR}/dsreplication \ initialize-all \ --adminUID ${REPLADM} \ --adminPassword ${REPLPASS} \ --baseDN ${BASEDN} \ --hostname $(hostname) \ --port ${ADMINPORT} \ --trustAll \ --no-prompt </code></pre><p>On vérifie, avec l'option <em>status</em> :</p> <pre><code>${BINDIR}/dsreplication status \ --adminUID ${REPLADM} \ --adminPassword ${REPLPASS} \ --port ${ADMINPORT} \ --trustAll \ --no-prompt Suffix DN : Server : Entries : Replication enabled : DS ID : RS ID : RS Port (1) : M.C. (2) : A.O.M.C. (3) : Security (4) ---------------------:---------------------:---------:---------------------:-------:-------:-------------:----------:--------------:------------- cn=Directory Manager : dj2.id-num.com:4445 : : : : : : : : o=example : dj1.id-num.com:4445 : 45 : true : 14774 : 27576 : 8989 : 0 : : false o=example : dj2.id-num.com:4445 : : true : 29513 : 481 : 8989 : 0 : : false </code></pre><h3>Schéma spécifique</h3> <p>Le schéma est modifié avec l'ajout de 2 attributs et une classe d'objet :</p> <pre><code>dn: cn=schema changetype: modify add: attributeTypes attributeTypes: ( partneruid-oid NAME 'partneruid' SUP name X-ORIGIN 'user Defined' ) attributeTypes: ( company-oid NAME 'company' SUP name X-ORIGIN 'user Defined' ) - add: objectclasses objectClasses: ( partnerperson-oid NAME 'partnerPerson' SUP inetorgperson MUST ( company ) MAY ( partneruid) ) </code></pre><h3>Version OpenDJ / DS</h3> <p>On peut vérifier la version actuelle avec la commande :</p> <pre><code>/opt/opendj/bin/dsconfig --version 3.5.1 (revision 23b322a7502f029b6d3725212c162de36f038122) </code></pre><h2>Upgrade "en place"</h2> <p>Il faut tout d'abord vérifier la trajectoire de migration supportée.</p> <p>Pour <em>Directory Services 6.5</em>, sorti fin 2018, la migration est supportée depuis la version OpenDJ 2.6 et supérieure.</p> <p>La version de Java est également à vérifier : Java 8 ou 11 pour DS 6.5. Nous utilisons une version 8 ce qui convient donc.</p> <p>Forgerock recommande de faire une sauvegarde <strong>physique</strong> du serveur à <em>upgrader</em>, en arrêtant l'annuaire et en faisant un <em>backup</em> de toute l'arborescence :</p> <pre><code>/opt/opendj/bin/stop-ds tar zcf opendj-3.5.tgz opendj </code></pre><p><strong>Note</strong> : l'upgrade en place nécessite d'arrêter l'instance d'annuaire qui va être migrée.</p> <h3>Procédure de migration</h3> <p>La procédure est simple ; elle consiste essentiellement à :</p> <ul> <li>extraire l'archive de la nouvelle version au dessus de l'ancienne</li> <li>lancer la commande <strong>upgrade</strong></li> <li>vérifier et éventuellement adapter le paramétrage.</li> </ul> <p>En détail :</p> <pre><code>cd /opt unzip /path/to/Software/Forgerock/DS-6.6.0.zip Archive: DS-6.6.0.zip creating: opendj/legal-notices/third-party-licenses/ creating: opendj/template/extlib/ creating: opendj/template/setup-profiles/ .../... creating: opendj/template/setup-profiles/IDM/repo/6.5/ creating: opendj/template/setup-profiles/IDM/repo/6.5/schema/ replace opendj/snmp/mib/rfc2605.txt? [y]es, [n]o, [A]ll, [N]one, [r]ename: y </code></pre><p>Répondre "A" pour écraser / remplacer les anciennes versions de fichiers.</p> <p>Lancer ensuite l'upgrade :</p> <pre><code>/opt/opendj/upgrade &gt;&gt;&gt;&gt; OpenDJ Upgrade Utility * OpenDJ configuration will be upgraded from version 3.5.1.23b322a7502f029b6d3725212c162de36f038122 to 6.5.0.e2a029a050cc6908fd144a6ca9c687e01ad56ea3 * OpenDJ data will be upgraded from version 6.0.0 to 6.5.0.e2a029a050cc6908fd144a6ca9c687e01ad56ea3 * See '/opt/opendj/logs/upgrade.log' for a detailed log of this operation &gt;&gt;&gt;&gt; Preparing to upgrade OpenDJ 5.5.0 changed the indexing algorithm for JSON equality matching rules. All JSON based attribute indexes must be rebuilt which may take a long time. Do you want to rebuild the indexes automatically at the end of the upgrade? (yes/no) [no]: yes OpenDJ 6.5.0 changed the indexing algorithm for replication metadata. Its index must be rebuilt which may take a long time; without a working index every server start will take longer than normal. Do you want to rebuild the index automatically at the end of the upgrade? (yes/no) [no]: yes The upgrade is ready to proceed. Do you wish to continue? (yes/no) [yes]: &gt;&gt;&gt;&gt; Performing upgrade Adding configuration for schema providers........................... 100% Adding configuration entry 'dn: cn=Core Schema,cn=Schema Providers,cn=config'................................................ 100% Removing top configuration entry for matching rules................. 100% Removing configuration for syntaxes................................. 100% .../... Replacing schema file '02-config.ldif'.............................. 100% Archiving concatenated schema....................................... 100% Migrating replication changelog files to 6.5.0 format............... 100% &gt;&gt;&gt;&gt; OpenDJ configuration was successfully upgraded from version 3.5.1.23b322a7502f029b6d3725212c162de36f038122 to 6.5.0.e2a029a050cc6908fd144a6ca9c687e01ad56ea3 &gt;&gt;&gt;&gt; OpenDJ data was successfully upgraded from version 6.0.0 to 6.5.0.e2a029a050cc6908fd144a6ca9c687e01ad56ea3 &gt;&gt;&gt;&gt; Performing post upgrade tasks Rebuilding index(es) '[.caseIgnoreJsonQueryMatch, .caseExactJsonQueryMatch, ds-sync-hist.changeSequenceNumberOrderingMatch]' for base dn(s) '[o=example]'....................................................... 100% &gt;&gt;&gt;&gt; Post upgrade tasks complete </code></pre><p>Les logs de la migration se trouvent dans <code>$DSHOME/logs/upgrade.log</code>.</p> <p>On peut ensuite démarrer l'instance, via la commande <code>bin/start-ds</code></p> <p>L'annuaire est bien passé en version supérieure :</p> <pre><code>/opt/opendj/bin/dsconfig --version 6.5.0 (revision e2a029a050cc6908fd144a6ca9c687e01ad56ea3) </code></pre><h3>Post Upgrade</h3> <p>Si on part d'une version inférieure à la version 5.5, Forgerock rcommande de donner des privilèges spécifiques au compte de réplication. Les privilèges à ajouter sont les suivants :</p> <ul> <li>bypass-lockdown</li> <li>monitor-read</li> <li>server-lockdown</li> </ul> <p>Ceci peut être fait en LDIF. Dans notre cas, le compte utilisé est <em>replicationAdmin</em> :</p> <pre><code>dn: cn=replicationAdmin,cn=Administrators,cn=admin data changetype: modify add: ds-privilege-name ds-privilege-name: bypass-lockdown ds-privilege-name: monitor-read ds-privilege-name: server-lockdown </code></pre><p>La modification n'est à faire qu'une seule fois, les données étant répliquées.</p> <p>L'upgrade peut ensuite être fait pour tous les serveurs de la topologie.</p> <h2>Ajout d'un nouveau serveur dans la topologie</h2> <p>Dans cette option, on va ajouter un nouveau serveur dans la topologie de réplication. Ceci peut donc être fait sans interruption de service.</p> <h3>Installation de DS 6.5</h3> <p>L'installation ds DS 6.5 se fait quasiment de la même manière que OpenDJ 3.5. Les seules différences : pas d'option <em>--cli</em> et l'option <em>--hostname</em> qui est obligatoire.</p> <pre><code>LDAPPORT=1389 PASS=ldapMaster REPLADM=replicationAdmin REPLPASS=replicationPassword ADMINPORT=4445 HOST1=dj1.id-num.com HOST2=dj2.id-num.com HOST3=dj3.id-num.com JAVAHOME=/opt/jre1.8.0_144 BASEDN="o=example" BASEDIR=/opt/opendj BINDIR=${BASESDIR}/bin export JAVA_HOME=${JAVAHOME} export OPENDJ_JAVA_HOME=${JAVAHOME} /opt/opendj/setup \ --hostname ${HOST3} \ --baseDN ${BASEDN} \ --addBaseEntry \ --ldapPort ${LDAPPORT} \ --adminConnectorPort ${ADMINPORT} \ --rootUserDN cn=Directory\ Manager \ --rootUserPassword ${PASS} \ --acceptLicense </code></pre><p>A ce stade, on a donc un annuaire en mode "standby". Nous pouvons l'inclure dans le domaine de réplication.</p> <h3>Ajout dans la réplication</h3> <p>Forgerock recommande d'utiliser la commande <em>dsreplication</em> du nouveau serveur pour l'ajouter dans une topologie de réplication existante.</p> <p>La commande pour configurer une nouvelle réplication utilise maintenant l'option <em>configure</em> plutôt que <em>enable</em> :</p> <pre><code>${BINDIR}/dsreplication configure \ --host1 ${HOST1} --port1 4445 \ --bindDN1 "cn=Directory Manager" \ --bindPassword1 ${PASS} \ --replicationPort1 8989 \ --host2 ${HOST3} --port2 4445 \ --bindDN2 "cn=Directory Manager" \ --bindPassword2 ${PASS} \ --replicationPort2 8989 \ --adminUID ${REPLADM} \ --adminPassword ${REPLPASS} \ --baseDN ${BASEDN} \ --trustAll \ --no-prompt </code></pre><h3>Initialisation de la réplication</h3> <p>On peut maintenant lancer l'initialisation de la réplication sur un seul serveur. Avec l'option <em>initialize</em> plutôt que <em>initialize-all</em> il faut préciser les serveurs sources et destinations :</p> <pre><code>${BINDIR}/dsreplication initialize \ --adminUID ${REPLADM} \ --adminPassword ${REPLPASS} \ --baseDN ${BASEDN} \ --hostSource ${HOST1} \ --portSource ${ADMINPORT} \ --hostDestination ${HOST2} \ --portDestination ${ADMINPORT} \ --trustAll \ --no-prompt </code></pre><p>On peut ensuite vérifier :</p> <pre><code>Suffix DN : Server : Entries : Replication enabled : DS ID : RS ID : RS Port (1) : Delay (ms) : Security (2) ----------:---------------------:---------:---------------------:-------:-------:-------------:------------:------------- o=example : dj1.id-num.com:4445 : 2025 : true : 14774 : 27576 : 8989 : N/A : false o=example : dj2.id-num.com:4445 : 2025 : true : 29513 : 481 : 8989 : 0 : false o=example : dj3.id-num.com:4445 : 2025 : true : 706 : 30128 : 8989 : 0 : false </code></pre><p>Une fois que l'on a ajouté le nouveau serveur, il faut éventuellement paramétrer les éléments propres à l'instance (par exemple les index) qui sont positionnés via l'utilitaire <em>dsconfig</em>.</p> <h3>Adaptations et différences</h3> <p>On peut trouver quelques différences entre OpenDJ 3.5 et DS 6.5. A première vue, on peut citer :</p> <ul> <li>les options des commandes <em>dsconfig</em> ou <em>dsreplication</em></li> <li>les fichiers de définition du schéma qui ont migré de <code>DJ_HOME/config/schema</code> à <code>DS_HOME/db/schema</code></li> </ul> <h2>Synthèse</h2> <p>Nous avons vu que deux possibilités sont offertes pour faire un <em>upgrade</em>, de manière assez simple.</p> <p>L'upgrade "en place" est intéressant pour récupérer toute la configuration spécifique de l'instance (paramètres positionnés par l'utilitaire <strong>dsconfig</strong> et qui ne sont pas répliqués). Dans ce cas, on garde le même serveur, et donc la même version d'O.S., de java, et d'adresse IP.</p> <p>Cependant, ceci nécessite d'arrêter l'une des instances de la topologie de réplication.</p> <p>L'ajout d'une nouvelle instance peut se justifier si on veut changer de version d'O.S. et/ou limiter au maximum les interruptions de service. Cela permet aussi d'adapter les scripts aux nouvelles versions d'outils (<strong>même si l'environnement de Test est fait pour cela</strong>). Par contre, ceci implique de reprendre le paramétrage de la configuration.</p> <table> <tbody> <tr> <th>Méthode</th> <th>Avantages</th> <th>Inconvénients</th> </tr> <tr> <th>Upgrade en place</th> <td>Pas de changement d'adresse IP<br />Récupération de la configuration</td> <td>Arrêt de l'instance</td> </tr> <tr> <th>Ajout d'une nouvelle instance</th> <td>Possibilité de monter de version d'O.S.<br />Pas d'arrêt de serveur<br />Possibilité de tester les outils</td> <td>Nouveau serveur à provisionner<br />Nouvelle adresse IP à gérer<br />Pas de récupération du paramétrage spécifique</td> </tr> </tbody> </table> <p> </p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/vincentl" typeof="schema:Person" property="schema:name" datatype="">vincentl</span></span> <span class="field field--name-created field--type-created field--label-hidden">ven 14/12/2018 - 22:06</span> <div class="field field--name-field-categorie field--type-entity-reference field--label-above"> <div class="field__label">Catégorie</div> <div class="field__item"><a href="/cat%C3%A9gorie/iam" hreflang="fr">IAM</a></div> </div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tag</div> <div class="field__items"> <div class="field__item"><a href="/tags/forgerock" hreflang="fr">forgerock</a></div> <div class="field__item"><a href="/tags/opendj" hreflang="fr">opendj</a></div> </div> </div> <section class="field field--name-comment-node-book field--type-comment field--label-hidden comment-wrapper"> </section> Fri, 14 Dec 2018 21:06:26 +0000 vincentl 197 at https://www.vincentliefooghe.net Evaluation des ACI LDAP dans les annuaires Sun / Oracle / Ping / Forgerock https://www.vincentliefooghe.net/content/evaluation-des-aci-ldap-dans-les-annuaires-sun-oracle-ping-forgerock <span property="dc:title" class="field field--name-title field--type-string field--label-hidden">Evaluation des ACI LDAP dans les annuaires Sun / Oracle / Ping / Forgerock</span> <div property="content:encoded" class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item">Beaucoup d'annuaires ont des ACI compatibles avec celles de l'annuaire Sun (issu des sources de Netscape) :<ul><li>Sun / Oracle DSEE</li><li>389DS / Red Hat Directory Server</li><li>Oracle OUD</li><li>Sun OpenDS / Wren DS</li><li>Forgerock OpenDJ</li><li>Ping Directory.</li></ul>Dans ces annuaires, les ACI peuvent être positionnées directement sur les objets eux-mêmes ou sur des objets plus proches de la racine.Le mécanisme d'évaluation est globalement le suivant :<ul><li>On traverse le DIT depuis la racine de l'annuaire jusqu'à l'entrée que l'on tente d'accéder</li><li>On collecte toutes les ACI rencontrées sur le chemin</li><li>Les ACI collectées sont séparées en 2 paquets : <strong>DENY</strong> (refuser) et <strong>ALLOW</strong> (autoriser)</li><li>Chaque paquet est trier pour que les ACI les plus simples/faciles/rapides sont évaluées en premier</li><li>Le paquet "DENY" est évalué en premier. Si une ACI correspond, l'analyse s'arrête et la requête est refusée</li><li>Si on arrive à la fin du paquet "DENY", on traite le paquet "ALLOW"</li><li>Si une règle ACI ALLOW est trouvée, alors l'analyse s'arrête et l'accès est autorisé</li><li>Si à la fin du traitement du paquet "ALLOW" aucune règle ne correspond, c'est un accès implicite "DENY". L'accès est refusé.</li></ul>On peut donc en déduire les règles suivantes :<ul><li>le placement d'une ACI affecte uniquement son périmètre</li><li>l'ordre d'évaluation est indéterminé. Une règle ACI plus "proche" d'une entrée n'est pas plus prioritaire que celle tout en haut du DIT</li><li>le fait de placer une ACI proche d'une entrée permet seulement de s'assurer que l'ACI ne sera évaluée que lorsqu'on tente d'accéder à la donnée (amélioration des performances)</li><li>par défaut, l'annuaire ne donne pas accès (au moins sur les nouveaux, type Forgerock, Ping). Les règles "<em>DENY</em>" doivent être très rares, et à manier avec précaution (puisqu'elles sont évaluées en premier).</li></ul> </div> <span rel="sioc:has_creator" class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/vincentl" typeof="schema:Person" property="schema:name" datatype="">vincentl</span></span> <span property="dc:date dc:created" content="2018-10-17T11:34:06+00:00" datatype="xsd:dateTime" class="field field--name-created field--type-created field--label-hidden">mer 17/10/2018 - 13:34</span> <div class="field field--name-field-categorie field--type-entity-reference field--label-above"> <div class="field__label">Catégorie</div> <div class="field__item"><a href="/cat%C3%A9gorie/iam" hreflang="fr">IAM</a></div> </div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tag</div> <div class="field__items"> <div class="field__item"><a href="/tags/ldap" hreflang="fr">ldap</a></div> <div class="field__item"><a href="/tags/acl" hreflang="fr">acl</a></div> </div> </div> <section class="field field--name-comment-node-blog field--type-comment field--label-hidden comment-wrapper"> </section> Wed, 17 Oct 2018 11:34:06 +0000 vincentl 195 at https://www.vincentliefooghe.net Migration Sun DSEE vers un autre annuaire LDAP https://www.vincentliefooghe.net/content/migration-sun-dsee-vers-un-autre-annuaire-ldap <span property="dc:title" class="field field--name-title field--type-string field--label-hidden">Migration Sun DSEE vers un autre annuaire LDAP</span> <div property="content:encoded" class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Suite au rachat (déjà ancien) de Sun par Oracle, l'annuaire LDAP Sun DSEE, devenu Oracle DSEE depuis, arrive en fin de support. Ce produit a eu ses beaux jours en entreprise, à l'époque c'était probablement l'un des meilleurs, capable de gérer des centaines de milliers d'entrées, avec une architecture assez simple.</p> <p>Depuis, pas mal de concurrents sont arrivés, et les entreprises se lancent vers des migrations d'annuaires LDAP Sun / Oracle DSEE vers d'autres solutions plus récentes, soit sur un modèle Open Source (OpenLDAP généralement) ou propriétaires. La migration d'annuaire LDAP peut se faire assez rapidement.</p> <p>Les points d'attention sont les éléments non couverts par le standard LDAP, à savoir :</p> <ul> <li>Formalisme du schéma (attributs et classes d'objets)</li> <li>Réplication</li> <li>ACI</li> <li>Privilèges et droits des comptes</li> <li>Politiques de mots de passe</li> </ul> <p>Les données elles-mêmes peuvent être <em>relativement</em> facilement, avec un export et import LDIF. Selon la méthode utilisée (<em>ldapsearch</em> ou fonction <em>export</em> de l'annuaire), on peut ou non récupérer des attributs opérationnels.</p> <h2>Schéma de données</h2> <p>Sun DSEE permet de gérer des OID alphanumériques, par exemple :</p> <pre>attributeTypes: (sitename-oid NAME 'sitename' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'user defined' ) </pre><p>Si on veut être conforme aux RFC - par exemple dans le cas de OpenLDAP - il faut utiliser des <em>OID</em> numériques. La bonne pratique demande aussi d'utiliser sa propre classe d'OID avec une demande auprès de l'IANA. Dans ce cas, l'OID serait, par exemple :</p> <pre>attributeTypes: (1.3.6.1.4.1.31488.2.3.51 NAME 'sitename' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'user defined' ) </pre><p>A part cela, la syntaxe elle-même est différente entre Sun DSEE et OpenLDAP. Les objets du schéma OpenLDAD sont préfixés par <em>olc</em></p> <h3>Exemple Sun DSEE</h3> <p>Les éléments spécifique du schéma (attributs et classes d'objets) sont définis dans le fichier <code>[DIRECTORY_HOME]/config/schema/99-user.ldif</code>, au format suivant :</p> <pre>dn: cn=schema objectClass: top objectClass: ldapSubentry objectClass: subschema cn: schema attributeTypes: (sitename-oid NAME 'sitename' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'user defined' ) objectClasses: (1.3.6.1.4.1.31488.2.4.18 NAME 'ocSite' SUP top STRUCTURAL MUST (cn $ description) MAY ( sitename $ c $ l $ telephonenumber ) X-ORIGIN 'user defined' ) </pre><p>Dans le cas d'une migration vers Forgerock, Ping Directory (et généralement tous les annuaires issus des sources de <em>OpenDS</em>), on peut réutiliser la même définition pour les attributs et classes d'objets.</p> <h3>Exemple avec OpenLDAP</h3> <p>OpenLDAP utilise une syntaxe un peu différente, en dehors des OID numériques. On doit notamment préciser le DN complet pour l'objet du schéma. Par exemple :</p> <pre>dn: cn=myschema,cn=schema,cn=config objectClass: olcSchemaConfig cn: myschema olcattributeTypes: ( 1.3.6.1.4.1.31488.2.3.110 NAME 'sitename' DESC 'Site name' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE) olcobjectClasses: ( 1.3.6.1.4.1.31488.2.4.18 NAME 'ocSite' SUP top STRUCTURAL MUST (cn $ description) MAY ( sitename $ c $ l $ telephonenumber ) ) </pre><h3>Différences sur des classes d'objet issues du standard</h3> <p>Suite à une migration à partir de Sun DSEE, je me suis aperçu que Ping Directory &amp; Forgerock définissent également des types différents pour certaines classes d'objet, notamment celles liées aux objets Posix :</p> <pre>objectClasses: ( 1.3.6.1.1.1.2.0 NAME 'posixAccount' SUP top STRUCTURAL DESC 'Abstraction of an account with POSIX attributes' MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) MAY ( authPassword $ userPassword $ loginShell $ gecos $ description ) X-ORIGIN 'draft-howard-rfc2307bis' ) objectClasses: ( 1.3.6.1.1.1.2.2 NAME 'posixGroup' SUP top STRUCTURAL DESC 'Abstraction of a group of accounts' MUST gidNumber MAY ( cn $ authPassword $ userPassword $ memberUid $ description ) X-ORIGIN 'draft-howard-rfc2307bis' ) </pre><p>Si dans les données, on utilise ce type d'objets, il peut y avoir des modifications à apporter, sachant qu'il est recommandé d'activer les contrôles de cohérence / validité du schéma lors des imports initiaux, pour avoir une meilleur qualité de données.</p> </div> <span rel="sioc:has_creator" class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/vincentl" typeof="schema:Person" property="schema:name" datatype="">vincentl</span></span> <span property="dc:date dc:created" content="2018-07-29T09:47:52+00:00" datatype="xsd:dateTime" class="field field--name-created field--type-created field--label-hidden">dim 29/07/2018 - 11:47</span> <div class="field field--name-field-categorie field--type-entity-reference field--label-above"> <div class="field__label">Catégorie</div> <div class="field__item"><a href="/cat%C3%A9gorie/iam" hreflang="fr">IAM</a></div> </div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tag</div> <div class="field__items"> <div class="field__item"><a href="/tags/ldap" hreflang="fr">ldap</a></div> <div class="field__item"><a href="/tags/openldap" hreflang="fr">openldap</a></div> <div class="field__item"><a href="/tags/sun-dsee" hreflang="fr">Sun DSEE</a></div> <div class="field__item"><a href="/tags/forgerock" hreflang="fr">forgerock</a></div> <div class="field__item"><a href="/tags/ping-directory" hreflang="fr">ping directory</a></div> </div> </div> <section class="field field--name-comment-node-blog field--type-comment field--label-hidden comment-wrapper"> </section> Sun, 29 Jul 2018 09:47:52 +0000 vincentl 193 at https://www.vincentliefooghe.net LDAP : les types de groupes https://www.vincentliefooghe.net/content/ldap-les-types-groupes <span property="dc:title" class="field field--name-title field--type-string field--label-hidden">LDAP : les types de groupes</span> <div property="content:encoded" class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Les annuaires LDAP sont principalement utilisés pour gérer 2 types d'objets :</p> <ul> <li>les comptes utilisateurs (qui vont permettre une authentification centralisée)</li> <li>des groupes d'utilisateurs (qui vont permettre une gestion de droits).</li> </ul> <p>D'autres objets annexes sont souvent ajoutés, qui ont un rôle de <em>"listes de valeurs"</em>.</p> <p>Nous allons voir dans cet article les différents types de groupes qui peuvent être utilisés, sachant que selon les RFC LDAP 4519, seuls les premiers sont vraiment standardisés.</p> <h2>Groupes statiques</h2> <p>A l'origine, les groupes statiques, définis par les classes d'objet <em>groupOfNames</em> et <em>groupOfUniqueNames</em> sont juste une liste de membres (donnés par leur DN).</p> <p>Dans un groupe de type <em>groupOfNames</em>, l'attribut utilisé est <em>member</em>, et on peut avoir plusieurs fois le même membre.</p> <p>Dans un groupe de type <em>groupOfUniqueNames</em>, l'attribut utilisé est <em>uniqueMember</em>, et on peut avoir une seule occurence pour chaque membre.</p> <p>Exemple :</p> <pre><code>dn: cn=firstGroup,ou=Groups,dc=example,dc=com objectclass: top objectclass: groupofnames member: uid=john.doe,ou=People,dc=example,dc=com cn: firstGroup </code></pre><p>On peut donc récupérer facilement tous les membres d'un groupe statique, en listant les valeurs de l'attribut <em>uniqueMember</em> ou <em>member</em>, selon le cas. De même, pour connaître les groupes affectés à un utilisateur, on utilisera une recherche du type :</p> <pre><code>ldapsearch -b "dc=example,dc=com" -s sub "(|(&amp;(objectClass=groupOfNames)(member=uid=john.doe,ou=People,dc=example,dc=com)) \ (&amp;(objectClass=groupOfUniqueNames) (uniqueMember=uid=john.doe,ou=People,dc=example,dc=com)) </code></pre><h3>Avantages</h3> <p>C'est le seul type de groupe standardisé, et qui peut donc être utilisé par la plupart des applications ou équipements. Certains équipements réseaux par exemple peuvent utiliser les groupes pour valider les habilitations.</p> <h3>Inconvénients</h3> <p>Le principal inconvénient est la maintenance de ces groupes, puisque l'ajout ou le retrait des membres se fait de manière unitaire. Par défaut il n'y a pas de contrôle de cohérence, on peut donc ajouter comme membre d'un groupe un DN inexistant ; et si on supprime un utilisateur, son DN peut encore apparaître dans la liste des membres.</p> <p><strong>Note</strong> : certains annuaires utilisent un <em>plugin</em> d'intégrité référentiel, qui permet, à la suppression de l'entrée utilisateur, d'aller supprimer le DN correspondant des membres des groupes.</p> <h2>Groupes dynamiques</h2> <p>Il n'y a pas de définition formelle de type RFC pour les groupes dynamiques. L'implémentation dépend du fournisseur d'annuaire. Sur OpenLDAP par exemple, on trouve un <em>overlay</em> <strong>dynlist</strong> qui permet d'apporter cette fonctionnalité.</p> <p>Sur les annuaires tels que Sun/Oracle DSEE, OpenDS et ses descendants (Forgerock OpenDJ, Oracle OUD, UnboundID/Ping Drectory), on utilise un groupe de type <em>groupOfUrls</em>.</p> <p>Les groupes dynamiques utilisent un attribut <em>memberURL</em> qui permet de définir une requête LDAP, de la forme ldap:///BASE_DN?ATTRIBUTS?SCOPE?FILTRE.</p> <p>Exemples de filtres :</p> <pre><code>ldap:///ou=People,dc=example,dc=com?uid,cn,sn?sub?(departmentNumber=IT) </code></pre><p>Recherche à partir de la base DN <code>ou=People,dc=example,dc=com</code>, avec un <em>scope</em> de type <code>subtree</code>, les utilisateurs qui sont dans le département IT, et récupère uniquement les attributs <em>uid, sn</em> et <em>cn</em>.</p> <p>Exemple :</p> <pre><code>dn: cn=IT-Staff,ou=Groups,dc=example,dc=com objectclass: top objectclass: groupOfURLs cn: IT-staff memberURL: ldap:///ou=People,dc=example,dc=com??sub?(departmentNumber=IT) </code></pre><h3>Avantages</h3> <p>Une fois que le filtre est défini, la gestion des membres est automatique, puisque basée sur les attributs de l'utilisateur.</p> <h3>Inconvénients</h3> <p>Par défaut, ce sont les clients qui doivent interpréter la requête LDAP présente dans l'attribut <em>memberURL</em>. On ne peut pas avoir la liste des membres rien qu'en faisant une requête sur le groupe, et on ne peut pas non plus avoir la liste des groupes d'un utilisateur en cherchant son DN dans les membres.</p> <p>Pour pallier ce problème, la plupart des annuaires qui implèmentent les groupes dynamiques gèrent aussi un attribut virtuel <strong>isMemberOf</strong>, qui contient la liste des groupes dont l'utilisateur est membre.</p> <h2>Exemples</h2> <p>Si on injecte le contenu du fichier LDIF suivant dans un annuaire :</p> <pre><code>dn: ou=staff,ou=people,dc=example,dc=com objectClass: organizationalunit objectClass: top ou: staff dn: uid=john.doe,ou=staff,ou=people,dc=example,dc=com givenName: John objectClass: person objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: top uid: john.doe cn: John Doe sn: Doe departmentNumber: Finance dn: uid=elliot.alderson,ou=staff,ou=people,dc=example,dc=com givenName: Elliot objectClass: person objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: top uid: elliot.alderson cn: Elliot Alderson sn: Alderson departmentNumber: IT dn: uid=angela.moss,ou=staff,ou=people,dc=example,dc=com givenName: Angela objectClass: person objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: top uid: angela.moss cn: Angela Moss sn: Moss departmentNumber: IT dn: ou=Groups,dc=example,dc=com objectClass: organizationalunit objectClass: top ou: Groups dn: cn=Staff,ou=Groups,dc=example,dc=com objectclass: top objectclass: groupofnames member: uid=john.doe,ou=staff,ou=people,dc=example,dc=com member: uid=elliot.alderson,ou=staff,ou=people,dc=example,dc=com member: uid=angela.moss,ou=staff,ou=people,dc=example,dc=com cn: Staff dn: cn=IT-Staff,ou=Groups,dc=example,dc=com objectclass: top objectclass: groupOfURLs cn: IT-staff memberURL: ldap:///ou=People,dc=example,dc=com??sub?(departmentNumber=IT) </code></pre><p>La recherche des groupes liés à Elliot Anderson donne le résultat suivant :</p> <pre><code>ldapsearch -b ou=groups,dc=example,dc=com member=uid=elliot.alderson,ou=staff,ou=people,dc=example,dc=com dn dn: cn=Staff,ou=Groups,dc=example,dc=com </code></pre><p>On ne trouve pas le groupe dynamique lié. Par contre, si on utilise l'attribut <em>isMemberOf</em>, on va avoir tous les groupes :</p> <pre><code>ldapsearch -b ou=people,dc=example,dc=com uid=elliot.alderson ismemberof dn: uid=elliot.alderson,ou=staff,ou=people,dc=example,dc=com ismemberof: cn=Staff,ou=Groups,dc=example,dc=com ismemberof: cn=IT-Staff,ou=Groups,dc=example,dc=com </code></pre><h2>Groupes statiques virtuels</h2> <p>Pour tenter de concilier les avantages des deux types de groupes, certains annuaires implémentent un nouveau type de groupe : le groupe Virtuel Static <em>Virtual Static Groups</em>).</p> <p>Les groupes virtuels statiques utilisent une classe d'objet spécifique, <em>ds-virtual-static-group</em>, et font référence à un groupe dynamique (qu'il faudra donc créer auparavant).</p> <p>Exemple :</p> <pre><code>dn: cn=VS-IT-staff,ou=Groups,dc=example,dc=com cn: VS-IT-staff objectclass: top objectclass: groupOfNames objectclass: ds-virtual-static-group ds-target-group-dn: cn=IT-Staff,ou=Groups,dc=example,dc=com </code></pre><p>Si on ajoute le groupe, on le récupère dans l'attribut <em>isMemberOf</em> de l'utilisateur. Par exemple ;</p> <pre><code>/opt/opendj/bin/ldapsearch -b ou=staff,ou=people,dc=example,dc=com uid=elliot.alderson ismemberof dn: uid=elliot.alderson,ou=staff,ou=people,dc=example,dc=com ismemberof: cn=Staff,ou=Groups,dc=example,dc=com ismemberof: cn=IT-Staff,ou=Groups,dc=example,dc=com ismemberof: cn=VS-IT-staff,ou=Groups,dc=example,dc=com </code></pre><p>Par contre par défaut le groupe virtuel statique ne donne pas la liste de ses membres :</p> <pre><code>ldapsearch -b ou=Groups,dc=example,dc=com cn=VS-IT-staff dn: cn=VS-IT-staff,ou=Groups,dc=example,dc=com cn: VS-IT-staff ds-target-group-dn: cn=IT-Staff,ou=Groups,dc=example,dc=com objectClass: groupOfNames objectClass: ds-virtual-static-group objectClass: top </code></pre><h3>Pour récupérer la liste des membres</h3> <p>La récupération de la liste des membres d'un groupe virtuel statique se fait à la volée, et peut donc être consommatrice de ressources, surtout sur de grosses volumétrie. La fonctionnalité est désactivée par défaut. On peut l'activer avec l'utilitaire <strong>dsconfig</strong> :</p> <pre><code>dsconfig set-virtual-attribute-prop --name "Virtual Static member" --set allow-retrieving-membership:true </code></pre><p>Si on recherche directement le groupe , on a bien la liste des membres :</p> <pre><code>ldapsearch -b ou=Groups,dc=example,dc=com cn=VS-IT-staff dn: cn=VS-IT-staff,ou=Groups,dc=example,dc=com member: uid=elliot.alderson,ou=staff,ou=people,dc=example,dc=com member: uid=angela.moss,ou=staff,ou=people,dc=example,dc=com cn: VS-IT-staff ds-target-group-dn: cn=IT-Staff,ou=Groups,dc=example,dc=com objectClass: groupOfNames objectClass: ds-virtual-static-group objectClass: top member: uid=elliot.alderson,ou=staff,ou=people,dc=example,dc=com member: uid=angela.moss,ou=staff,ou=people,dc=example,dc=com </code></pre><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ce type de groupe peut donc être utilisé si une application cliente veur récupérer la liste des membres, à partir du nom du groupe. Par contre, si on fait une recherche sur les membres, le groupe n'apparaît pas non plus :</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p> <pre style="margin: 0px; text-indent: 0px;">ldapsearch -b ou=groups,dc=example,dc=com member=uid=elliot.alderson,ou=staff,ou=people,dc=example,dc=com dn dn: cn=Staff,ou=Groups,dc=example,dc=com</pre><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Seul l'attribut <em>isMemberOf</em> contient l'ensemble des groupes affectés à la personne.</p> <style type="text/css"> <!--/*--><![CDATA[/* ><!--*/ <!--/*--><![CDATA[/* ><!--*/ p, li { white-space: pre-wrap; } /*--><!]]]]><![CDATA[>*/ /*--><!]]>*/ </style><pre><code> </code></pre></div> <span rel="sioc:has_creator" class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/vincentl" typeof="schema:Person" property="schema:name" datatype="">vincentl</span></span> <span property="dc:date dc:created" content="2018-04-30T09:20:07+00:00" datatype="xsd:dateTime" class="field field--name-created field--type-created field--label-hidden">lun 30/04/2018 - 11:20</span> <div class="field field--name-field-categorie field--type-entity-reference field--label-above"> <div class="field__label">Catégorie</div> <div class="field__item"><a href="/cat%C3%A9gorie/iam" hreflang="fr">IAM</a></div> </div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tag</div> <div class="field__items"> <div class="field__item"><a href="/tags/ldap" hreflang="fr">ldap</a></div> <div class="field__item"><a href="/tags/ping-directory" hreflang="fr">ping directory</a></div> <div class="field__item"><a href="/tags/opendj" hreflang="fr">opendj</a></div> </div> </div> <section class="field field--name-comment-node-blog field--type-comment field--label-hidden comment-wrapper"> </section> Mon, 30 Apr 2018 09:20:07 +0000 vincentl 192 at https://www.vincentliefooghe.net