on 09-Oct-2008 06:44
LDAP is one of the most widely used authentication protocols around today. There are plenty of others, but LDAP is undeniably one of the big ones. It comes as no surprise then that we often hear different questions about using F5 technology with LDAP servers on the back-end. Whether people are looking for more performance, increased reliability and availability through load-balancing, or just more flexibility, there are many things that we can do to help.
A great example is improving performance. This example is driven from a client's requirements to reduce the overhead on their LDAP systems. They wanted to do so in a particular way, however. They were receiving a high-volume of very short-lived connections that all needed to query the back-end LDAP systems for information. Each one of these connections would open a new connection and as it turns out the overhead of setting up and tearing down the TCP connections was creating a fair amount of churn on their server, due to the high volume and short duration of the requests. Seeing this, they turned to their BIG-IP, looking for a solution.
As luck would have it, iRules was able to step in to help them accomplish just what they were looking for. Thanks to one of the many bright engineers here at F5, Nat Thirasuttakorn, they were able to leave server-side connections to the LDAP systems open for long periods of time and just manage the handshakes on the client side, thereby greatly reducing the overhead on the LDAP servers.
Below is the iRule that Nat was kind enough to share with me so I could pass it along to the DevCentral community. What it does is listen to the LDAP traffic, watching for an unbind to occur. Once the iRule sees an unbind from the client, which would normally be sent to the LDAP server terminating the connection, it simply uses the LB::detach command to detach the back-end connection at the BIG-IP, and tosses the unbind command itself so the LDAP server never sees it. This leaves the server's connection to the BIG-IP open and available for the next request that comes in.
It's important to understand that the LB::detach command isn't terminating any connection, even though the name might sound like it. All it's doing is detaching the current session from the connection it's established to the server in question, allowing future sessions to make use of the connection. This will, in essence, makes it look to the LDAP server that there's a single (or several), long-lived connection being held open with many requests flowing through. What's really happening is the BIG-IP brokering requests from the client and using already established back-end connections to keep overhead down to a minimum. This is the beauty of OneConnect (which is required for this solution to work) and iRules on the BIG-IP's TMOS architecture.
I don't have any performance numbers to share, but I'm willing to bet the BIG-IP is a fair amount more efficient at managing those large numbers of short-lived connections than an LDAP server is going to be. That means not only are you gaining the overhead back on your auth server, but you’re not really losing much on the BIG-IP. That makes it a win-win. Again, many thanks to Nat for sharing the below code.
when CLIENT_ACCEPTED { TCP::collect } when CLIENT_DATA { binary scan [TCP::payload] xc ber_len if { $ber_len < 0 } { set ber_index [expr 2 + 128 + $ber_len] } else { set ber_index 2 } # message id binary scan [TCP::payload] @${ber_index}xcI ber_len ber_len_ext if { $ber_len < 0 } { set ext_len [expr 128 + $ber_len] set ber_len [expr ($ber_len_ext & 0xffffffff) >>(4-$ext_len)*8)] } else { set ext_len 0 } incr ber_index [expr 2 + $ext_len + $ber_len] # ldap message binary scan [TCP::payload] @${ber_index}c ber_type if { [expr $ber_type & 0x1f] == 2 } { log local0. "unbind => detach" TCP::payload replace 0 [TCP::payload length] "" LB::detach } TCP::release TCP::collect }
I though LDAP connections were authenticated when they were established; do you handle the case where an authenticated connection is left open?
Any ideas?
Thanks
I believe most LDAP implementations support implicit unbind when TCP connection is closed. However, I am not LDAP expert at all. I know LDAP from Networking perspective not LDAP admin. So any feedback is very appreciated.
answer to rasputnik's question:
Yes, this iRule intends to demonstrate simple technique to keep server connection open. It is assume that servers support rebind in the same TCP connection. It is also possible to use iRule to strip off bind message.
This iRule is suitable for simple and secure environment such as between web-tier and application tier. To use this technique in a more secure way, security check should be added to the iRule, for example, make sure the first message from client is a bind message, restrict access to some IP addresses, etc.
answer to tbernath's question
1) the client is binding on each request, I want to eat those on the F5
you can use the same technique. This iRule detects unbind message type (which is 2) at the below line
if { [expr $ber_type & 0x1f] == 2 } {
You may just change it to 0. (bind request message type is one). Don't detach, swallow the bind request using TCP::payload command and send bind successful to client with TCP::respond. Here is a basic example
if { [expr $ber_type & 0x1f] == 0 } {
TCP::payload replace 0 [TCP::payload length] ""
TCP::respond $bind_success
}
If client always use message id 1, anonymous bind and same bind dn, this bind response message may always be the same. Otherwise, you may need to craft bind_response message on the fly.
2) do the rebind automatically
One technique that you can use is to save previous Client's bind message for reuse. Once there is a need to rebind. You can either put the saved bind message in the beginning of TCP::payload or use TCP::respond in server_connected event. here is an example:
TCP::payload replace 0 0 $save_bind_message
to save bind message, you may simply catch it in the first time that client send bind message and perform this.
set save_bind_message [TCP::payload]
(assume TCP::payload only contain bind message)
This technique may only work with simple authentication and not SASL. SASL may require more complicated iRule or it may not be possible at all.
If you have further questions, please post them to iRule forum. (Usually, I don't monitor this page and I have to sorry for late reply)
Thank you very much for all questions and comments,
Nat
However, there is a bug 🙂
this line:
set ber_len [expr (($ber_len_ext>>(4-$ext_len)*8)+(0x100^$ext_len))%(0x100^$ext_len)]
should be changed to:
set ber_len [expr ($ber_len_ext & 0xffffffff) >>(4-$ext_len)*8)]
I got error:
syntax error in expression "(16777216 & 0xffffffff) >>(4-22)*8)": extra tokens at end of expression while executing "expr ($ber_len_ext & 0xffffffff) >>(4-$ext_len)*8)"
Should be:
set ber_len [expr ($ber_len_ext & 0xffffffff) >>(4-$ext_len)*8]
I would think you'd want to be able to log the client source IP to track where your users are coming in from - if not for real time monitoring than for forensics after a problem. ("hey, Alice started having problems right after that ldap auth request came in from an address in China!")
We are facing an Issue where we use LDAP V3 and Directory Server 6.3.1.1.1 .
One of the Application using Domino is used by devices like iphones and ipads and making many hits on the server .
The hits made by the application are not clean.
The Application does not UNBINDS after establishing a new connection and gets closed by T1 error and this chain goes on which is impacting our infrastructure.
The code provided by you will it help in this case??
and if yes how and where should we implement this code??
Application Logs:
[27/Feb/2013:11:11:59 +0000] conn=100178 op=1 msgId=2 - UNBIND
[27/Feb/2013:11:11:59 +0000] conn=100178 op=1 msgId=-1 - closing from 10.71.8.19:59631 - U1 - Connection closed by unbind client -
[27/Feb/2013:11:11:59 +0000] conn=100178 op=-1 msgId=-1 - closed.
This code seems to do the opposite of what you are looking for. It prevents UNBIND message to be sent to server so that it can keep server connection open for reuse.
If I understand correctly, in your case, you might want to detect idle client connection in iRule, and try to UNBIND the connection before it is terminated by server? or please feel free to correct me if I am wrong.
maybe something like this...
when CLIENT_ACCEPTED {
set client_idle_time 0
set disconnect [expr [IP::idle_timeout] * 0.8]
after 1000 -periodic {
incr client_idle_time
if { $client_idle_time > $disconnect } {
unbind
TCP::payload replace 0 0 $static::unbind_message
}
}
}
when CLIENT_DATA {
reset idle time when there is data
set client_idle_time 0
}
the $static::unbind_message could be created in RULE_INIT
for example, use something like
set static::unbind_message [binary format H* "30050201034200"]
Nat