Forum Discussion
Universal Persistence with X-forwarder
- Jul 20, 2016
A formatted version of the "Per VS" rate limiting. You can apply the same irule to all standard VS using UIE persistence.
when RULE_INIT { set static::maxReqs 3; set static::timeout 60; } when HTTP_REQUEST { set vs [URI::basename [virtual]] if { [HTTP::header exists "X-Forwarded-For"] } { set client_IP_addr [getfield [lindex [HTTP::header values "X-Forwarded-For"] 0] "," 1] } else { set client_IP_addr [IP::client_addr] } if { ([HTTP::method] eq "GET") and ([class match [string tolower [HTTP::uri]] ends_with $vs_URI_LIST_TO_LIMIT] ) } { whitelist if { [class match [IP::client_addr] equals $vs_ips_whitelist] }{ return } set getcount [table lookup -notouch "$vs_$client_IP_addr:[HTTP::uri]"] if { $getcount equals "" } { table set "$vs_$client_IP_addr:[HTTP::uri]" "1" $static::timeout $static::timeout } else { if { $getcount < $static::maxReqs } { table incr -notouch "$vs_$client_IP_addr:[HTTP::uri]" } else { reject } } } persist uie $clientip } when HTTP_RESPONSE { persist add uie $clientip }
Hi Yann
Getting a whole bunch of errors on this. Can you assist please? I can't debug, not from coding background.
01070151:3: Rule [/Common/iRule_rate_limit] error: /Common/iRule_rate_limit:8: error: [command is not valid in the current scope][if { ([HTTP::method] eq "GET") and ([class match [string tolower [HTTP::uri]] ends_with URI_LIST_TO_LIMIT] ) } ] /Common/iRule_rate_limit:10: error: [undefined procedure: set getcount [table lookup -notouch "$client_IP_addr:[HTTP::uri]"] if { $getcount equals "" } { table set "$client_IP_addr:[HTTP::uri]" "1" $static::timeout $static::timeout } else { if { $getcount < $static::maxReqs } { table incr -notouch "$client_IP_addr:[HTTP::uri]" } else { reject } } ][{ set getcount [table lookup -notouch "$client_IP_addr:[HTTP::uri]"] if { $getcount equals "" } { table set "$client_IP_addr:[HTTP::uri]" "1" $static::timeout $static::timeout } else { if { $getcount < $stat
Regards,
Sumanta.
- Yann_DesmarestAug 16, 2016Cirrus
Hi,
On my side, I just tested the following irule (same as previous but without comments) :
when RULE_INIT { set static::maxReqs 20000; set static::timeout 1800; } when HTTP_REQUEST { set client_IP_addr [IP::client_addr] if { ([HTTP::method] eq "GET") and ([class match [string tolower [HTTP::uri]] ends_with URI_LIST_TO_LIMIT] ) } { set getcount [table lookup -notouch "$client_IP_addr:[HTTP::uri]"] if { $getcount equals "" } { table set "$client_IP_addr:[HTTP::uri]" "1" $static::timeout $static::timeout } else { if { $getcount < $static::maxReqs } { table incr -notouch "$client_IP_addr:[HTTP::uri]" } else { reject } } } }
And it's working. I'm using 12.1.0 but should be fine on older version. Can you repost the irule you try to use ?
- Sumanta_88744Aug 16, 2016Cirrus
Hi Yann
Thanks. See this below please. I am using 11.6.0 with HF 5. I removed the whitelist IP group and XFF sections since I am testing with Fast L4 profile on https port.
when RULE_INIT { set static::maxReqs 20000; set static::timeout 1260; } when HTTP_REQUEST { set client_IP_addr [IP::client_addr] } if { ([HTTP::method] eq "GET") and ([class match [string tolower [HTTP::uri]] ends_with URI_LIST_TO_LIMIT] ) } set getcount [table lookup -notouch "$client_IP_addr:[HTTP::uri]"] if { $getcount equals "" } { table set "$client_IP_addr:[HTTP::uri]" "1" $static::timeout $static::timeout } else { if { $getcount < $static::maxReqs } { table incr -notouch "$client_IP_addr:[HTTP::uri]" } else { reject } } }
- Sumanta_88744Aug 16, 2016Cirrus
Hi Yann
The last one you posted is working, probably I got some wrong braces. :(
Let me test this with traffic and see if I can perform any DoS attack on the virtual server.
Regards,
Sumanta.
- Sumanta_88744Aug 16, 2016Cirrus
Hi Yann
Another error, while trying to apply to the L4 vs.
01070151:3: Rule [/Common/iRule_rate_limit] error: Unable to find value_list (URI_LIST_TO_LIMIT) referenced at line 7: [class match [string tolower [HTTP::uri]] ends_with URI_LIST_TO_LIMIT]
What values to be entered under "URI_LIST_TO_LIMIT"? I only have a single vs running on port 443, with end user connecting to this for accessing back end services. Do I have to list the exact URIs, which is contained inside the https requests, from client side? How will the rule behave if there is any URI, outside this "URI_LIST_TO_LIMIT" pool?
Regards,
Sumanta.
- Kai_WilkeAug 16, 2016MVP
Hi Sumanta,
while pentesting the rate limiting functionality, make sure you request lots of different URIs (e.g. add a random query string of your choice to each single request) at a high request rate. Depending on the available bandwith to your Virtual Server, this test will have the potential to stall your entire LTM... 😞
Note: The problem of this specific rate-limiting implematation is, that it creates table records (e.g. this requires RAM ) based on untrusted "freetext" user input. So please be aware of it...
Cheers, Kai
- Sumanta_88744Aug 16, 2016Cirrus
Hi Kai
Thanks for your input. Is there any way out of it? I haven't done the testing yet, getting some errors on the rule.
I am not sure if the default security settings on F5 (such as adaptive reaping) can prevent a DOS attack on the VS, so I had to query DC in order to get any information. Our intention is to allow legitimate users to access the servers and not get locked out due to random DOS.
Regards,
Sumanta.
- Kai_WilkeAug 17, 2016MVP
Hi Sumanta,
the easiest way would be to not table track the individual URIs. Tracking the individual IPs will be most likely enough, to identify way too agressive users/attackers. In this case the maximum number of table entries will be capped and in additiona are more or less secured by the 3-way handshake of the IP protocol (aka. TCPs build-in spoofing check)
If tracking of specific URL is required, then track just on the HTTP::path and insert/increase the table entries if a the HTTP::status code of your application meets certain criterias (e.g. 200 OK, 301 Moved, etc.) and create an IP::client_addr based table entry for the remaining error codes, to not exhaust the memory at the attackers will.
In addition to that the provided iRule code can be further improved, to become more resilent to DoS attacks (e.g. less CPU cycles required in DoS conditions) and if required to count more accurate (e.g. using a fixed time frame burst mode tracking or even a sliding window tracking).
Cheers, Kai
- Sumanta_88744Aug 17, 2016Cirrus
Hi Kai
Thanks for your input, but I can't understand. Sorry.
Regards,
Sumanta.
- Kai_WilkeAug 17, 2016MVP
See it this way: The counting of individual request and to be able to block further requests, if counters have reched will require certain amount of memory on your LTM.
Depending on which information (IP::addr, the HTTP::path, or even the full HTTP::uri which may contain several kbyte of data) and under which conditions the counters are getting increased (an attacker sends an (yet) unverified HTTP::uri or the backend server has already verified the request as valid), the total memory consumption can be more or less predicted and the accepted with an clear conscience, or just get out of control and stall your entire LTM.
The table command can be your best friend, but also become itself a security issue if its not well implemented. And nothing is worse than implementing a DoS protection for a single website that could have an negative impact on every other website, isn't it?
Cheers, Kai
Recent Discussions
Related Content
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com