Forum Discussion

bernie_9326's avatar
bernie_9326
Icon for Nimbostratus rankNimbostratus
Feb 24, 2010

in layer 7 routing, how to direct request under SSL

Hi

Wondering if you can help. We are trying to use layer 7 routing to send web requests to three different pools.

Our questions are - is there a way to set up Layer 7 routing such that

a. if the requests come in as HTTPS, we can interpret the URI and the redirected request goes up as HTTPS (see issues in Test 2 below)

b. if the requests come in as HTTP, we simply redirect along as HTTP.

Our test iRule is:

 
 when HTTP_REQUEST { 
   log local0. "uri=[HTTP::uri]" 
   set uri [HTTP::uri] 
   if {$uri starts_with "/acme"} { 
       log local0. "  going to pool-acme" 
       pool pool-acme 
   } elseif {$uri starts_with "/onlyssl"} { 
       pool pool-onlyssl 
   } else { 
       pool pool-bob 
   } 
 } 
 

Test 1

When we send in the request as HTTP (e.g., http://vip/acme), the iRule works. The request comes in and goes to pool-acme.

Test 2

if we send in the request as httpS://vip/onlyssl (because the servers in pool-onlyssl only serves requests coming in as HTTPS), the HTTP::uri returns what appears to be encrypted string. Specifically, it is not /onlyssl but is instead something like "uri=~G?}?~_?X??\R".

Our suspicion is that the request is still under SSL hence the URI is still encrypted.

Test 3

If we set up the virtual server to break the SSL. We could be wrong but we added "clientssl" to Virtual Server > Configuration > SSL Profile (client). When we did that, the logging now shows "uri=/onlyssl". However, the request going to the onlyssl servers are in HTTP. We needed them to be in SSL.

Test 4

We then also added "serverssl" to SSL Profile (server). Now the requests goes all the way to the onlyssl servers as HTTPS and we get a clean response.

Test 5

But now the problem is that the original request in Test 1 fails.

8 Replies

  • Hi Bernie,

    Are you running 2 virtual address, one running on http and other on https

    In that case you could use the following on HTTP

     
     when HTTP_REQUEST {  
        log local0. "uri=[HTTP::uri]"  
        if {[HTTP::uri] starts_with "/acme"} {  
            log local0. "  going to pool-acme"  
            pool pool-acme  
           } 
     } 
     

    The other can be applied to HTTPS

     
     when HTTP_REQUEST {  
        log local0. "uri=[HTTP::uri]"  
        switch -glob [string tolower [HTTP::uri]] { 
         "/onlyssl*" { pool pool-onlyssl } 
         default { pool pool-bob } 
         } 
     } 
     

    I hope this helps

    Bhattman

  • Bhattman

     

    Thanks for your reply. Unfortunately, we are running under one virtual address (vip). The reason is for cross-domain communication. The containing (parent) HTML from /acme needs to be able to grab data from iFrames that get data from /onlyssl and /bob. We are using the single vip to make all requests appear to be coming from the same URL domain.

     

     

    Our root issue is HTTP::uri returning the encrypted URI under HTTPS. I believe our problem would be solved if HTTP::uri could return the actual uri so we can route even when the request comes up as HTTPS.

     

     

    Thanks again.

     

     

    Bernie
  • Hi Bernie

     

    Hoolio created an excellent irule to run http and https on the same virtual address (Click here)

     

     

    Here is the the same code that has your specifics (Of course it's untested so there might be some fine tunning)

     

     

     
     when RULE_INIT { 
      
         Requests to ports not defined in either the https or http ports list will be reset 
         Set this option to 1 to redirect client requests from HTTP to HTTPS.  Set to 0 to not redirect clients from HTTP to HTTPS. 
        set ::redirect_http_to_https 0 
      
         Set this option to 1 to rewrite the requested URI to lower case.  Set to 0 to not rewrite the URI. 
        set ::rewrite_uri_to_lower_case 1 
      
         Set this option to 1 to log debug messages (to /var/log/ltm by default) 
        set ::single_vs_debug 1     
     } 
     when CLIENT_ACCEPTED { 
      
         Save the VIP name, client IP:port as a log prefix to make the log lines shorter 
        set log_prefix "[IP::client_addr]:[TCP::client_port]" 
      
        if { [matchclass [TCP::local_port] equals "443" }{ 
      
            Request was to an HTTPS port, so do nothing for the clientside connection.   
            The defined client and/or server SSL profiles will be applied as normal 
           if {$::single_vs_debug}{log local0. "$log_prefix: HTTPS request to [IP::local_addr]:[TCP::local_port]"} 
      
            log an error if the virtual server doesn't have a client SSL profile, but receives an SSL request 
           if {[PROFILE::exists clientssl] == 0}{ 
              if {$::single_vs_debug}{log local0. "$log_prefix:\ 
                 Client connection received on port [TCP::local_port], but no client SSL profile is enabled on [IP::local_addr]"} 
              reject 
           } 
          log local0. "uri=[HTTP::uri]"   
         switch -glob [string tolower [HTTP::uri]] {  
          "/onlyssl*" { pool pool-onlyssl }  
          default { pool pool-bob }  
          }  
           
      
      
        } elseif {([matchclass [TCP::local_port] equals "80"]) }{ 
      
            Request was to an HTTP port, not an HTTPS port, so disable client SSL profile if one is enabled on the VIP 
           set vip_http_port 1 
           if {$::single_vs_debug}{log local0. "$log_prefix: HTTP request to [IP::local_addr]:[TCP::local_port]"} 
      
            Check to see if there is a client SSL profile and if so, disable it 
           if { [PROFILE::exists clientssl] == 1} { 
              if {$::single_vs_debug}{log local0. "$log_prefix: Client SSL profile enabled on VIP.  Disabling SSL."} 
              set disable_cmd "SSL::disable" 
              eval $disable_cmd 
      
            if {[HTTP::uri] starts_with "/acme"} {   
                log local0. "  going to pool-acme"   
             pool pool-acme   
            }  
      
      
           }  
        } else { 
      
            Request wasn't to a defined port, so reset the TCP connection. 
           if {$::single_vs_debug}{log local0. "$log_prefix:\ 
              Dropping request to undefined port [IP::local_addr]:[TCP::local_port]"} 
           reject 
        } 
     } 
     when HTTP_REQUEST { 
      
         If redirect_http_to_https is enabled and the request was made to an HTTP port, 
           redirect the client to the same host/URI over HTTPS 
        if { ($::redirect_http_to_https == 1 or ([info exists redirect_http_to_https] && $redirect_http_to_https)) && \ 
           ([info exists vip_http_port] && $vip_http_port==1)}{ 
      
           HTTP::redirect https://[getfield [HTTP::host] ":" 1][HTTP::uri] 
           if {$::single_vs_debug}{log local0. "$log_prefix:\ 
              Redirecting client [IP::client_addr] to https://[getfield [HTTP::host] \":\" 1][HTTP::uri]"} 
      
        } else { 
      
            Rewrite the HTTP::path to lower case if the option is enabled globally or in a separate rule 
           if {$::rewrite_uri_to_lower_case or ([info exists rewrite_uri_to_lower_case] and $rewrite_uri_to_lower_case)}{ 
              HTTP::path [string tolower [HTTP::path]] 
           } 
        }      
     } 
      
     

     

     

    I hope this helps

     

     

  • Hey Bhattman

    Wow - thanks for that sample. I am thinking that my problem is a little bit simpler. I apologize if I didn't explain the problem clearer.

    Here is the description of what we would like to do

    1. all traffic (both HTTP and HTTPS) go to a single virtual address (vip).

    2. we want to route requests based on URI (layer 7 routing) to one of three different load-balancing pools (call them pool-acme, pool-bob and pool-onlyssl).

    3. F5 when routing preserves the protocol - if requests comes in as HTTP, it is sent to the pool as HTTP

    We have the following iRule that seems to do the trick

     
     when HTTP_REQUEST {  
        log local0. "uri=[HTTP::uri]"  
        set uri [HTTP::uri]  
        if {$uri starts_with "/acme"} {  
            log local0. "  going to pool-acme"  
            pool pool-acme  
        } elseif {$uri starts_with "/onlyssl"} {  
            pool pool-onlyssl  
        } else {  
            pool pool-bob  
        }  
      }  
     

    EXCEPT when the request comes in as HTTPS. When in HTTPS, the value of HTTP::uri appears to be the encrypted uri which breaks our Layer 7 routing decision.

    Question 1 - is there a way we could get the value of HTTP::uri when the request comes in HTTPS

    Alternatively, we could set up the F5 to be the SSL break. In that case, when requests comes in as

    1. HTTP: look at and route based on uri - leave protocol as HTTP

    2. HTTPS: then break SSL, route based on uri, then send to pool as HTTPS

    Bernie
  • Hi Bernie,

     

    Looking back at your earlier posts, you mentioned that you couldn't use 2 Virtual addresses. After re-reading that statement I think I wasn't clear in explaining. You can create 2 Virtual address using the same IP address but different ports on them. This way your domains will always point to the same address and be able to separately handle the SSL termination on the F5 for HTTPS and HTTP separately.

     

     

    Bhattman

     

     

  • Hi Bernie,

    In order to inspect or modify the HTTP headers or payload for HTTPS traffic, you need to import the cert/key to LTM and configure them in a client SSL profile. You can then use an iRule like the one you've posted to inspect the HTTP and select a pool based on the URI.

    Also, you can use a switch statement to evaluate the URI. It's a bit more efficient and easier to organize.

     
     when HTTP_REQUEST { 
        log local0. "uri=[HTTP::uri]" 
      
         Check the requested URI 
        switch -glob [HTTP::uri] { 
           "/acme*" { 
              log local0. "  going to pool-acme" 
              pool pool-acme 
           } 
           "/onlyssl*"} { 
              pool pool-onlyssl 
              log local0. "  going to onlyssl" 
           } 
           default { 
              pool pool-bob 
              log local0. "  going to pool-bob" 
           } 
        } 
     } 
     

    Aaron
  • Posted By Bhattman on 02/25/2010 4:34 AM

     

     

    Hi Bernie,

     

    Looking back at your earlier posts, you mentioned that you couldn't use 2 Virtual addresses. After re-reading that statement I think I wasn't clear in explaining. You can create 2 Virtual address using the same IP address but different ports on them. This way your domains will always point to the same address and be able to separately handle the SSL termination on the F5 for HTTPS and HTTP separately.

     

     

    Bhattman

     

     

     

     

     

    Bhattman, you are right!!!! It worked. Thanks!!!!

     

     

  • Aaron

     

    Thanks for those two suggestions. We will definitely try it out.

     

    Bernie