Minimalistic LDAP(S) proxy (via Simple-Bind) with Base-DN rewrite

Problem this snippet solves:

The outlined iRule can be used to access independent LDAP(S) instances behind a single Virtual Server using a single Base-DN.

The iRule, will forward LDAP(S) requests to an additional LDAP(S) pool, if the Simple-Bind user account name matches the

$static::other_domains
list.

The iRule would then translate the original Base-DN to the Base-DN matching the additional LDAP(S) instance.

Note1: The iRule doesn't adjust ASN.1/BER structures. So the original and changed Base-DN MUST have the same length. You may have to add SPACE paddings to allign the lenght of the Base-DNs.

Note2: The iRule wouldn't change the traffic for the default LDAP(S) instance. The traffic is passed through after the Simple-Bind request is completed.

Note3: The iRule would translates just the LDAP requests destined to the Base-DN of the additional LDAP(S) instance. The retrived results could then be accessed using their original DNs.

Cheers, Kai

How to use this snippet:

  • Setup a standard Virtual Server for TCP:636.
  • Apply SNAT and TCP profiles as needed.
  • Apply client and server side SSL profiles. The server side SSL profile has to support both instances.
  • Assign the default LDAP(S) instance as default_pool.
  • Tweak the RULE_INIT event to match your environment.

Code :

when RULE_INIT {

#
# Minimalistic LDAP(S) proxy (via Simple-Bind) with Base-DN rewrite
#

# Configuration of the other LDAP(S) instance username prefix/suffixes
set static::other_domains [list "itacs\\" "@itacs.de"];# List of lower case domain strings

# Configuration of the other LDAP(S) instance pool name
set static::other_ldaps_poolname OTHER_LDAPS_POOL_NAME;# Value of the other pool name

# Configuration of the Base-DN translation strings
#
# Important: The Base-DNs MUST have the same lenght.
#            You have to pad SPACES to match the length
#
binary scan "OU=xyz,DC=your-domain,DC=tld" H* temp(dn_default) ;# This is the default Base-DN
binary scan "OU=f5-team, DC=itacs,  DC=de" H* temp(dn_other);# This is the other Base-DN. Pad SPACES to match the length

set static::other_base_dn_map [list $temp(dn_default) $temp(dn_other)]
unset -nocomplain temp

}
when CLIENTSSL_HANDSHAKE {
# SSL session init
set session_binding_ldap 1
set session_other_active 0
# Collecting SSL data
SSL::collect
}
when CLIENTSSL_DATA {
if { $session_binding_ldap } then {
# Searching for simple Bind request to the other LDAP(S) instance 
set session_binding_ldap 0 
foreach temp(domain_string) $static::other_domains {
if { [string tolower [SSL::payload]] contains $temp(domain_string) } then {
# Forwarding the request to the other LDAP(S) instance 
set session_other_active 1
pool $static::other_ldaps_poolname
log -noname local0.debug "Bind request for other LDAP(S) instance detected. Forwarding the connection to pool [LB::server pool]"
break
}
}
if { $session_other_active == 0 } then {
# Forwarding the request to the default LDAP(S) instance
log -noname local0.debug "LDAPS request for default LDAP(S) instance detected. Forwarding the connection to pool [LB::server pool]"
# Releasing SSL data
SSL::release
unset -nocomplain temp
}
}
if { $session_other_active } then {
# Translating Base-DNs for the other LDAP(S) instance
binary scan [SSL::payload] H* temp(hex_ssl_payload)
set temp(new_ssl_payload) [binary format H* [string map $static::other_base_dn_map $temp(hex_ssl_payload)]]
SSL::payload replace 0 [string length [SSL::payload]] $temp(new_ssl_payload)
# Releasing SSL data
SSL::release
# Collecting further SSL data
SSL::collect
unset -nocomplain temp
}
}

Tested this on version:

12.0
Updated Jun 06, 2023
Version 2.0

Was this article helpful?

No CommentsBe the first to comment