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

Was this article helpful?

No CommentsBe the first to comment