<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>My approach to FreeIPA server using LDAP Connector<br>
      <br>
      Assumptions:</p>
    <p>- FreeIPA v4.6.x (or maybe newer),<br>
      - midPoint v3.9 or newer,<br>
      - LDAP Connector v2.0 or newer,<br>
      - Most important: Manage IPA users and group membership only.<br>
      <br>
      So, for those who don't know what FreeIPA (or IPA) server is: It
      is NOT a <b>free beer</b>! :(<br>
      Link: <a class="moz-txt-link-freetext" href="https://www.freeipa.org/">https://www.freeipa.org/</a><br>
      <br>
      FreeIPA is a bunch of Open Source blocks stuck together, but the
      most important for us is LDAP, a 389-ds in particular, hidden
      under the mask.<br>
      Another important piece is the Kerberos.<br>
      <br>
      Therefore the first thoughts is to use LDAP Connector. Well, there
      is dedicated IPA connector, but is dead I think, or at least its
      development is stalled long time ago.<br>
      <br>
      The challenge:<br>
      <br>
      - FreeIPA user account schema contains A LOT of ObjectClasses.
      Newly created IPA account has 14 ObjectClasses:<br>
      <br>
      objectClass: inetOrgPerson<br>
      objectClass: ipaObject<br>
      objectClass: mepManagedEntry<br>
      objectClass: posixGroup<br>
      objectClass: top<br>
      objectClass: inetuser<br>
      objectClass: ipaNTUserAttrs<br>
      objectClass: ipaObject<br>
      objectClass: ipaSshGroupOfPubKeys<br>
      objectClass: ipaSshUser<br>
      objectClass: krbPrincipalAux<br>
      objectClass: krbTicketPolicyAux<br>
      objectClass: mepOriginEntry<br>
      objectClass: posixAccount<br>
      <br>
      - LDAP Connector doesn't like many object classes. Why?, you may
      ask...<br>
      <br>
      Example: ipaNTUserAttrs ObjectClass REQUIRES some attributes, for
      instance: ipaNTSecurityIdentifier provided in the format below:<br>
      S-1-5-21-17112102435-2119056689-2123461762-1015<br>
      The last part: "1015" is an incremental number, everything before
      is constant.<br>
      The attribute value must be compound, we have constant first part
      and calculate next free number for the last part, so we have to
      read the max value from LDAP and increase it by 1.<br>
      This value MUST be known at the moment of account creation. The
      DNA plugin is good for simple tasks, such POSIX UID and GID
      calculation, but this is something we have to calculate ourselves.<br>
      <br>
      There is a lot of problems like that.<br>
      I tried to configure the LDAP resource to set the "inetOrgPerson"
      as default ObjectClass and all the rest as auxiliary object
      classes, and I failed because of so many different errors.<br>
      <br>
      Working solution:<br>
      <br>
      - The LDAP account has only one ObjectClass defined in midPoint
      Schema Handling.<br>
        All the rest is made using "scripting" functionality of the
      connector AFTER "create account" operation.<br>
      <br>
      Problem:<br>
      If the account is created, and THEN its schema is extended, some
      requied attributes are still missing,<br>
      for example "krbExtraData" and "krbPrincipalKey" which are binary
      data created during password change.<br>
      But the password is already set.<br>
      <br>
      So creation of an LDAP account for IPA must be done in 3 steps:<br>
      1. create LDAP account with inetOrgPerson ObjectClass only,<br>
      2. extend the account schema - add missing ObjectClasses and all
      required attributes (scripting functionality),<br>
      3. set the password again for the missing Kerberos data (scripting
      functionality).<br>
      <br>
      The account created this way works good, it is visible in FreeIPA
      User Interface, we can login to our Linux boxes using this
      account, Kerberos tickets work OK.<br>
      <br>
      MidPoint part:<br>
      <br>
      1) Resource XML<br>
      <br>
      Almost everything is standard for common LDAP resource, schema
      handling, mappings, etc. - nothing really sophisticated.<br>
      These are some the parts worth mentioning:<br>
      <br>
      <icfc:configurationProperties><br>
                 
      <objectClassesToSynchronize>top</objectClassesToSynchronize><br>
                 
<objectClassesToSynchronize>person</objectClassesToSynchronize><br>
                 
<objectClassesToSynchronize>organizationalPerson</objectClassesToSynchronize><br>
                 
<objectClassesToSynchronize>inetUser</objectClassesToSynchronize><br>
                 
<objectClassesToSynchronize>inetOrgPerson</objectClassesToSynchronize><br>
      </icfc:configurationProperties><br>
      <br>
      <schema><br>
              <generationConstraints><br>
                 
      <generateObjectClass>ri:top</generateObjectClass><br>
                 
      <generateObjectClass>ri:person</generateObjectClass><br>
                 
<generateObjectClass>ri:organizationalPerson</generateObjectClass><br>
                 
      <generateObjectClass>ri:inetUser</generateObjectClass><br>
                 
      <generateObjectClass>ri:inetOrgPerson</generateObjectClass><br>
                 
      <generateObjectClass>ri:ipaNTUserAttrs</generateObjectClass><br>
                 
      <generateObjectClass>ri:ipaObject</generateObjectClass><br>
                 
<generateObjectClass>ri:ipaSshGroupOfPubKeys</generateObjectClass><br>
                 
      <generateObjectClass>ri:ipaSshUser</generateObjectClass><br>
                 
<generateObjectClass>ri:krbPrincipalAux</generateObjectClass><br>
                 
<generateObjectClass>ri:krbTicketPolicyAux</generateObjectClass><br>
                 
      <generateObjectClass>ri:mepOriginEntry</generateObjectClass><br>
                 
      <generateObjectClass>ri:posixAccount</generateObjectClass><br>
                 
      <generateObjectClass>ri:groupOfNames</generateObjectClass><br>
                 
<generateObjectClass>ri:ipaNTGroupAttrs</generateObjectClass><br>
                 
      <generateObjectClass>ri:ipaUserGroup</generateObjectClass><br>
                 
      <generateObjectClass>ri:nestedGroup</generateObjectClass><br>
                 
      <generateObjectClass>ri:posixGroup</generateObjectClass><br>
                 
      <generateObjectClass>ri:ipaHostGroup</generateObjectClass><br>
                 
      <generateObjectClass>ri:ipaPermission</generateObjectClass><br>
              </generationConstraints><br>
      </schema><br>
      <br>
      <schemaHandling><br>
                  <!-- Allmost all attributes require "explicit"
      fetchStrategy!!! --><br>
                  <...><br>
                  <fetchStrategy>explicit</fetchStrategy><br>
                  </...><br>
      </schemaHandling><br>
      <br>
      <scripts><br>
              <script><br>
                  <host>connector</host><br>
                  <language>Groovy</language><br>
                  <argument><br>
                      <name>username</name><br>
                      <c:path
      xmlns:xsi=<a class="moz-txt-link-rfc2396E" href="http://www.w3.org/2001/XMLSchema-instance">"http://www.w3.org/2001/XMLSchema-instance"</a>
      xsi:type="t:ItemPathType">$focus/name</c:path><br>
                  </argument><br>
                  <argument><br>
                      <name>pass</name><br>
                      <c:path
      xmlns:xsi=<a class="moz-txt-link-rfc2396E" href="http://www.w3.org/2001/XMLSchema-instance">"http://www.w3.org/2001/XMLSchema-instance"</a>
xsi:type="t:ItemPathType">$focus/credentials/password/value</c:path><br>
                  </argument><br>
                  <code><br>
                     println "/opt/midpoint/scripts/freeipa/after_add.sh
      $username $pass".execute().text</code><br>
                  <operation>add</operation><br>
                  <order>after</order><br>
              </script><br>
      </scripts><br>
      <br>
      2) The Bash script:<br>
      <br>
      #!/bin/bash<br>
      <br>
      server='ipaserver.ipa.domain.ltd'<br>
      user='cn=Directory Manager'<br>
      password='LDAP PASSWORD'<br>
      basedn='dc=ipa,dc=domain,dc=ltd'<br>
      userid=$1<br>
      pass=$2<br>
      <br>
      #last value for ipaNTSecurityIdentifier<br>
      declare i ipaNTSecurityIdentifier=`ldapsearch -H <a class="moz-txt-link-freetext" href="ldap://$server">ldap://$server</a> -x
      -w $password -D "$user" -b "$basedn" '(objectClass=inetOrgPerson)'
      ipaNTSecurityIdentifier|grep ipaNTSecurityIdentifier|rev|cut -d
      "-" -f 1|rev|sort|tail -1`<br>
nextNTSecurityIdentifier=S-1-5-21-1453210229-2669001626-2403961664-$(($ipaNTSecurityIdentifier+1))<br>
      <br>
      #last UID i GID - Posix<br>
      declare i olduid=`ldapsearch -H <a class="moz-txt-link-freetext" href="ldap://$server">ldap://$server</a> -x -w $password -D
      "$user" -b "$basedn" '(objectClass=inetOrgPerson)' uidNumber|grep
      uidNumber|cut -d " " -f 2|tail -1`<br>
      NEWUID=$(($olduid+1))<br>
      declare i oldgid=`ldapsearch -H <a class="moz-txt-link-freetext" href="ldap://$server">ldap://$server</a> -x -w $password -D
      "$user" -b "$basedn" '(objectClass=inetOrgPerson)' gidNumber|grep
      gidNumber|cut -d " " -f 2|tail -1`<br>
      NEWGID=$(($oldgid+1))<br>
      <br>
      cat > /tmp/ipa_add_group_$userid.ldif <<EOF<br>
      dn: cn=$userid,cn=groups,cn=accounts,$basedn<br>
      objectClass: ipaObject<br>
      objectClass: mepManagedEntry<br>
      objectClass: posixGroup<br>
      objectClass: top<br>
      cn: $userid<br>
      gidNumber: $NEWGID<br>
      description: User private group for $userid<br>
      mepManagedBy: uid=$userid,cn=users,cn=accounts,$basedn<br>
      EOF<br>
      <br>
      /usr/bin/ldapmodify -h $server -x -w $password -D "$user" -a -f
      /tmp/ipa_add_group_$userid.ldif<br>
      <br>
      <br>
      <br>
      cat > /tmp/ipa_add_$userid.ldif <<EOF<br>
      dn: uid=$userid,cn=users,cn=accounts,$basedn<br>
      changetype: modify<br>
      add: objectClass<br>
      objectClass: inetuser<br>
      -<br>
      add: objectClass<br>
      objectClass: ipaNTUserAttrs<br>
      -<br>
      add: objectClass<br>
      objectClass: ipaObject<br>
      -<br>
      add: objectClass<br>
      objectClass: ipaSshGroupOfPubKeys<br>
      -<br>
      add: objectClass<br>
      objectClass: ipaSshUser<br>
      -<br>
      add: objectClass<br>
      objectClass: krbPrincipalAux<br>
      -<br>
      add: objectClass<br>
      objectClass: krbTicketPolicyAux<br>
      -<br>
      add: objectClass<br>
      objectClass: mepOriginEntry<br>
      -<br>
      add: objectClass<br>
      objectClass: posixAccount<br>
      -<br>
      add: homeDirectory<br>
      homeDirectory: /home/$userid<br>
      -<br>
      add: loginshell<br>
      loginshell: /bin/bash<br>
      -<br>
      add: ipaNTSecurityIdentifier<br>
      ipaNTSecurityIdentifier: $nextNTSecurityIdentifier<br>
      -<br>
      add: uidNumber<br>
      uidNumber: $NEWUID<br>
      -<br>
      add: gidNumber<br>
      gidNumber: $NEWGID<br>
      -<br>
      add: krbCanonicalName<br>
      krbCanonicalName: $userid@IPA.DOMAIN.LTD<br>
      -<br>
      add: krbPrincipalName<br>
      krbPrincipalName: $userid@IPA.DOMAIN.LTD<br>
      -<br>
      add: preferredLanguage<br>
      preferredLanguage: YourLangHere<br>
      -<br>
      add: gecos<br>
      gecos: $userid<br>
      -<br>
      add: mepManagedEntry<br>
      mepManagedEntry: cn=$userid,cn=groups,cn=accounts,$basedn<br>
      EOF<br>
      <br>
      /usr/bin/ldapmodify -h $server -x -w $password -c -D "$user" -f
      /tmp/ipa_add_$userid.ldif<br>
      <br>
      # Change password once again!<br>
      cat > /tmp/ipa_password_$userid.ldif <<EOF<br>
      dn: uid=$userid,cn=users,cn=accounts,$basedn<br>
      changetype: modify<br>
      replace: userPassword<br>
      userPassword: $pass<br>
      EOF<br>
      <br>
      /usr/bin/ldapmodify -h $server -x -w $password -c -D "$user" -f
      /tmp/ipa_password_$userid.ldif<br>
      <br>
      rm -f /tmp/ipa_add_$userid.ldif<br>
      rm -f /tmp/ipa_add_group_$userid.ldif<br>
      rm -f /tmp/ipa_ipausers_$userid.ldif<br>
      rm -f /tmp/ipa_password_$userid.ldif<br>
      <br>
      ##########<br>
      If you know the better way, please let me know.<br>
      Best Regards!<br>
      WS<br>
    </p>
  </body>
</html>