Forum Discussion

Brian_69413's avatar
Brian_69413
Icon for Nimbostratus rankNimbostratus
Nov 06, 2007

iRule not capturing HTTP_REQUEST

I am working on parsing the payload of an HTTP_REQUEST event and encrypting/decrypting usernames/passwords. I am having trouble triggering the iRule for the specified request.

 

 

I do a tcpdump on the LTM to capture the request and I see a POST from the client to the server to a specific authentication URL. This should be captured in the HTTP_REQUEST event, correct? Having no success, I simplified the process by simply logging the [HTTP::method]. All I see are GET's when clicking the same link. Also, I tried logging the [HTTP::header names] and it is not the same list as the one I see in the dump.

 

 

Does anyone have any ideas why this iRule is not "seeing" the POST? Could this be in the [TCP::payload]? I tried logging the [TCP::payload] and got jibberish(at least to me ):D)

 

 

I can show code if you would like, but I think it might not be relevant to the problem at hand.

 

 

Any help would be appreciated, Thanks!

8 Replies

  • Perhaps some insight into where the iRule "intercepts" the request would be helpful. Does anyone know whether the tcpdump facility will get the packets first or will the iRule for the HTTP_REQUEST event?
  • I believe that the output from tcpdump would be written before LTM parses the HTTP headers and the HTTP_REQUEST event is triggered. Maybe a developer could comment on that?

     

     

    Are the POST requests that you don't see in the rule log coming over an existing TCP connection?

     

     

    It might be helpful to log a statement in the CLIENT_ACCEPTED event with the client IP:port as well as in the HTTP_REQUEST event.

     

     

    Could you post your virtual server and profiles from the bigip.conf as well as your rule so we can take a look?

     

     

    Thanks,

     

    Aaron
  • Thanks for the reply, below is the code, I will have to get back to you on the others. I am encrypting the username/password from the server and decrypting on the way back from the client. I have it limited to my IP for now as I do not want to interrupt others. It is the Client communication(POST) that I am not seeing, although it looks as if it is in the same TCP connection.

     

     

    when RULE_INIT { 
    SET THE ENCRYPTION KEY
    set ::key [AES::key 128]
    }
    when HTTP_REQUEST {
    LIMIT TO A TEST PC's IP
    if { ([IP::client_addr] equals "x.x.x.x") } {
    COLLECT THE CONTENT 
    switch [HTTP::method] {
    "GET" {
    log local0. "GET Request"
    }
    "POST" {
    log local0. "POST Request"
    if { [HTTP::header Content-Type] eq "application/x-www-form-urlencoded" } {
    HTTP::collect [HTTP::header Content-Length]
    }
    }
    }
    }
    }
    when HTTP_REQUEST_DATA {
    TESTING
    set namevals [split [HTTP::payload] "&"]
    for {set i 0} {$i < [llength $namevals]} {incr i} {
    set params [split [lindex $namevals $i] "="]
    log local0. "    [lindex $params 0] : [lindex $params 1]"
    }
    }
    when HTTP_RESPONSE {
    WHEN SERVER SENDS PASSWORD, START COLLECTING
    if { [IP::client_addr] equals "x.x.x.x" } {
    if { [HTTP::header "Content-Length"] == 733 } {
    HTTP::collect [HTTP::header Content-Length]
    }
    }
    }
    when HTTP_RESPONSE_DATA {
    EXTRACT USERNAME AND PASSWORD
    set user [findstr [HTTP::payload] "j_username\" value=\"" 19 "\""]
    set pass [findstr [HTTP::payload] "j_password\" value=\"" 19 "\""]
    ENCRYPT THE USERNAME AND PASSWORD
    set encrypted_user [b64encode [AES::encrypt $::key $user]]
    set encrypted_pass [b64encode [AES::encrypt $::key $pass]]
    DETERMINE THE LOCATION AND LENGTH OF THE USERNAME
    set user_begin [string first "j_username" [HTTP::payload] 0]
    set user_begin [incr user_begin 19]
    set user_end [string first "\"" [HTTP::payload] $user_begin]
    set user_end [incr user_end -1]
    set user_len [string length [string range [HTTP::payload] $user_begin $user_end]]
    REPLACE THE USERNAME
    HTTP::payload replace $user_begin $user_len $encrypted_user
    DETERMINE THE LOCATION AND LENGTH OF THE PASSWORD
    set encrypt_user_len [string length $encrypted_user]
    set encrypt_user_end [incr user_begin $encrypt_user_len]
    set pass_begin [string first "j_password" [HTTP::payload] $encrypt_user_end]
    set pass_begin [incr pass_begin 19]
    set pass_end [string first "\"" [HTTP::payload] $pass_begin]
    set pass_end [incr pass_end -1]
    set pass_len [string length [string range [HTTP::payload] $pass_begin $pass_end]]
    REPLACE THE PASSWORD
    HTTP::payload replace $pass_begin $pass_len $encrypted_pass
    HTTP::release
    }
  • Can you add a few log statements to the rule?

    
    when CLIENT_ACCEPTED {
       if { ([IP::client_addr] equals "x.x.x.x") } {
          log local0. "client [IP::client_addr]:[TCP::client_port] connected"
       }
    }
    when HTTP_REQUEST {
       log local0. "client [IP::client_addr]:[TCP::client_port] -> [HTTP::method] -> [HTTP::host][HTTP::uri] (HTTP v[HTTP::version])"

    Then within the POST case, before any additional logic, add a log entry to show the Content-Type and Content-Length header values:

    
    log local0. "client [IP::client_addr]:[TCP::client_port] -> \
       [HTTP::method] -> Content-Type: [HTTP::header value Content-Type], \
       Content-Length: [HTTP::header value Content-Length], \
       Transfer-Encoding: [HTTP::header value Transfer-Encoding]"

    Aaron
  • I added the logging. It only confirms what I saw before, the tcpdump shows a POST and the iRule logs only GET's
  • Deb_Allen_18's avatar
    Deb_Allen_18
    Historic F5 Account
    Your iRule looks correct to me.

     

    Perhaps a Support case is in order, esp if you are on a feature release branch (9.4.x)

     

     

    /deb

     

     

  • Turns out the assumption was correct that this post was on a different TCP connection. Same server, new connection(new app, I guess you could say)

     

     

    Thanks for everyones help.
  • Interesting post. I'm pretty certain tcpdump is way ahead of any input parsing on the LTM. I ran across this post after experiencing a similar problem myself and was able to use tcpdump as a clean input from which to troubleshoot.