ldap proxy
2 TopicsMinimalistic LDAP proxy (via Simple-Bind) with Base-DN rewrite
Problem this snippet solves: The outlined iRule can be used to access independent LDAP instances behind a single Virtual Server using a single Base-DN. The iRule, will forward LDAP requests to an additional LDAP 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 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 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 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:386. Apply SNAT and TCP profiles as needed. Assign the default LDAP instance as default_pool. Tweak the RULE_INIT event to match your environment. Code : when RULE_INIT { # # Minimalistic LDAP proxy (via Simple-Bind) with Base-DN rewrite # # Configuration of the other LDAP instance username prefix/suffixes set static::other_domains [list "itacs\\" "@itacs.de"];# List of lower case domain strings # Configuration of the other LDAP instance pool name set static::other_ldap_poolname OTHER_LDAP_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 CLIENT_ACCEPTED { # TCP session init set session_binding_ldap 1 set session_other_active 0 # Collecting TCP data TCP::collect } when CLIENT_DATA { if { $session_binding_ldap } then { # Searching for simple Bind request to the other LDAP instance set session_binding_ldap 0 foreach temp(domain_string) $static::other_domains { if { [string tolower [TCP::payload]] contains $temp(domain_string) } then { # Forwarding the request to the other LDAP instance set session_other_active 1 pool $static::other_ldap_poolname log -noname local0.debug "LDAP simple bind request for other LDAP instance detected. Forwarding the connection to pool [LB::server pool]" break } } if { $session_other_active == 0 } then { # Forwarding the request to the default LDAP instance log -noname local0.debug "LDAP request for default LDAP instance detected. Forwarding the connection to pool [LB::server pool]" # Releasing TCP data TCP::release unset -nocomplain temp } } if { $session_other_active } then { # Translating Base-DNs for the other LDAP instance binary scan [TCP::payload] H* temp(hex_tcp_payload) set temp(new_tcp_payload) [binary format H* [string map $static::other_base_dn_map $temp(hex_tcp_payload)]] TCP::payload replace 0 [string length [TCP::payload]] $temp(new_tcp_payload) # Releasing TCP data TCP::release # Collecting further TCP data TCP::collect unset -nocomplain temp } } Tested this on version: 12.0539Views0likes0CommentsLDAP StartTLS Extension to LDAPS Proxy
Problem this snippet solves: The outlined iRule implements a LDAP StartTLS-Extension (see RFC2830) to traditional LDAPS proxy. The iRule can be used to add StartTLS-Extension support to LDAP instances, which do not support the StartTLS extension. The iRule will inspect the first TCP datagram for the LDAP-StartTLS request OID. If a StartTLS request is identified, the iRule will parse the ASN.1/BER structure of the LDAP request to extract the LDAP MessageID. Once the MessageID is extracted, the iRule will respond on behalf of the LDAP server with a LDAP response containing the request MessageID and StartTLS OID and then forward the remaining server side connection to a traditional LDAPS pool. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_ACCEPTED>: Client 172.21.92.28%1 is Connected to VS 172.21.17.211%1:389 Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_ACCEPTED>: Starting to collect LDAP TCP data. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Received LDAP TCP data. Searching for LDAP STARTTLS request OID. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Parsing 1st BER encoded message envelope (type and length). Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Checking 1st BER encoded message type = sequence (0x30). Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: 1st BER encoded message type is sequence (0x30). Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Checking if 1st BER encoded length value is a Multi-Byte length field. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Multi-Byte length field detected. Parsing the Multi-Byte length field value. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Adding the Multi-Byte length to scan offset. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Parsing 2nd BER encoded message envelope (type and length). Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Checking 2nd BER encoded message type = integer (0x02) Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: 1st BER encoded message type is integer (0x02). Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Checking if 1st BER encoded length value is a Multi-Byte length field. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Extracting 2nd BER encoded message value = MessageId. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Sending STARTTLS reply with matching LDAP MessageID to client. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Selecting LDAPS:683 pool. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Clearing the collected payload buffer. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Unset temporary array variable. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <CLIENT_DATA>: Releasing the connection. Mon Feb 29 09:41:42 CET 2016 debug f5-02 tmm1[2649] Rule /Common/LDAP_STARTTLS <SERVER_CONNECTED>: Client 172.21.92.28%1 is forwarded to 172.21.91.20%1:636 Cheers, Kai How to use this snippet: Usage: Create a new iRule and copy/paste the provided iRule into it. Modify the $static::ldaps_pool variable to match your LDAPS:636 pool name. Attach the iRule to your LDAP:386 Vitual Server. Test the iRule with an StartTLS aware LDAP client pointing to the LDAP:386 Vitual Server. Code : when RULE_INIT { set static::ldaps_pool "INSERT_YOUR_LDAPS_POOL_NAME" array set static::hex_filler { 1 "0000000" 2 "000000" 3 "00000" 4 "0000" 5 "000" 6 "00" 7 "0" 8 "" } } when CLIENT_ACCEPTED { log local0.debug "Client [IP::client_addr] is Connected to VS [IP::local_addr]:[TCP::local_port]" # log local0.debug "Starting to collect LDAP TCP data." TCP::collect } when CLIENT_DATA { # log local0.debug "Received LDAP TCP data. Searching for LDAP STARTTLS request OID." if { [TCP::payload] contains "1.3.6.1.4.1.1466.20037" } then { if { [catch { # log local0.debug "Parsing 1st BER encoded message envelope (type and length)." binary scan [TCP::payload] H2c temp(ber_1_type) temp(ber_1_length) set temp(scan_offset) 2 # log local0.debug "Checking 1st BER encoded message type = sequence (0x30)." if { $temp(ber_1_type) eq "30" } then { # log local0.debug "1st BER encoded message type is sequence (0x30)." # log local0.debug "Checking if 1st BER encoded length value is a Multi-Byte length field." if { [expr { $temp(ber_1_length) & 0x80 }] } then { # log local0.debug "Multi-Byte length field detected. Parsing the Multi-Byte length field value." # Diabled: binary scan [TCP::payload] x$temp(scan_offset)H[expr { ($temp(ber_1_length) & 0x7F) * 2 }] temp(ber_1_mlength) # log local0.debug "Adding the Multi-Byte length to scan offset." incr temp(scan_offset) [expr { $temp(ber_1_length) & 0x7F }] # Diabled: log local0.debug "Formating the received HEX string to numeric value." # Diabled: set temp(ber_1_length) [expr { [subst "0x$temp(ber_1_mlength)"] }] } # Diabled: log local0.debug "Extracting 1nd BER encoded message value = XXX." # Diabled: binary scan [TCP::payload] x$temp(scan_offset)H[expr { $temp(ber_2_length) * 2 }] temp(ber_2_value) # log local0.debug "Parsing 2nd BER encoded message envelope (type and length)." binary scan [TCP::payload] x$temp(scan_offset)H2c temp(ber_2_type) temp(ber_2_length) incr temp(scan_offset) 2 # log local0.debug "Checking 2nd BER encoded message type = integer (0x02)" if { $temp(ber_2_type) eq "02" } then { # log local0.debug "1st BER encoded message type is integer (0x02)." # log local0.debug "Checking if 1st BER encoded length value is a Multi-Byte length field." if { [expr { $temp(ber_2_length) & 0x80 }] } then { # log local0.debug "Multi-Byte length field detected. Parsing the Multi-Byte length field value." binary scan [TCP::payload] x$temp(scan_offset)H[expr { ($temp(ber_2_length) & 0x7F) * 2 }] temp(ber_2_mlength) # log local0.debug "Adding the Multi-Byte length to scan offset." incr temp(scan_offset) [expr { $temp(ber_2_length) & 0x7F }] # log local0.debug "Formating the received HEX string to numeric value." set temp(ber_2_length) [expr { [subst "0x$temp(ber_2_mlength)"] }] } # log local0.debug "Extracting 2nd BER encoded message value = MessageId." binary scan [TCP::payload] x$temp(scan_offset)H[expr { $temp(ber_2_length) * 2 }] temp(ber_2_value) # log local0.debug "Sending STARTTLS reply with matching LDAP MessageID to client." TCP::respond [binary format H* "30840000002B0204$static::hex_filler([string length $temp(ber_2_value)])$temp(ber_2_value)78840000001f0a0100040004008a16312e332e362e312e342e312e313436362e3230303337"] # log local0.debug "Selecting LDAPS:683 pool $static::ldaps_pool." pool $static::ldaps_pool # log local0.debug "Clearing the collected payload buffer." TCP::payload replace 0 [TCP::payload length] "" } } }]} then { log local0.debug "Parsing of BER encoded message envelope raised an error." log local0.debug "Extended ErrorInfo = [subst "\$::errorInfo"]" } # log local0.debug "Unset temporary array variable." unset -nocomplain temp } # log local0.debug "Releasing the connection." TCP::release } when SERVER_CONNECTED { log local0.debug "Client [IP::client_addr] is forwarded to [LB::server addr]:[LB::server port]" } Tested this on version: 12.0514Views0likes0Comments