Forum Discussion

Village_Idiot's avatar
Village_Idiot
Icon for Nimbostratus rankNimbostratus
Aug 09, 2021
Solved

Pool selection based on X-Forwarded-For header

We have moved our on-prem WAF out to a cloud solution and need to modify an iRule that is used to do pool selection based on client IP. Private IP's directed to an internal pool and public IP's directed to an external pool. The WAF is adding an X-Forwarded-For header and we want to modify the iRule to read the header and make a pool decision based on the IP using a data group with a list of our public IP's. Below is the current iRule that we are wanting to modify

when HTTP_REQUEST {
  set fqdn_name [HTTP::host]
	if { [class match [HTTP::host] equals DomainsToRedirect] } {
		HTTP::redirect  "http://$fqdn_name"
    } elseif { [HTTP::uri] starts_with  "/someuri" } {
    if { [lsearch {"CONNECT" "OPTIONS" "HEAD" "TRACE"} [HTTP::method]] > 0 } {
        reject
    } else {
	  set xlateuri [string map {"/someuri" "/"} [HTTP::uri]]
		HTTP::header replace HOST "host.domain.com"
		HTTP::uri "$xlateuri"
		if { [class match [IP::client_addr] equals private_net] } {
			pool Prod-Internal-Pool
		} else {	
			pool Prod-External-Pool
		}
	}
	} elseif { [lsearch {"CONNECT" "DELETE" "HEAD" "PUT" "TRACE"} [HTTP::method]] > 0 } {
		reject
	} elseif { [class match [IP::client_addr] equals private_net] }{
		pool Web-Internal-Pool
	} else {
		pool Web-External-Pool
	}
}

Thanks in advance

  • Hi, if WAF is adding XFF header, shouldn't that contain original IP of client and it can be anything in the world if site is public? Not sure, why you want to read XFF header instead of IP from WAF itself?

    since your current iRule is checking for client IP from the internal network (private_net) to send to internal pool and everything else is being sent to the external pool, you won't need any modification. It would anyway send the traffic from external WAF to the external pool. You can apply whitelist on the FW to only accept the traffic from WAF IP addresses.

    But still you are looking for sending traffic based on XFF header, you can use something like below.

    when HTTP_REQUEST {
       if { [HTTP::header exists X-Forwarded-For] } {
       set xff [HTTP::header X-Forwarded-For]
       return
       }
      set fqdn_name [HTTP::host]
    	if { [class match [HTTP::host] equals DomainsToRedirect] } {
    		HTTP::redirect  "http://$fqdn_name"
        } elseif { [HTTP::uri] starts_with  "/someuri" } {
        if { [lsearch {"CONNECT" "OPTIONS" "HEAD" "TRACE"} [HTTP::method]] > 0 } {
            reject
        } else {
    	  set xlateuri [string map {"/someuri" "/"} [HTTP::uri]]
    		HTTP::header replace HOST "host.domain.com"
    		HTTP::uri "$xlateuri"
    		if { [class match [IP::client_addr] equals private_net] } {
    			pool Prod-Internal-Pool
    		} elseif { ([info exists xff]) and ([class match $xff equals xff_net]) } { 
    			pool Prod-External-Pool
    		 } else {
    		   reject
    		 }	
    		}
    	} elseif { [lsearch {"CONNECT" "DELETE" "HEAD" "PUT" "TRACE"} [HTTP::method]] > 0 } {
    		reject
    	} elseif { [class match [IP::client_addr] equals private_net] }{
    		pool Web-Internal-Pool
    	}  elseif { ([info exists xff]) and ([class match $xff equals xff_net]) } { 
    	   pool Prod-External-Pool
    	 } else {
    		   reject
    		 }	
     }

2 Replies

  • Hi, if WAF is adding XFF header, shouldn't that contain original IP of client and it can be anything in the world if site is public? Not sure, why you want to read XFF header instead of IP from WAF itself?

    since your current iRule is checking for client IP from the internal network (private_net) to send to internal pool and everything else is being sent to the external pool, you won't need any modification. It would anyway send the traffic from external WAF to the external pool. You can apply whitelist on the FW to only accept the traffic from WAF IP addresses.

    But still you are looking for sending traffic based on XFF header, you can use something like below.

    when HTTP_REQUEST {
       if { [HTTP::header exists X-Forwarded-For] } {
       set xff [HTTP::header X-Forwarded-For]
       return
       }
      set fqdn_name [HTTP::host]
    	if { [class match [HTTP::host] equals DomainsToRedirect] } {
    		HTTP::redirect  "http://$fqdn_name"
        } elseif { [HTTP::uri] starts_with  "/someuri" } {
        if { [lsearch {"CONNECT" "OPTIONS" "HEAD" "TRACE"} [HTTP::method]] > 0 } {
            reject
        } else {
    	  set xlateuri [string map {"/someuri" "/"} [HTTP::uri]]
    		HTTP::header replace HOST "host.domain.com"
    		HTTP::uri "$xlateuri"
    		if { [class match [IP::client_addr] equals private_net] } {
    			pool Prod-Internal-Pool
    		} elseif { ([info exists xff]) and ([class match $xff equals xff_net]) } { 
    			pool Prod-External-Pool
    		 } else {
    		   reject
    		 }	
    		}
    	} elseif { [lsearch {"CONNECT" "DELETE" "HEAD" "PUT" "TRACE"} [HTTP::method]] > 0 } {
    		reject
    	} elseif { [class match [IP::client_addr] equals private_net] }{
    		pool Web-Internal-Pool
    	}  elseif { ([info exists xff]) and ([class match $xff equals xff_net]) } { 
    	   pool Prod-External-Pool
    	 } else {
    		   reject
    		 }	
     }