Multiple LDAP Servers Integration

When an administative solution (like Global Catalog) is not possible or wanted and we have to integrate more LDAP servers under one hood there is a simple way how to do it with MyVirtualDirectory.


MyVirtualDirectory (MyVD) offers much more than an integration of multiple LDAP servers, actually anything could be exposed as a LDAP service via MyVD. In this tutorial we focus only on LDAP.

Simple LDAP Integration

We begin with a simple example of two LDAP servers integration.

We have one LDAP running in server1.com network on the port 398 and another running in server2.com on the same port.

We integrate the server in out local network on the port 50983 as shows the following picture:

Simple LDAP Integration

First, let's set up the MyVD server (HTTP in our case).

server.listener.port=50983

That's it. Now the integration of the servers:

server.nameSpaces=server1,server2
 
server.server1.nameSpace=dc=server1,dc=com
server.server1.weight=100
server.server1.chain=ldap
server.server1.ldap.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.server1.ldap.config.host=server1.com
server.server1.ldap.config.port=389
server.server1.ldap.config.remoteBase=dc=server1,dc=com
server.server1.ldap.config.proxyDN=uid=testuser1,ou=people,dc=server1,dc=com
server.server1.ldap.config.proxyPass=123
 
server.server2.nameSpace=dc=server2,dc=com
server.server2.weight=100
server.server2.chain=ldap
server.server2.ldap.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.server2.ldap.config.host=server2.com
server.server2.ldap.config.port=389
server.server2.ldap.config.remoteBase=dc=server2,dc=com
server.server2.ldap.config.proxyDN=uid=testuser2,ou=people,dc=server2,dc=com
server.server2.ldap.config.proxyPass=123

LDAP Client

As a non-trivial client example let's consider the Spring Security LDAP.

Simple said, the Spring Security LDAP does two search queries to the LDAP server:

Spring allow the user to redefine the search bases and filters for different LDAP structures and uses placeholders (so the filter looks like (uid={0})). For the MyVD setting above let's set our client as following:

user search base dc=com
user search filter (uid={0})
groups search base dc=com
groups search filter (uid={0})

Namespaces Integration

In our first example we had two server with one base in common dc=com. But what happend when we have to integrate multiple server with different bases? This is what the property server.<server>.nameSpace is meant for.

Namespaces Integration

...
server.server1.nameSpace=dc=mycom,dc=com
...
server.server1.ldap.config.host=server1.org
server.server1.ldap.config.remoteBase=dc=server1,dc=org
server.server1.ldap.config.proxyDN=uid=testuser1,ou=people,dc=server1,dc=org
...
server.server2.nameSpace=dc=mycom,dc=com
...
server.server2.ldap.config.host=server2.net
server.server2.ldap.config.remoteBase=dc=server2,dc=net
server.server2.ldap.config.proxyDN=uid=testuser2,ou=people,dc=server2,dc=net
...

Now we can change our search base to dc=mycom,dc=com. Unfortunately this doesn't work. The problem is the DN of the result user entity is mapped to the integration namespace. It means for the username testuser1 we get instead of uid=testuser1,ou=people,dc=server1,dc=com a DN uid=testuser1,ou=people,dc=mycom,dc=com and that doesn't match the value of the member attribute of group entities.

MyVD brings a solution in Mapping Inserts:

...
server.server1.chain=dnMapper,ldap
...
server.server1.dnMapper.className=net.sourceforge.myvd.inserts.mapping.DNAttributeMapper
server.server1.dnMapper.config.dnAttribs=member
server.server1.dnMapper.config.remoteBase=dc=server1,dc=org
server.server1.dnMapper.config.localBase=dc=mycom,dc=com
...
server.server2.chain=dnMapper,ldap
...
server.server2.dnMapper.className=net.sourceforge.myvd.inserts.mapping.DNAttributeMapper
server.server2.dnMapper.config.dnAttribs=member
server.server2.dnMapper.config.remoteBase=dc=server2,dc=net
server.server2.dnMapper.config.localBase=dc=mycom,dc=com
...

The DN Attribute Mapper maps specified attributted back to the original namespace. Now the search works again.

Integration of Heterogeneous Services

As we mentioned in the beginning MyVD provides the possibility to integrate different services. So what happend when we integrate a standard LDAP server (e.g. OpenLDAP) and Active Directory (AD)? We are still on LDAP field but the details are different. For example AD's user entity holds the username in an sAMAccountName attribute. This means we have to integrate those heterogeneous attributes to be searchable with one client search query.

Integration of Heterogeneous Services

Sure, we can compose the search filter like (|(uid={0})(sAMAccountName={0})), which will work fine, but it exposes implementation details to the client and breaks so the encapsulation principle. The client shouldn't know anything about the backend server, it should treat the service as a single LDAP server.

Fortunately, there is MyVD's Attribute Mapper:

...
server.server2.chain=uidMapper,dnMapper,ldap
...
server.server2.uidMapper.className=net.sourceforge.myvd.inserts.mapping.AttributeMapper
server.server2.uidMapper.config.mapping=sAMAccountName=uid
...

Now works everything fine. The whole MyVD configuration file:

server.listener.port=50983
 
server.nameSpaces=server1,server2
 
server.server1.nameSpace=dc=mycom,dc=com
server.server1.weight=100
server.server1.chain=dnMapper,ldap
server.server1.ldap.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.server1.ldap.config.host=server1.org
server.server1.ldap.config.port=389
server.server1.ldap.config.remoteBase=dc=server1,dc=org
server.server1.ldap.config.proxyDN=uid=testuser1,ou=people,dc=server1,dc=org
server.server1.ldap.config.proxyPass=123
 
server.server1.dnMapper.className=net.sourceforge.myvd.inserts.mapping.DNAttributeMapper
server.server1.dnMapper.config.dnAttribs=member
server.server1.dnMapper.config.remoteBase=dc=server1,dc=org
server.server1.dnMapper.config.localBase=dc=mycom,dc=com
 
server.server2.nameSpace=dc=mycom,dc=com
server.server2.weight=100
server.server2.chain=uidMapper,dnMapper,ldap
server.server2.ldap.className=net.sourceforge.myvd.inserts.ldap.LDAPInterceptor
server.server2.ldap.config.host=server2.net
server.server2.ldap.config.port=389
server.server2.ldap.config.remoteBase=dc=server2,dc=net
server.server2.ldap.config.proxyDN=uid=testuser2,ou=people,dc=server2,dc=net
server.server2.ldap.config.proxyPass=123
 
server.server2.dnMapper.className=net.sourceforge.myvd.inserts.mapping.DNAttributeMapper
server.server2.dnMapper.config.dnAttribs=member
server.server2.dnMapper.config.remoteBase=dc=server2,dc=net
server.server2.dnMapper.config.localBase=dc=mycom,dc=com
 
server.server2.uidMapper.className=net.sourceforge.myvd.inserts.mapping.AttributeMapper
server.server2.uidMapper.config.mapping=sAMAccountName=uid

Happy integrating!