HTTP and HTTPS on a single virtual server

Problem this snippet solves:

iRule to support a virtual server on port 0, a client SSL profile, and an HTTP pool.

The goal is to replace two separate HTTP and HTTPS virtual servers with a single virtual server.

Requests to ports defined as HTTPS will have the client SSL profile applied and traffic will be decrypted. Requests to ports defined as HTTP will be left in cleartext All other requests to undefined ports will be reset. All requests sent to the pool are in HTTP. The rule depends on having a pool of HTTP servers defined as the default pool of the virtual server.

Optionally, you can redirect all HTTP requests to the same host and URI over HTTPS by enabling static::redirect_http_to_https in the RULE_INIT event.

If you do not need either the redirection from HTTP to HTTPS or to rewrite the URI to lowercase, you can remove the code from the iRule to save resources.

The old v9 iRule version is below this one.

Code :

when RULE_INIT {

   # Requests to ports not defined in either the https or http ports list will be reset

   # Define virtual server ports that should have SSL enabled
   # in an integer type datagroup (example: 443, 8443, etc)
   # and update the name here from https_ports_dg to the data group name you create
   set static::vip_https_ports "https_ports_dg"

   # Define virtual server ports that should be answered with HTTP
   # in an integer type datagroup (example: 80, 8080, etc)
   # and update the name here from http_ports_dg to the data group name you create
   set static::vip_http_ports "http_ports_dg"

   # 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 static::redirect_http_to_https 0

   # Set this option to 1 to log debug messages (to /var/log/ltm by default)
   set static::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] [virtual name]"

   if { [matchclass [TCP::local_port] equals $static::vip_https_ports] }{

      # 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 {$static::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 {$static::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
      }

   } elseif { [matchclass [TCP::local_port] equals $static::vip_http_ports] }{

      # 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 {$static::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 {$static::single_vs_debug}{log local0. "$log_prefix: Client SSL profile enabled on VIP.  Disabling SSL."}
         set disable_cmd "SSL::disable"
         eval $disable_cmd
      } 
   } else {

      # Request wasn't to a defined port, so reset the TCP connection.
      if {$static::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 { ($static::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 {$static::single_vs_debug}{log local0. "$log_prefix:\
         Redirecting client [IP::client_addr] to https://[getfield [HTTP::host] \":\" 1][HTTP::uri]"}
   }     
}
Published Mar 18, 2015
Version 1.0
  • oguzy's avatar
    oguzy
    Icon for Cirrostratus rankCirrostratus

    Hi Aaron,

     

    We put a proxy server behind a virtual server to able to access some external publication (online libraries etc.) from the external network. Also, we use apm ad authentication, sso, local db etc. Proxy server works in two ways such as subdomain or port based. We did not want to buy a subdomain ssl wildcard certificate and chose port based solution.

     

    We created a virtual server which listens all port, both client and server ssl profiles are selected. Moreover, we created lots of virtual server which listens to specific ports such as 2073, 2141 etc. In that virtual servers, client and server profiles are not selected. Everything works fine with that kind of configuration. However, sometimes additional port requirement emerges and we have to create additional virtual servers. We do not want to create additional virtual servers and want to handle this issue using irules (ssl disable) and data groups.

     

    • Client Ssl profile: the certificate (also installed in the pyhsical server)
    • Server Ssl profile: serverssl-insecure-compatible

    Thanks to your guidance I tried to resolve the issue, however it does not work again. The server ssl profile may be lead to this problem.

     

    Do you have any idea?

     

    Thanks.

     

    Edit: Hi again. I figure out the issue. The serverside ssl disable should be done within the SERVER_CONNECTED event. Have a nice day!

     

  • Hello,

     

    I am facing a challenge in which VS listens on port 80 only. I have two pools (for two different back-end apps, one of the app is secure while the 2nd one is not)--> one pool listens on port 80, the other is on 443.

     

    I created ssl server profile and attached to VS. then by using irule I disabled ssl, only enable it when request has to go to pool_https... however I get : 400 The plain HTTP request was sent to HTTPS port

     

    ...pseudo code when CLIENT_ACCEPTED { SSL::disable serverside } when SERVER_CONNECTED { SSL::disable serverside } when HTTP_REQUEST { if {[HTTP::uri] eq "/xxx"} { pool http_pool } if {[HTTP::uri] eq "/yyy"} { SSL::enable serverside pool https_pool } }

     

    any help? thanks in advance.

     

  • @jaz

     

    The code above was published in 2015 and doesn’t manage same requirement as yours!

     

    If you expect any answer, create a thread in questions section