<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>