Forum Discussion

smilanko_261688's avatar
Jul 08, 2016

Headers not being set at http_request

I am working on a clientless APM process with F5, where the clients requesting a webservice will use http basic authentication to present their credentials. Once the APM retrieves the username/password combo, it authenticates them in the AD. If successful, I grab out a whole bunch of values using an AD query, and then they are passed onto the application. This is how the flow looks like:

Now, as soon as the APM policy has completed, I am parsing out the CN values of all groups they are in, and storing them as some custom session variable. When a http request is made, I look to ensure that the session is established, and insert the constructed headers, as needed by my application. My IRule looks as follows:

when HTTP_REQUEST  {

     insert clientless mode
    set apmsessionid [HTTP::cookie value MRHSession]
    if { [HTTP::cookie exists "MRHSession"] } { set apmstatus [ACCESS::session exists -state_allow $apmsessionid]} else {set apmstatus 0}
    if {!($apmstatus)} {
         Insert Clientless-mode header to start APM in clientless mode
        if { [catch {HTTP::header insert "clientless-mode" 1} ] } {log local0. "[IP::client_addr]:[TCP::client_port] : TCL error on HTTP header insert clientless-mode : URL : [HTTP::host][HTTP::path] - Headers : [HTTP::request]"}
    }

     empty the current headers
    HTTP::header remove username
    HTTP::header remove domain
    HTTP::header remove roles

     set the headers needed for the application
    HTTP::header insert username [ACCESS::session data get session.custom.login]
    HTTP::header insert domain [ACCESS::session data get session.custom.domain]
    HTTP::header insert roles [ACCESS::session data get session.custom.roles]

    log " sending [HTTP::header username]"
    log " sending [HTTP::header domain]"
    log " sending [HTTP::header roles]"

}

when ACCESS_POLICY_COMPLETED {
    Authentication request for non bowser user-agent session denied
   if { ([ACCESS::policy result] equals "deny") } {
      ACCESS::respond 401 noserver WWW-Authenticate "Basic realm=\"My Web Services Authentication\"" Connection close
      ACCESS::session remove
      return
    } else {
        set allRoles ""
         grab the session data containing the groups associated with the member
        set s [ACCESS::session data get "session.ad.last.attr.memberOf"]
        set matched [split [string map [list {| CN=} \0] $s] \0]
        foreach match $matched {
            if { $match != "" } { 
                puts [string range $match 0 [expr [string first , $match] - 1]]
                set queryarray [split $match ","]
                set firstvalue [lindex $queryarray 0]
                set cleaneddata [string toupper [join $firstvalue _]]
                if { [string length allRoles] == 0 } {
                    append allRoles "ROLE_$cleaneddata"
                } else {
                    append allRoles ",ROLE_$cleaneddata"
                }
            }
        }
        puts $allRoles

         grab the session data containing the username associated with the member
        ACCESS::session data set session.custom.login [ACCESS::session data get "session.logon.last.logonname"]
         grab the session data containing the domaiin associated with the member
        ACCESS::session data set session.custom.domain [ACCESS::session data get "session.ad.last.actualdomain"]
         grab the session data containing the roles associated with the member
        ACCESS::session data set session.custom.roles $allRoles
    }
}

The issue I am having is that the first set of log statements:

log " sending [HTTP::header username]"
log " sending [HTTP::header domain]"
log " sending [HTTP::header roles]"

All print out blank values. I have to refresh the page I am authenticating against to get those values to populate. This leads me to believe that I should be setting these values a bit earlier. Where should I start inserting these headers, to ensure the end application is getting them on every request?

  • I ended up ensuring that the headers are being set before the request is sent, using HTTP_REQUEST_SEND. Here is the working iRule:

    when HTTP_REQUEST  {
         insert clientless mode
        set apmsessionid [HTTP::cookie value MRHSession]
        if { [HTTP::cookie exists "MRHSession"] } { set apmstatus [ACCESS::session exists -state_allow $apmsessionid]} else {set apmstatus 0}
        if {!($apmstatus)} {
             Insert Clientless-mode header to start APM in clientless mode
            if { [catch {HTTP::header insert "clientless-mode" 1} ] } {log local0. "[IP::client_addr]:[TCP::client_port] : TCL error on HTTP header insert clientless-mode : URL : [HTTP::host][HTTP::path] - Headers : [HTTP::request]"}
        }
    }
    
    when HTTP_REQUEST_SEND {
        clientside {
             empty the current headers
            HTTP::header remove username
            HTTP::header remove domain
            HTTP::header remove roles
    
             set the headers needed for the application
            HTTP::header insert username [ACCESS::session data get session.custom.login]
            HTTP::header insert domain [ACCESS::session data get session.custom.domain]
            HTTP::header insert roles [ACCESS::session data get session.custom.roles]
        }
    }
    
    when ACCESS_POLICY_COMPLETED {
        Authentication request for non bowser user-agent session denied
       if { ([ACCESS::policy result] equals "deny") } {
          ACCESS::respond 401 noserver WWW-Authenticate "Basic realm=\"My Web Services Authentication\"" Connection close
          ACCESS::session remove
          return
        } else {
            set allRoles ""
             grab the session data containing the groups associated with the member
            set s [ACCESS::session data get "session.ad.last.attr.memberOf"]
            set matched [split [string map [list {| CN=} \0] $s] \0]
            foreach match $matched {
                if { $match != "" } { 
                    puts [string range $match 0 [expr [string first , $match] - 1]]
                    set queryarray [split $match ","]
                    set firstvalue [lindex $queryarray 0]
                    set cleaneddata [string toupper [join $firstvalue _]]
                    if { [string length allRoles] == 0 } {
                        append allRoles "ROLE_$cleaneddata"
                    } else {
                        append allRoles ",ROLE_$cleaneddata"
                    }
                }
            }
            puts $allRoles
    
             grab the session data containing the username associated with the member
            ACCESS::session data set session.custom.login [ACCESS::session data get "session.logon.last.logonname"]
             grab the session data containing the domaiin associated with the member
            ACCESS::session data set session.custom.domain [ACCESS::session data get "session.ad.last.actualdomain"]
             grab the session data containing the roles associated with the member
            ACCESS::session data set session.custom.roles $allRoles
        }
    }
    
  • Hi,

    I think that you can insert the header for the application in an ACCESS_ACL_ALLOWED event instead of HTTP_REQUEST :

    when ACCESS_ACL_ALLOWED {
    
         empty the current headers
        HTTP::header remove username
        HTTP::header remove domain
        HTTP::header remove roles
    
         set the headers needed for the application
        HTTP::header insert username [ACCESS::session data get session.custom.login]
        HTTP::header insert domain [ACCESS::session data get session.custom.domain]
        HTTP::header insert roles [ACCESS::session data get session.custom.roles]
    
        log " sending [HTTP::header username]"
        log " sending [HTTP::header domain]"
        log " sending [HTTP::header roles]"
    
    }
    

    Wiki page for this event : ACCESS_ACL_ALLOWED

    • smilanko_261688's avatar
      smilanko_261688
      Icon for Cirrus rankCirrus

      This did not work for me, however, it does not mean it would not work for others. Possibly something to try. I did manager, however, to figure out a solution. Posting now

       

  • Hi,

    I think that you can insert the header for the application in an ACCESS_ACL_ALLOWED event instead of HTTP_REQUEST :

    when ACCESS_ACL_ALLOWED {
    
         empty the current headers
        HTTP::header remove username
        HTTP::header remove domain
        HTTP::header remove roles
    
         set the headers needed for the application
        HTTP::header insert username [ACCESS::session data get session.custom.login]
        HTTP::header insert domain [ACCESS::session data get session.custom.domain]
        HTTP::header insert roles [ACCESS::session data get session.custom.roles]
    
        log " sending [HTTP::header username]"
        log " sending [HTTP::header domain]"
        log " sending [HTTP::header roles]"
    
    }
    

    Wiki page for this event : ACCESS_ACL_ALLOWED

    • smilanko_261688's avatar
      smilanko_261688
      Icon for Cirrus rankCirrus

      This did not work for me, however, it does not mean it would not work for others. Possibly something to try. I did manager, however, to figure out a solution. Posting now

       

  • I ended up ensuring that the headers are being set before the request is sent, using HTTP_REQUEST_SEND. Here is the working iRule:

    when HTTP_REQUEST  {
         insert clientless mode
        set apmsessionid [HTTP::cookie value MRHSession]
        if { [HTTP::cookie exists "MRHSession"] } { set apmstatus [ACCESS::session exists -state_allow $apmsessionid]} else {set apmstatus 0}
        if {!($apmstatus)} {
             Insert Clientless-mode header to start APM in clientless mode
            if { [catch {HTTP::header insert "clientless-mode" 1} ] } {log local0. "[IP::client_addr]:[TCP::client_port] : TCL error on HTTP header insert clientless-mode : URL : [HTTP::host][HTTP::path] - Headers : [HTTP::request]"}
        }
    }
    
    when HTTP_REQUEST_SEND {
        clientside {
             empty the current headers
            HTTP::header remove username
            HTTP::header remove domain
            HTTP::header remove roles
    
             set the headers needed for the application
            HTTP::header insert username [ACCESS::session data get session.custom.login]
            HTTP::header insert domain [ACCESS::session data get session.custom.domain]
            HTTP::header insert roles [ACCESS::session data get session.custom.roles]
        }
    }
    
    when ACCESS_POLICY_COMPLETED {
        Authentication request for non bowser user-agent session denied
       if { ([ACCESS::policy result] equals "deny") } {
          ACCESS::respond 401 noserver WWW-Authenticate "Basic realm=\"My Web Services Authentication\"" Connection close
          ACCESS::session remove
          return
        } else {
            set allRoles ""
             grab the session data containing the groups associated with the member
            set s [ACCESS::session data get "session.ad.last.attr.memberOf"]
            set matched [split [string map [list {| CN=} \0] $s] \0]
            foreach match $matched {
                if { $match != "" } { 
                    puts [string range $match 0 [expr [string first , $match] - 1]]
                    set queryarray [split $match ","]
                    set firstvalue [lindex $queryarray 0]
                    set cleaneddata [string toupper [join $firstvalue _]]
                    if { [string length allRoles] == 0 } {
                        append allRoles "ROLE_$cleaneddata"
                    } else {
                        append allRoles ",ROLE_$cleaneddata"
                    }
                }
            }
            puts $allRoles
    
             grab the session data containing the username associated with the member
            ACCESS::session data set session.custom.login [ACCESS::session data get "session.logon.last.logonname"]
             grab the session data containing the domaiin associated with the member
            ACCESS::session data set session.custom.domain [ACCESS::session data get "session.ad.last.actualdomain"]
             grab the session data containing the roles associated with the member
            ACCESS::session data set session.custom.roles $allRoles
        }
    }