Background

To ensure that sensitive information is kept secret from potential eavesdroppers, TCP/IP includes secure protocols (SSL/TLS) which have been designed specifically to achieve true end-to-end security. These protocols work by following a handshake algorithm to establish a shared encryption key which is known only to the client and the secure server. Once the handshake is complete, all subsequent data transfer between the client and server are encrypted. Since the client and secure server are the only devices which know the shared encryption key, they alone are able to decrypt the communications using the shared encryption key.

A special case occurs when the client must use a proxy server to connect to a remote secure server. The proxy server must connect the client to the remote secure server so the handshake can take place to establish the shared encryption key. To maintain true end-to-end security, this encryption key must remain unknown to the proxy server .

To handle this special case, HTTP has a special method: CONNECT. This method is used by a client to instruct a proxy server to establish a connection with a remote server so that handshaking between the client and the remote server can take place. At the end of the handshake, the shared encryption key is established between the client and the remote secure server. All subsequent communications between the client and the remote secure server are encrypted using that key and sent to the proxy server which then acts only as a data relay between the client and the remote secure server. This is known as tunneling through the proxy. Since the proxy server does not know the encryption key, it cannot examine the data in the communications, and end-to-end security is preserved.


The Problem

Many of our customers want to use LTM to load balance proxy servers (or otherwise tunnel traffic via HTTP using the "CONNECT" method). They report that traffic doesn't seem to flow as expected with an HTTP profile applied to the virtual server: LTM forwards the request to the server, but the connection stalls upon a "200 OK" response from the server.

Removing the HTTP profile is only a solution if no HTTP profile-based features are required, so that can result in a bit of a chicken & egg situation if you need to forward CONNECT requests while still taking advantage of HTTP profile-based features for other traffic.

Modifications to the http profile to fully support the CONNECT method are in the works, but in the meantime, here's a simple iRule workaround.


The Solution

By configuring an HTTP virtual server with an iRule, you can still use HTTP events to examine each request for the CONNECT method (or any other HTTP conditions to which you might need to react). When the request contains the CONNECT method, you can disable HTTP processing with the HTTP::disable command (added in LTM 9.0.5 for just this purpose), allowing the remainder of the connection to pass through LTM unaffected by the HTTP parser.

Start by configuring a Standard virtual server with an HTTP profile, the proxy servers as the load balancing pool. Then apply an iRule looking for the CONNECT header.

Here's the basic iRule to disable HTTP processing when a request using the CONNECT method is seen:

 when HTTP_REQUEST {
   if { [HTTP::method] eq "CONNECT" } {
     HTTP::disable
   }
 } 


If you need to include some additional traffic management logic for non-tunneled HTTP traffic, you can add that to the same iRule:

 when HTTP_REQUEST {
   if { [string toupper [HTTP::method]] eq "CONNECT" } {
     HTTP::disable
   } else {
     if { [HTTP::uri] starts_with "/admin" && !([IP::addr [IP::client_addr] equals 10.0.0.0/8]) }{
       reject return
     }
     switch [HTTP::host] {
       "some.host.com" { pool somepool }
       "someother.host.com" { pool someotherpool }
     }
   }
 } 


Caveats


Since HTTP events are only triggered when HTTP processing is enabled, once HTTP is disabled in this fashion for a connection, the HTTP events in your iRule are effectively disabled as well for the life of that connection. There are corner cases in which HTTP-like protocols might need HTTP processing selectively re-enabled when conditions change. LTM iRules also include the HTTP::enable command for that purpose, but it requires capturing data at the TCP level with TCP::collect and coding the logic to parse each packet for the appropriate trigger in the CLIENT_DATA event. That can be somewhat tricky, and is beyond the scope of this article. If you need assistance with such a solution, please post to the iRules forum and we'll do what we can to help.