Forwarded HTTP Extension Insertion (RFC 7239)
Problem this snippet solves:
Until June 2014, there was no standard HTTP headers to share with web server behind reverse proxy client side request properties like:
- Client IP Address
- requested Host header
- requested protocol (HTTP / HTTPS)
- Reverse Proxy egress IP address
These informations were injected in following non standard headers
- Client IP Address : X-Forwarded-For
- requested Host header : X-Forwarded-Host
- requested protocol (HTTP / HTTPS) : X-Forwarded-Proto
- Reverse Proxy egress IP address : Via
The RFC 7239 (Forwarded HTTP Extension) provide a single header which contains all these informations : Forwarded
Here is an examples of Forwarded header :
Forwarded: for="_gazonk" Forwarded: For="[2001:db8:cafe::17]:4711" Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43 Forwarded: for=192.0.2.43, for=198.51.100.17
This code insert the Forwarded header with expected values to HTTP request
- insert for parameter with Client IP address (IPv6 format support with bracket)
- include in For parameter the client request value of For parameter
- include in For parameter the client request value of X-Forwarded-For header
- insert by parameter with IP address (IPv6 format support with bracket)
- insert proto parameter depending on clientssl profile enabled on VS
- insert host parameter with HTTP client request Host header
version 1.1 : add route domain id support (remove route domain ID from IP addresses before insert it)
How to use this snippet:
Enable this irule on virtual server an set following variables to 0 or 1 to disable / enable parameter in header:
# Insert "For" parameter set INSERT_FORWARDED_FOR 1 # Include in "For" parameter values from request Forwarded header set KEEP_FORWARDED_FOR 1 # Include in "For" parameter values from request X-Forwarded-For header set CONVERT_XFF_TO_FORWARDED_FOR 1 # Insert "By" parameter set INSERT_FORWARDED_BY 0 # Insert "Proto" parameter set INSERT_FORWARDED_PROTO 1 # Insert "Host" parameter set INSERT_FORWARDED_HOST 0
Code :
when CLIENT_ACCEPTED { ############### Configure Which info insert in Forwarded header ############### # Insert "For" parameter set INSERT_FORWARDED_FOR 1 # Include in "For" parameter values from request Forwarded header set KEEP_FORWARDED_FOR 1 # Include in "For" parameter values from request X-Forwarded-For header set CONVERT_XFF_TO_FORWARDED_FOR 1 # Insert "By" parameter set INSERT_FORWARDED_BY 0 # Insert "Proto" parameter set INSERT_FORWARDED_PROTO 0 # Insert "Host" parameter set INSERT_FORWARDED_HOST 1 ############### Get HTTP / HTTPS info based on the clientssl profile assigned to the VS ############### if { [PROFILE::exists clientssl] == 1} { set REQUEST_PROTO "https" } else { set REQUEST_PROTO "http" } ############### prepare IPV6 encoding ############### if {[set CLIENT_ADDR [getfield [IP::remote_addr] "%" 1]] contains ":"} {set CLIENT_ADDR "\[$CLIENT_ADDR\]"} } when HTTP_REQUEST { set FFOR_HEADER "" set FPROTO_HEADER "" set FHOST_HEADER "" ############### Insert "for" parameter ############### if {$INSERT_FORWARDED_FOR} { set FFOR_HEADER_LIST [list] lappend FFOR_HEADER_LIST "for=$CLIENT_ADDR" if {$KEEP_FORWARDED_FOR} { foreach item [split [HTTP::header "Forwarded"] ";"] { if {[string tolower [string trimleft $item]] starts_with "for"} { lappend FFOR_HEADER_LIST "$item" break } } } if {$CONVERT_XFF_TO_FORWARDED_FOR && [HTTP::header exists "X-Forwarded-For"]} { if {[HTTP::header value "X-Forwarded-For"] contains ","} { foreach item [split [HTTP::header value "X-Forwarded-For"] ","] { if {$item contains ":" && !($item contains "\[")} { lappend FFOR_HEADER_LIST "for=\[[string trim $item]\]" } else { lappend FFOR_HEADER_LIST "for=[string trim $item]" } } } else { lappend FFOR_HEADER_LIST "for=[HTTP::header value "X-Forwarded-For"]" } HTTP::header remove "X-Forwarded-For" } set FFOR_HEADER [join $FFOR_HEADER_LIST ","] } ############### Insert "proto" parameter ############### if {$INSERT_FORWARDED_PROTO} { set FPROTO_HEADER "proto=$REQUEST_PROTO" } ############### Insert "host" parameter ############### if {$INSERT_FORWARDED_HOST} { set FHOST_HEADER "host=[HTTP::host]" } ############### Insert HTTP header only if "by" parameter won't be included ############### ############### Else the whole header will be inserted in HTTP_REQUEST_RELEASE Event ############### if {!$INSERT_FORWARDED_BY} { HTTP::header remove "Forwarded" HTTP::header insert "Forwarded" [join "$FFOR_HEADER $FPROTO_HEADER $FHOST_HEADER" ";"] } } when HTTP_REQUEST_RELEASE { ############### Insert "by" parameter ############### if {$INSERT_FORWARDED_BY} { if {[set BY_ADDR [getfield [IP::local_addr] "%" 1]] contains ":"} {set CLIENT_ADDR "\[$BY_ADDR\]"} set FBY_HEADER "by=$BY_ADDR" HTTP::header remove "Forwarded" HTTP::header insert "Forwarded" [join "$FFOR_HEADER $FPROTO_HEADER $FHOST_HEADER $FBY_HEADER" ";"] } }
Tested this on version:
13.0Updated Jun 06, 2023
Version 2.0