OpenLdap – LSC – Active Directory sync and login pass-through

Explain:

Example user name Jenkins login process:

Install and config OpenLDAP

Install:

apt-get install slapd ldap-utils libsasl2-modules-gssapi-mit

Note: libsasl2-modules-gssapi-mit may not need

Admin username and password: admin / demopassword (note that admin is default username of Admin in OpenLDAP)

Edit file /etc/ldap/ldap.conf, add:

BASE dc=your_ad_domain,dc=local
URI ldap://ldap-server.your_ad_domain.local ldap://ldap-server.your_ad_domain.local:666

ldap-server is the hostname of server openldap installed, to change hostname in Ubuntu:

Edit hostname nano /etc/hostname content ldap-server.your_ad_domain.local

Edit host file nano /etc/hosts content 127.0.0.1 ldap-server.your_ad_domain.local

update value, run dpkg-reconfigure slapd

select No
enter DNS domain name: your_ad_domain.local
enter Organization name: CompanyName
enter admin password:demopassword
Database backend to use: MDB
slapt is purged: Yes
move old database: Yes
Allow LDAPv2 protocol? No

Test Ldap is running: ldapsearch -x

View Ldap server status: /etc/init.d/slapd status

Install LSC (tool support sync data between ldap server)

Files & Example: http://tools.lsc-project.org/projects/lsc/repository

$ apt-get install default-jre
$ nano /etc/apt/sources.list

add content:

deb     http://lsc-project.org/debian lsc main
deb-src http://lsc-project.org/debian lsc main

run apt-get update

Import reponsitory public key: wget -O - http://ltb-project.org/wiki/lib/RPM-GPG-KEY-LTB-project | sudo apt-key add -

Install: apt-get install lsc

Configuration:

$ mkdir /etc/lsc/ad2openldap
$ nano /etc/lsc/ad2openldap/lsc.xml

LSC will sync data source from Active Directory to OpenLDAP, content of lsc.xml:

Note: ad-server.publicdomain.xyz is a public domain point to IP address of AD server to get data.

<?xml version="1.0" ?>
<lsc xmlns="http://lsc-project.org/XSD/lsc-core-2.1.xsd" revision="0">
    <connections>
        <ldapConnection>
            <name>ldap-src-conn</name>
            <url>ldap://ad-server.publicdomain.xyz:389/DC=your_ad_domain,DC=local</url>
            <username>user_to_login@your_ad_domain.local</username>
            <password>password_to_login_ad</password>
            <authentication>SIMPLE</authentication>
            <referral>IGNORE</referral>
            <derefAliases>NEVER</derefAliases>
            <version>VERSION_3</version>
            <pageSize>-1</pageSize>
            <factory>com.sun.jndi.ldap.LdapCtxFactory</factory>
            <tlsActivated>false</tlsActivated>
        </ldapConnection>
        <ldapConnection>
            <name>ldap-dst-conn</name>
            <url>ldap://localhost:389/dc=your_ad_domain,dc=local</url>
            <username>cn=admin,dc=your_ad_domain,dc=local</username>
            <password>demopassword</password>
            <authentication>SIMPLE</authentication>
            <referral>IGNORE</referral>
            <derefAliases>NEVER</derefAliases>
            <version>VERSION_3</version>
            <pageSize>-1</pageSize>
            <factory>com.sun.jndi.ldap.LdapCtxFactory</factory>
            <tlsActivated>false</tlsActivated>
        </ldapConnection>
    </connections>
    <tasks>
        <task>
            <name>Sync_1_Users</name>
            <bean>org.lsc.beans.SimpleBean</bean>
            <ldapSourceService>
                <name>ad-source-service</name>
                <connection reference="ldap-src-conn" />
                <baseDn>DC=your_ad_domain,DC=local</baseDn>
                <pivotAttributes>
                    <string>samAccountName</string>
                </pivotAttributes>
                <fetchedAttributes>
                    <string>description</string>
                    <string>cn</string>
                    <string>sn</string>
                    <string>givenName</string>
                    <string>samAccountName</string>
                    <string>userPrincipalName</string>
                    <string>title</string>
                    <string>physicalDeliveryOfficeName</string>
                    <string>telephoneNumber</string>
                    <string>facsimileTelephoneNumber</string>
                    <string>department</string>
                    <string>company</string>
                    <string>mail</string>
                    <string>mobile</string>
                    <string>jpegPhoto</string>
                </fetchedAttributes>
                <!-- <getAllFilter>(objectClass=user)</getAllFilter> -->
                <getAllFilter>(&amp;(objectCategory=person)(objectClass=user)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))</getAllFilter>
                <!-- <getOneFilter>(&amp;(objectClass=user)(samAccountName={samAccountName})(mail=*))</getOneFilter> -->
                <getOneFilter>(&amp;(objectCategory=person)(objectClass=user)(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(samAccountName={samAccountName}))</getOneFilter>
                <!-- <cleanFilter>(&amp;(objectClass=user)(samAccountName={uid})(mail=*))</cleanFilter> -->
                <cleanFilter>(&amp;(objectCategory=person)(objectClass=user)(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(samAccountName={uid}))</cleanFilter>
            </ldapSourceService>
            <ldapDestinationService>
                <name>opends-dst-service</name>
                <connection reference="ldap-dst-conn" />
                <baseDn>ou=Users,dc=your_ad_domain,dc=local</baseDn>
                <pivotAttributes>
                    <string>uid</string>
                </pivotAttributes>
                <fetchedAttributes>
                    <string>description</string>
                    <string>cn</string>
                    <string>sn</string>
                    <string>userPassword</string>
                    <string>objectClass</string>
                    <string>uid</string>
                    <string>title</string>
                    <string>physicalDeliveryOfficeName</string>
                    <string>telephoneNumber</string>
                    <string>telexNumber</string>
                    <string>ou</string>
                    <string>o</string>
                    <string>mail</string>
                    <string>mobile</string>
                    <string>jpegPhoto</string>
                </fetchedAttributes>
                <getAllFilter>(objectClass=inetorgperson)</getAllFilter>
                <getOneFilter>(&amp;(objectClass=inetorgperson)(uid={samAccountName}))</getOneFilter>
            </ldapDestinationService>
            <propertiesBasedSyncOptions>
                <mainIdentifier>"uid=" +
                    srcBean.getDatasetFirstValueById("samAccountName") +
                    ",ou=Users,dc=your_ad_domain,dc=local"</mainIdentifier>
                <defaultDelimiter>;</defaultDelimiter>
                <defaultPolicy>FORCE</defaultPolicy>
                <dataset>
                    <name>description</name>
                    <policy>FORCE</policy>
                    <forceValues>
                        <string>js:(srcBean.getDatasetFirstValueById("sn") != null  ? srcBean.getDatasetFirstValueById("sn").toUpperCase() : null )</string>
                    </forceValues>
                </dataset>
                <dataset>
                    <name>userPassword</name>
                    <policy>KEEP</policy>
                    <createValues>
                        <string>js:"{SASL}" +
                            srcBean.getDatasetFirstValueById("samAccountName") + "@your_ad_domain.local"</string>
                    </createValues>
                </dataset>
                <dataset>
                    <name>sn</name>
                    <!-- <policy>FORCE</policy> -->
                    <policy>KEEP</policy>
                    <createValues>
                        <string>js:srcBean.getDatasetFirstValueById("samAccountName")</string>
                    </createValues>
                </dataset>
                <dataset>
                    <name>description</name>
                    <policy>FORCE</policy>
                    <forceValues>
                        <string>js:(srcBean.getDatasetFirstValueById("sn") != null  ? srcBean.getDatasetFirstValueById("sn").toUpperCase() : null )</string>
                    </forceValues>
                </dataset>
                <dataset>
                    <name>uid</name>
                    <policy>KEEP</policy>
                    <createValues>
                        <string>js:srcBean.getDatasetFirstValueById("samAcccountName")</string>
                    </createValues>
                </dataset>
                <dataset>
                    <name>objectClass</name>
                    <policy>KEEP</policy>
                    <createValues>
                        <string>"inetOrgPerson"</string>
                    </createValues>
                </dataset>
                <dataset>
                    <name>telexNumber</name>
                    <policy>FORCE</policy>
                    <forceValues>
                        <string>js:srcBean.getDatasetFirstValueById("facsimileTelephoneNumber")</string>
                    </forceValues>
                </dataset>
                <dataset>
                    <name>o</name>
                    <policy>FORCE</policy>
                    <forceValues>
                        <string>js:srcBean.getDatasetFirstValueById("company")</string>
                    </forceValues>
                </dataset>
                <dataset>
                    <name>ou</name>
                    <policy>FORCE</policy>
                    <forceValues>
                        <string>js:srcBean.getDatasetFirstValueById("department")</string>
                    </forceValues>
                </dataset>
            </propertiesBasedSyncOptions>
        </task>
        <task>
            <name>Sync_2_Groups</name>
            <bean>org.lsc.beans.SimpleBean</bean>
            <ldapSourceService>
                <name>group-source-service</name>
                <connection reference="ldap-src-conn" />
                <baseDn>DC=your_ad_domain,DC=local</baseDn>
                <pivotAttributes>
                    <string>cn</string>
                </pivotAttributes>
                <fetchedAttributes>
                    <string>cn</string>
                    <string>description</string>
                    <string>member</string>
                    <!-- <string>objectClass</string> -->
                </fetchedAttributes>
                <getAllFilter><![CDATA[(objectClass=group)]]></getAllFilter>
                <getOneFilter><![CDATA[(&(objectClass=group)(cn={cn}))]]></getOneFilter>
                <cleanFilter><![CDATA[(&(objectClass=group)(cn={cn}))]]></cleanFilter>
                <!-- <serverType>ActiveDirectory</serverType> -->
            </ldapSourceService>
            <ldapDestinationService>
                <name>group-dst-service</name>
                <connection reference="ldap-dst-conn" />
                <baseDn>ou=Groups,dc=your_ad_domain,dc=local</baseDn>
                <pivotAttributes>
                    <string>cn</string>
                </pivotAttributes>
                <fetchedAttributes>
                    <string>cn</string>
                    <string>description</string>
                    <string>uniqueMember</string>
                    <string>objectClass</string>
                    <!-- <string>gidNumber</string> -->
                </fetchedAttributes>
                <getAllFilter><![CDATA[(objectClass=groupOfUniqueNames)]]></getAllFilter>
                <getOneFilter><![CDATA[(&(objectClass=groupOfUniqueNames)(cn={cn}))]]></getOneFilter>
            </ldapDestinationService>
            <propertiesBasedSyncOptions>
                <mainIdentifier>js:"cn=" + srcBean.getDatasetFirstValueById("cn") + ",OU=Groups,dc=your_ad_domain,dc=local"</mainIdentifier>
                <defaultDelimiter>;</defaultDelimiter>
                <defaultPolicy>FORCE</defaultPolicy>
                <conditions>
                    <create>true</create>
                    <update>true</update>
                    <delete>true</delete>
                    <changeId>true</changeId>
                </conditions>
                <dataset>
                    <name>uniqueMember</name>
                    <policy>FORCE</policy>
                    <forceValues>
                        <string>
                            <![CDATA[rjs:
                                    var membersSrcDn = srcBean.getDatasetValuesById("member");
                                    var membersDstDn = new java.util.ArrayList();;
                                    for  (var i=0; i < membersSrcDn.size(); i++) {
                                            var memberSrcDn = membersSrcDn.get(i);
                                            var sAMAccountName = "";
                                            try {
                                                    sAMAccountName = srcLdap.attribute(memberSrcDn, "samAccountName").get(0);
                                            } catch(e) {
                                                    continue;
                                            }
                                            var destDn = ldap.search("ou=Users", "(uid=" + sAMAccountName + ")");
                                            if (destDn.size() == 0 || destDn.size() > 1) {
                                                    continue;
                                            }
                                            var destMemberDn = destDn.get(0) + "," +  ldap.getContextDn();
                                            membersDstDn.add(destMemberDn);
                                    }
                                    if (membersDstDn.size() == 0) {
                                        membersDstDn.add("uid=empty_member,dc=your_ad_domain,dc=local");
                                    }
                                    membersDstDn
                            ]]>
                        </string>
                    </forceValues>
                </dataset>
                <dataset>
                    <name>objectClass</name>
                    <policy>FORCE</policy>
                    <forceValues>
                        <string>"groupOfUniqueNames"</string>
                        <string>"top"</string>
                    </forceValues>
                    <delimiter>$</delimiter>
                </dataset>
            </propertiesBasedSyncOptions>
        </task>
    </tasks>
</lsc>

You need a logback configuration in this directory: run cp /etc/lsc/logback.xml /etc/lsc/ad2openldap/

To test is working: /usr/bin/lsc -f /etc/lsc/ad2openldap -s all -c all -n

To real sync, remove -n

Create a crontab run sync every 6 hours: run crontab -e, add 0 */6 * * * /usr/bin/lsc -f /etc/lsc/ad2openldap -s all -c all, save cron.

Use Active Directory to authentication user for LDAP (OpenLDAP pass-through)

Concept: User login, OpenLdap check user password field (userPassword), if it has format: {SASL}[email protected] OpenLdap with authentication user via saslauthd service, saslauthd server with connect to AD and return the result.

$ apt install sasl2-bin
# edit saslauthd
$ nano /etc/default/saslauthd

add content:

# Should saslauthd run automatically on startup? (default: no)
START=yes
# Example: MECHANISMS="pam"
MECHANISMS="ldap"

OpenLDAP service account name: openldap – view in file /etc/default/slapd

Add OpenLDAP service account to the sasl group: adduser openldap sasl

Create file /etc/saslauthd.conf, content

ldap_servers: ldap://ad-server.publicdomain.xyz/
ldap_search_base: DC=your_ad_domain,DC=local
ldap_filter: (sAMAccountName=%u)
ldap_bind_dn: cn=user_to_login_ad,ou=SomeOU,dc=your_ad_domain,dc=local
ldap_password: password_to_login_ad

Restart service to update setting: root@ldap-server:/# service saslauthd restart

To test authentication:

$ testsaslauthd -u some_ad_user_ex -p user_password
0: OK "Success."

Create file /usr/lib/sasl2/slapd.conf, edit file nano /usr/lib/sasl2/slapd.conf, add content

pwcheck_method: saslauthd
saslauthd_path: /run/saslauthd/mux

OpenLdap with check password field, if format {SASL}xxx… it with go to Active Directory to authentication user.

Fix Error:

Jan 21 02:25:58 ldap-server.your_ad_domain.local slapd[20263]: SASL [conn=1001] Failure: cannot connect to saslauthd server: Permission denied

To resolve:

apt install apparmor-utils
aa-complain usr.sbin.slapd

Setting /etc/apparmor.d/usr.sbin.slapd to complain mode.

$ /etc/init.d/slapd restart
$ service saslauthd restart

Done!!!!

Origin post at https://archive.camratus.com/2017/01/24/openldap-lsc-active-directory-sync-and-login-pass-through

Tags:
#OpenLdap #LSC #ActiveDirectory #Sync