Forum Discussion

Durga_Dash_2124's avatar
Durga_Dash_2124
Icon for Nimbostratus rankNimbostratus
Jun 02, 2008

Shared Virtual Server HTTP to HTTPS redirect

I want to build a single virtual server that will respond to requests both on port 80 and 443. I want port 80 traffic to be redirected to port 443. Also the servers on the internal pool listen on both 80 and 443 and they redirect all traffic to 443 except if the source is the Big-Ip. This we achieve by writing a server side script that looks at the HTTP header of the client request and if it has a variable 'SSLClientCipher' it assumes it is F5 and does not redirect that traffic to 443 but keeps it on port 80.

 

So i have the following irule in place:

 

-----------

 

rule httptossl_insertcipher {

 

when HTTP_REQUEST {

 

 

if { [TCP::local_port] == 80 }{

 

log local0. "in HTTP_REQUEST"

 

HTTP::respond 301 Location "https://[getfield [HTTP::host] : 1][HTTP::uri]"

 

}

 

elseif { [TCP::local_port] == 443}{

 

log local0. "in HTTPS_REQUEST"

 

HTTP::header insert SSLClientCipher [SSL::cipher name],\x20version=[SSL::cipher version],\x20bits=[SSL::cipher bits]

 

}

 

else {

 

log local0. "in Reject_REQUEST"

 

reject

 

}

 

}

 

}

 

-----------------

 

This is what my virtual server configuration looks like

 

virtual Test_CLIN {

 

destination 10.50.1.77:any

 

ip protocol tcp

 

translate service enable

 

profile http tcp wwwadirondocorg

 

persist cookie

 

pool ARCHIE_CLIN

 

rule httptossl_insertcipher

 

vlans external enable

 

}

 

----------------

 

I have enabled port translation on the vs to make sure it goes to port 80 on the pool.

 

 

..and this is my pool

 

pool ARCHIE_CLIN {

 

lb method member predictive

 

monitor all http

 

member 10.50.10.2:http session disable

 

member 10.50.10.42:http

 

member 10.50.10.82:http session disable

 

}

 

---

 

 

With this configuration https links work..but if i try to hit the virtual server on port 80 the virtual server closes the connection.

 

irule debugs don't generate any logs in /var/log/ltm when i try http but tcpdump shows..the virtual server closing out the client connection.

 

 

Not sure if this is the correct forum i.e. if this is an irule issue...

 

 

Any help is appreciated.

 

Thanks

 

Durga.

8 Replies

  • Do you get anything in the logs when using HTTPS links? You said the links work, but does that mean the app/page works correct, or that you're actually seeing what you expect in the logs.

     

     

    I didn't think HTTP profiles could be applied to wildcard virtual servers, so it's possible the HTTP_REQUEST isn't being triggered, but I could be wrong.
  • When I try https. The appropriate webpage from the server in the pool is displayed and also in the ltm logs i see thr irule being triggered.

     

     

    But when i try http i never see the irule being triggered from the ltm logs.

     

     

    Is there another way to set the above up?
  • We do SSL termination in our application, and we have separate virtual servers configured. One specifically for port 443 that has the ssl profile applied to it, and one for port 80 that doesn't. All the rest of the configuration between the two are the same.

     

     

    It's a bit more configuration, but I generally like having explicit Virtual Servers instead of generic ones. It makes separation of logic and stuff a bit cleaner.
  • That is how we have done it always too. Separate VS for 80 and 443. With a http-to-https irule for the VS listening on port 80 and our insert Cipher irule for the VS listening on 443...and that works fine.

     

     

    But i saw this irule in devcentral HttptoHttpsSingleVirtualServer

     

     

    http://devcentral.f5.com/wiki/default.aspx/iRules/HttpHttpsSingleVirtualServer.html

     

    That should work for wildcard virtual servers. I modified it by adding

     

    HTTP::header insert SSLClientCipher [SSL::cipher name],\x20version=[SSL::cipher version],\x20bits=[SSL::cipher bits]

     

     

    the above to the HTTP_request event but that gives me an error in ltm " Error: SSL hudfilter not reached or not in chain".

     

     

    even without the cipher insert it doesn't seem to work for me.

     

  • If you're trying to insert the output from an SSL-based command, you'd want to only attempt it for HTTPS connections. The SSL:: commands wouldn't be valid if the clientside connection was non-HTTPS. So add the header insert line within the 'else' block of the HTTP_REQUEST event:

     
     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 {$::debug}{log local0. "redirecting client [IP::client_addr] to https://[getfield [HTTP::host] \":\" 1][HTTP::uri]"} 
      
        } else { 
           HTTP::header insert SSLClientCipher [SSL::cipher name],\x20version=[SSL::cipher version],\x20bits=[SSL::cipher bits] 
        ... 
        } 
     } 
     

    If you're already checking that the requested port is HTTPS before trying to insert the HTTP header, can you post the full rule you get the error with?

    Another way to insert a space would be to wrap the header value in double quotes:

    HTTP::header insert SSLClientCipher "[SSL::cipher name], version=[SSL::cipher version], bits=[SSL::cipher bits]"

    Aaron
  • Yes i had finally reached there tinkering with the rules....Coz i realised the SSLCipher will not work when it is a request over port 80 with no SSLprofile..

     

    This was my final rule..

     

    ----------------------------------

     

    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 {$::debug}{log local0. "redirecting client [IP::client_addr] to https://[getfield [HTTP::host] \":\" 1][HTTP::uri]"}

     

     

    }

     

    if { [TCP::local_port] == 443 }{

     

    HTTP::header insert SSLClientCipher [SSL::cipher name],\x20version=[SSL::cipher version],\x20bits=[SSL::cipher bits]

     

    }

     

    }

     

    --------------

     

    I just added a check for TCP::local_port before trying to insert the SSL cipher.

     

     

    Now that i have finally got it working..I have one last question..

     

     

    Are there any negatives in setting up my virtual servers this way with the single irule for both http and https requests? (like more cpucycles)....instead of the traditional two virtual servers one for 80 and one for 443?

     

     

    ..and thanks a lot guys for the responses and help.

     

    Durga.
  • There is more overhead to run the rule than have two distinct VIPs. If you haven't already, you could remove some of the code from the iRule if you aren't using the functionality. Also, if you know there is a client SSL profile on the VIP, you could remove the 'PROFILE::exists clientssl' check.

     

     

    You could use timing to get an idea of how much CPU time the iRule requires (Click here). You won't have another iRule to compare it to though. Or you could do a rough comparison by doing load testing with the iRule, checking the performance graphs and then compare that with the same load testing on two separate VIPs.

     

     

    Aaron
  • Here is my final working irule, after cleaning out all the debugging and checking since I know how my virtuals will be built.

     

    Single wildcard virtual server on port 'Any' with ClientSSL profile set and 'http' profile set. Also with a default pool on port 80 and port translation enabled...and cookie persistence.

     

    ------------------------------

     

    when CLIENT_ACCEPTED {

     

    if { [TCP::local_port] == 80 }{

     

    set disable_cmd "SSL::disable"

     

    eval $disable_cmd

     

    }

     

    elseif { [TCP::local_port] != 443 }{

     

    reject

     

    }

     

    }

     

    when HTTP_REQUEST {

     

     

     

    if { [TCP::local_port] == 80 }{

     

    HTTP::redirect https://[getfield [HTTP::host] ":" 1][HTTP::uri]

     

    }

     

    if { [TCP::local_port] == 443 }{

     

    HTTP::header insert SSLClientCipher [SSL::cipher name],\x20version=[SSL::cipher version],\x20bits=[SSL::cipher bits]

     

     

    }

     

    }

     

    -----------------------------------

     

    Thanks all for your help.