For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Reverse Proxy With Basic SSO

Problem this snippet solves:

The iRule implements a authenticated HTTPS reverse proxy.

This iRule respond to a possible use of BigIP as an authenticated HTTPS reverse proxy. At this point, the iRule rewrite Host and Location in HTTP::header, support virtual multihosting and basic single sign-on.

First, declare two classes for rewrite HTTP::header Host and HTTP::header Location

How to use this snippet:

Supporting Classes

class tab_hostrewrite extern { 
  type string filename "tab_hostrewrite.txt" 
} 

class tab_locationrewrite extern { 
  type string filename "tab_locationrewrite.txt" 
}
  • The 'extern' keyword is not required in 9.4.x versions when creating the class from the CLI.
  • For "Host" rewriting, the file format is specific to our use (no DNS use), example: "host1.mydomain.com ws1.inet.mydomain.com 192.168.1.14 80", "host2.mydomain.com ws2.inet.mydomain.com 192.168.1.11 80",
  • For "Location" rewriting, an example is: "ws2.inet.mydomain.com https://host2.mydomain.com",

Code :

# rule ReverseProxy
###########################################################################
## IRule Reverse Proxy (c)05 F.NOEL - No warranty, feel free to use
## v0.01: F.NOEL - create irules with basic Host and Location rewrite
##  0.02: F.NOEL - add Single Sign ON (need to set :SSO to 1 in RULE_INIT)
##  0.03: F.NOEL - build cookie domain based on HTTP Header "Host"
##               - if "Host" not FQDN, cookie's domain set to ::COOKIE_DEFAULT_DOMAIN
##  0.04: F.NOEL - add SSO cookie encryption (AES)
##
###########################################################################

when RULE_INIT {
  set ::DEBUG 0
  set ::RP_PRIVIP "192.168.1.254"
  set ::SSO 1
  set ::COOKIE_DEFAULT_DOMAIN ".mydomain.com"
  set ::COOKIE_NAME "WSSOMYDOMAIN"
  set ::COOKIE_ENCRYPT 1
  set ::AES_KEY [AES::key 256]
  if { $::DEBUG } { log local0.debug "Initialyze random AES_KEY='$::AES_KEY' " }
}

when HTTP_REQUEST {
  set header_auth ""

  set header_host [HTTP::host]
  set hostrewrite [findclass $header_host $::tab_hostrewrite " "]

  if { $hostrewrite ne "" } {
    set destnodeaddr [getfield $hostrewrite " " 2]
    set destnodeport [getfield $hostrewrite " " 3]
    set hostrewrite  [getfield $hostrewrite " " 1]
    HTTP::header replace "Host" $hostrewrite
    if { $::DEBUG } { 
      log local0.debug "Header Host '$header_host' found -> rewrite with '$hostrewrite'" 
    }
    snat $::RP_PRIVIP
    node $destnodeaddr $destnodeport
  }
  else {
    if { $::DEBUG } { log local0.debug "$header_host not found -> respond with 403..." }
    HTTP::respond 403 content "HTTP Error 403 - Forbidden"
  }

  if { $::SSO } {
    set header_auth [HTTP::header "Authorization"]
    if { $::DEBUG } { log local0.debug "REQ header_auth -> '$header_auth'" }

    if { [HTTP::cookie exists $::COOKIE_NAME] } {
      set SSO_DO_SET_COOKIE 0
      HTTP::cookie remove $::COOKIE_NAME
      if { $::DEBUG } { 
        log local0.debug "REQ cookie_sso exist -> set SSO_DO_SET_COOKIE=0 and remove cookie" 
      }
    }
    else {
      if { $header_auth ne "" } {
        set SSO_DO_SET_COOKIE 1
        if { $::DEBUG } { log local0.debug "REQ cookie_sso not exist -> set SSO_DO_SET_COOKIE=1" }
      }
    }
  }
}

when HTTP_RESPONSE {
  ### SSO_DO_SET_COOKIE flag on, set a cookie for support Single Sign ON
  if { $::SSO and $SSO_DO_SET_COOKIE } {

    ## extract domain name from host to set cookie domain
    set cookiedomain $::COOKIE_DEFAULT_DOMAIN
    for {set i 6} {$i > 1} {incr i -1} {
      set tmp [getfield $header_host "." $i]
      ## if Host is FQDN
      if { $tmp ne "" and $i > 2 } {
        set cookiedomain $tmp
        incr i -1
        set cookiedomain ".[getfield $header_host "." $i].$cookiedomain"
        break;
      }
    }
    if { $::DEBUG } { log local0.debug "cookiedomain= '$cookiedomain' " }

    if { $::COOKIE_ENCRYPT } {
      set cookie_payload [b64encode [AES::encrypt $::AES_KEY $header_auth]]
    }
    else {
      set cookie_payload $header_auth
    }
    if { $::DEBUG } { log local0.debug "cookie_payload= '$cookie_payload' " }

    HTTP::cookie insert name $::COOKIE_NAME value $cookie_payload
    HTTP::cookie domain $::COOKIE_NAME $cookiedomain
    HTTP::cookie path $::COOKIE_NAME "/"
    set SSO_DO_SET_COOKIE 0
    if { $::DEBUG } { 
      log local0.debug "SSO_DO_SET_COOKIE=1 -> insert cookie $::COOKIE_NAME='$cookie_payload' in response..." 
    }
  }

  if { [HTTP::status] starts_with "3" } {
    set location [HTTP::header "Location"]
    if { $location ne "" } {
      if { $::DEBUG } { log local0.debug "Header Location '$location' not null -> checking..." }

      set loc_start ""
      if { $location starts_with "http://" } { set loc_start "http://" }
      elseif { $location starts_with "https://" } { set loc_start "https://" }
      set loc_start_len [string length $loc_start]

      if { $loc_start_len eq 0 } {
        if { $::DEBUG } { log local0.debug "No absolute redirection! return... " }
        return
      }

      set loc_end [substr $location $loc_start_len]
      set loc_to_search [getfield $loc_end "/" 1]
      set locationrewrite [findclass $loc_to_search $::tab_locationrewrite " "]

      if { $locationrewrite ne "" } {
        set loc_to_search_len [string length $loc_to_search]
        set loc_end [substr $loc_end $loc_to_search_len]
        set new_loc "$locationrewrite$loc_end"
        HTTP::header replace "Location" $new_loc
        if { $::DEBUG } { log local0.debug "Rewrite Location -> $new_loc" }
      }
      else {
        if { $::DEBUG } { log local0.debug "Location not found in tab_locationrewrite -> return..." }
      }
      return
    }
  }
  else
  { return }
}

# Finally, if you want activate the support for basic single sign-on with a ldap authentication, add rule SSO_Auth_Ldap on your authentication profile

# rule SSO_Auth_Ldap
###########################################################################
## IRule Reverse Proxy - AuthLdap (c)05 F.NOEL - No warranty, feel free to use
## v0.01: create irule for support Single Sign ON with LDAP authentication
##
###########################################################################

when CLIENT_ACCEPTED {
    set tmm_auth_ldap_sid [AUTH::start pam default_ldap]
}
when HTTP_REQUEST {
if { [HTTP::cookie exists $::COOKIE_NAME] } {
  set cookie_payload [HTTP::cookie value $::COOKIE_NAME]

  if { $::COOKIE_ENCRYPT } {
set cookie_payload [AES::decrypt $::AES_KEY [b64decode $cookie_payload]]
  }

  if { $::DEBUG } { log local0.debug "cookie $::COOKIE_NAME exist -> set Authorization credential" }
  HTTP::header replace "Authorization" $cookie_payload
}
else
{
  if { $::DEBUG } { log local0.debug "cookie $::COOKIE_NAME doesn't exist -> authenticate user" }
  AUTH::username_credential $tmm_auth_ldap_sid [HTTP::username]
  AUTH::password_credential $tmm_auth_ldap_sid [HTTP::password]
  AUTH::authenticate $tmm_auth_ldap_sid
  HTTP::collect
}
}
when AUTH_SUCCESS {
if {$tmm_auth_ldap_sid eq [AUTH::last_event_session_id]} {
HTTP::release
}
}
when AUTH_FAILURE {
if {$tmm_auth_ldap_sid eq [AUTH::last_event_session_id]} {
HTTP::respond 401
}
}
when AUTH_WANTCREDENTIAL {
if {$tmm_auth_ldap_sid eq [AUTH::last_event_session_id]} {
HTTP::respond 401
}
}
when AUTH_ERROR {
if {$tmm_auth_ldap_sid eq [AUTH::last_event_session_id]} {
HTTP::respond 401
}
}
Published Mar 18, 2015
Version 1.0
No CommentsBe the first to comment