Forum Discussion

wlepkin_98758's avatar
wlepkin_98758
Icon for Nimbostratus rankNimbostratus
Apr 12, 2012

string range arguments

Hi, I'm trying to capture the first x-forwarded-for header value in an http request. There's probably a more efficient way to do this, but this is what I came up with:

 

 

when HTTP_REQUEST {

 

if { [HTTP::header exists X-Forwarded-For] } then

 

{

 

set header_values [HTTP::header values X-Forwarded-For]

 

log local0. "X-Forwarded-For exists, value = $header_values"

 

set no_of_headers [HTTP::header count X-Forwarded-For]

 

log local0. "Number of X-Forwarded-For headers is $no_of_headers"

 

if { $no_of_headers > 0 } then

 

{

 

set blank_index [ string wordstart $header_values " " ]

 

log local0. "Blank index = $blank_index"

 

set client_addr [ string range $header_values 0 [ expr {$blank_index - 1} ] ]

 

unset blank_index

 

}

 

else

 

{

 

set client_addr $header_values

 

}

 

log local0. "Client addr = $client_addr"

 

unset header_values no_of_headers

 

}

 

else

 

{

 

set client_addr [IP::client_addr]

 

log local0. "No X-Forwarded-For exists, client addr = $client_addr"

 

}

 

unset client_addr

 

}

 

 

So I check to see if there is more than one header, and if so get the index of the first blank space in the string of header values, then do a string range from the beginning of the string of values to the index of the first space minus 1.

 

 

But I get the following error: "Rule [display-x-forwarded-for] error: line 10: [invalid index: should be integer or "end"] [" "]." And for all I know I may be making other mistakes that I haven't gotten to yet.

 

 

What am I doing wrong?

 

 

Thanks much,

 

 

Wayne

 

8 Replies

  • How are you getting multiple X-forwarder-For? I'm asking because I thought its only put into the header by the Layer7 once on the way through to the backend server and it is stripped there (and not echoed back to client).
  • It looks like to me you are just trying to locally log the Client IP from the xForwarder for the connection. XForwarder is pertinent after the autoSNAT going to the back end server. At the front end when the dicrete ClientPC->VIP connection is made, the client IP is available there, the only case I can think of at moment is where you accept a connection on on VIP, add X-Forwarder and you bounce that connection to another VIP.

     

     

    Maybe something a bit simpler could help in this case? Sorry if I'm on the wrong track, I've had to make a few assumptions on your architecture. The code below should identify the IP and if there is an xforward

     

     

    when HTTP_REQUEST {

     

    set client_addr [IP::client_addr]

     

    set Xforward [HTTP::header values X-Forwarded-For]

     

     

    if { [HTTP::header exists X-Forwarded-For] } then

     

    {

     

    log local0. "X-Forwarded-For exists, value = $Xforward (ClientIP=$client_addr)"

     

    } else {

     

    log local0. "No X-Forwarded-For exists, client addr = $client_addr"

     

    }

     

     

    }
  • Hi Wayne,

    Can you try this to get the first xff IP:

    
    when HTTP_REQUEST {
    scan [HTTP::header values x-forwarded-for] {%d.%d.%d.%d%*s} a b c d 
    set first_xff $a.$b.$c.$d
    }
    

    Aaron
  • Aaron, can you ellaborate how you can get more than one xff ?

     

    Also, as multiples are permitted, can we be sure the additional xff are appended and not pre-pended?

     

     

  •  

    Hoolio/Aaron --

     

     

    Wow! I figured there was a more efficient way, but two lines is great! Works very well, thanks.

     

     

    Antony --

     

     

    Thanks for your comments. I'm certainly no expert on http, but apparently devices that insert x-forwarded-for headers (such as the F5) always just append a new one at the end, regardless of how many are already there. In our case, the request may come in via an Akamai server and then go through our F5, and each of those devices puts in its own x-forwarded-for header, so your code above would not work for us because you assume that there is only one.

     

     

    Wayne

     

  • Any client or intermediate device can insert any HTTP header. And the XFF header isn't formalized in an RFC. So you can get a lot of variation in how different devices handle XFF insertions. Some devices will append the client IP they receive in the IP header to an existing XFF header (if one or more is present already). Others might insert a new XFF header. That header could be inserted before or after existing XFF headers.

    BIG-IP inserts a new XFF header at the end of the HTTP headers even when one or more XFF headers exist in the request.

    I tested a bit more and the scan iRule doesn't handle requests where the first XFF header value has more than one IP address. Here's an updated version that will parse the first XFF IP address even if there is more than one header and the first header has more than one IP address.

    
    when HTTP_REQUEST {
        if { [HTTP::header values x-forwarded-for] ne "" } {
            set xff_first_ip [lindex [split [lindex [HTTP::header values x-forwarded-for] 0] ", "] 0]
             Breakdown of commands:
             [HTTP::header values x-forwarded-for] - Get all XFF header value(s) with each header in a list element
             [lindex [HTTP::header values x-forwarded-for] 0] - Get the first XFF header value from the list
             [split [lindex [HTTP::header values x-forwarded-for] 0] ", "] - Split the first XFF header value on commas and/or spaces
             [lindex [split [lindex [HTTP::header values x-forwarded-for] 0] ", "] 0] - Get the first list element from the above split
        }
    }

    On a tangent here are a few tidbits on the HTTP profile options:

    1. If you want to pass on a "trusted" XFF value to a pool member, you can strip out any pre-existing XFF headers and then have LTM insert one using a custom HTTP profile:

    
    profile http sanitized_xff_http {
       defaults from http
       header insert "x-forwarded-for: [IP::client_addr]"
       header erase "x-forwarded-for"
    }

    2. The checkbox option to insert the XFF header is done before the header to insert action is taken. So if you want to remove any existing XFF headers and then insert a new one, don't use the checkbox option.
  • Thanks Aaron for a complete response and expanding on it.

     

     

    Wayne, happy you got a usable answer from the MVP
  • Hi Antony,

     

     

    Thanks for the example and comments too.

     

     

    Aaron