Hi Abed AL-R,
parsing and evaluating "X-Forwareded-For" headers correctly could be a challenging task.
Keep in mind that XFF header(s) may use either a DNS or IP format and that a single request may include multiple XFF header instances. The XXF structure below outlines how XFF headers may look like. This structure should be always keeped in mind when developing XFF based iRules...
...
X-Forwarded-For: 10.10.10.10
X-Forwarded-For: 10.10.20.10
X-Forwarded-For: host.domain.tld
X-Forwarded-For: 10.10.30.10, 10.10.40.10, 10.10.50.10
...
Per XFF definition (XFF is not a RFC), the first entry of the first header is the originator of the request and subsequent entries and/or headers are building the upstream chain. The
[HTTP::header value "X-Forwarded-For"]
command will always pick the last header as a whole and ignore any leading XFF headers. The
[HTTP::header values "X-Forwarded-For"]
command (notice the
value
vs.
values
directive) will fetch every single XFF header in the request and combine them into a TCL list-item, which would require some additional
[string]
replacements to create a list of consecutive XFF value.
To pick accurately the very first XFF entry you may use a command like...
set original_client [lindex [string map { ", " " " "\{" "" "\}" ""} [HTTP::header values "X-Forwarded-For"]] 0]
To pick accurately the very last XFF entry you may use a command like...
set last_hop [lindex [string map { ", " " " "\{" "" "\}" ""} [HTTP::header value "X-Forwarded-For"]] end]
And to skip through the list of XFF values starting from the client to the last_hop you may use a [foreach] loop like...
foreach xff_value [string map { ", " " " "\{" "" "\}" ""} [HTTP::header values "X-Forwarded-For"]] {
log local0.debug "XFF Value: $xff_value"
}
So depending on your detailed requirements you would need to select the right commands to select the XFF value(s) of your choice.
A very generic iRule to evaluate every single XFF value starting from first to last XFF value with an IP based Data-Group would look like that.
iRule:
when HTTP_REQUEST {
if { [HTTP::header values "X-Forwarded-For"] ne "" } then {
Client has passed at least one XFF header. Combining and formating the XFF header as a list and skipping through the individual values one by one...
foreach xff_value [string map { ", " " " "\{" "" "\}" ""} [HTTP::header values "X-Forwarded-For"]] {
if { [catch {
Try to find the XFF in the IP-Address data-group. This command may fail if the XFF value is not an IP address.
set snatpool [class match -value $xff equals MY_XFF_to_SNATPOOL_DG]
}] } then {
The XFF value was not an IP address and caused a TCL exemption. Skipping to the next XFF value...
continue
}
if { $snatpool ne "" } then {
The data-group query found a matching entry for the currently processed XFF value.
snatpool $snatpool
Successfully selected a SNATPOOL for this connection. Stopping further iRule processing...
return
} else {
The XFF value was not found in the data-group. Skipping to the next XFF value...
}
}
Finish the evaluation of the individual XFF values. The client has send just unknown XFF value(s)...
} else {
The clients has not send a XFF header...
}
Define a default action here...
drop
}
Data-Group
172.28.0.0/16 := /Common/SNATPOOL_1
172.29.0.0/16 := /Common/SNATPOOL_2
172.29.10.0/24 := /Common/SNATPOOL_3
Cheers, Kai