Forum Discussion
Limiting Duplicate HTTP GETs
Hi folks. I'm new to DevCentral so I apologize if I'm posting in the wrong place. I've been running a couple of old v4 BIG-IPs for years and just recently made the jump to a couple of LTM-1600's with 10.0.1. That said, we occasionally receive duplicate GETs to one of our IIS/ColdFusion-based sites, from a single user. When this flood of GETs occurs, the requests come through at such a high rate (upwards of 50 requests per second) that it's causing us some headaches. Therefore, I need to develop an iRule that will handle the duplicates and discard them before they make it to the site.
To provide some additional information, we typically have many users NAT'd behind the same IP, but the floods originate from only one unique workstation. So it's really not feasible to block or discard requests based on IP. The good news is that our app sets a cookie which contains a number unique to each workstation. This cookie is passed back to our servers with each GET, so if we can leverage a rule which uses this cookie to identify the duplicates, that would be great. Ideally, I'd like to have a rule which would look for duplicate GETs (containing the same URL and unique string in our custom cookie) and reject subsequent requests which surpass a predetermined threshold.
I know this isn’t a lot of information to go off of and I can certainly provide more information as needed. After some poking around, I suspect there may be an existing rule which would do what we want to do. I stumbled across the following rule which appears to limit the number of POSTs: http://devcentral.f5.com/Wiki/iRules.RateLimit_HTTPRequest.ashx
It’s possible that rule could be adapted to help us out here. I’ve also seen this rule: http://devcentral.f5.com/wiki/iRules.HTTPRequestThrottle.ashx
The first rule seems to filter based on an authorization header, while the latter rule seems to filter on IP. Both rules would need to be adapted to our scenario, but being completely new to iRules, I’m not sure which would work better for us. I’m certainly able to provide more details, as needed, but I’d love to hear any ideas you guys might have. Thanks much for the help.
-Ryan
11 Replies
- ryan_111816
Nimbostratus
Sorry to bump this, but does anyone have any thoughts on this? - ryan_111816
Nimbostratus
Aaron - since you seem to be responding to most of the posts in here, maybe you can point me in the right direction. Since I'm not getting any help, would you recommend that I contact F5 support? The funny thing is that we just recently purchased the LTM-1600's over a competitor's product, specifically for the flexibility of iRules. It was explained to F5 sales that I'm new to iRules, even though I had a few v4 BIG-IPs already. DevCentral was then used as an additional selling point. I understand that DevCentral is a "community driven innovation", as the fancy logo states, but where should a customer turn when no help is provided? Am I on my own here?
Thanks,
Ryan - hoolio
Cirrostratus
Hi Ryan,
Sorry you haven't gotten any feedback. I did take an initial look at your post and the linked iRules but got sidetracked. Both rules initially looked like a good starting point. Let me take a closer look at your scenario and the examples to give you a suggestion.
As far as support for iRules:
I work for an F5 partner, so I can't speak for F5. F5 Support doesn't support iRules unless it's a suspected bug in the iRule functionality. I think this is fairly understandable as customers can do quite a lot with iRules and F5 probably doesn't want to get stuck troubleshooting custom scripting that they don't provide. So F5 will point you to Devcentral for free advice or to their consulting group for paid work. They also offer an iRules on demand service for quick paid help:
http://www.f5.com/training-support/professional-services/offerings/irules-on-demand.html
Aaron - hoolio
Cirrostratus
Hi Ryan,
I think Deb's rule, RateLimit_HTTPRequest, is a little more straightforward for your scenario. The other rule has more functionality but is more complicated than what I think you need.
Are there specific URIs and/or client IP address ranges that you want to rate limit GETs for?
What do you want to do if a client makes multiple GET requests without the session identifying cookie?
Are the clients potentially malicious, or just badly configured/implemented? ie, will they potentially modify or delete their cookies?
When the flood of requests comes, is it from many different clients at the same time? To many different URIs?
Also, which LTM version (b version | head) and platform (b platform | head) are you using?
Thanks,
Aaron - ryan_111816
Nimbostratus
Thanks for the response Aaron. Here are some answers:
If we knew we could apply the new rule to all URIs without causing a performance hit, I think we would do that. But there's only two URIs that are causing us problems, so it's probably safer to limit this rule to just those. As for IP ranges, we've seen these HTTP floods from several different customers, so we'd prefer to not restrict this by IP.
I should tell you that we're not setting a session cookie on the application in question, as persistence isn't a requirement. Rather, what we are setting is a unique workstation-specific cookie. The first time the user visits our site, we set this cookie (i.e "workstationid=xxxxxxxx"). We then can track that user/workstation anywhere on our site. What happens when we see a flood is the duplicate GETs all contain this same workstationid cookie, so it's either the actual workstation/browser resending the requests, or it's a proxy in the middle that is stuck in a loop and sending the same request over and over. But to answer your question, if we receive a GET without that cookie, which is rare, I'd just assume we allow them access. In all of the cases of these floods, the cookie has been properly set.
The clients are not malicious. These are our own customers (schools) that have paid for our product. Therefore, I'm not worried about them deleting or manipulating cookies.
The flood is always from a single client to a single URI. For example, we’ll be seeing normal traffic for days or weeks at a time, and then we’ll receive 30,000 duplicate requests, within the period of maybe 20 minutes, to: www.mysite.com/index.html. But again, because we’re dealing with schools that are using one-to-many NAT, we can’t block the IP because there may be a few hundred other students using our program behind the same IP.
LTM-1600 v10.0.1 (build 283.0 final) in an active/passive HA configuration. Not sure what other info you’re looking for.
Lastly, I should provide a little more information on the issue. I recently captured a trace during a flood, and what I found is that each duplicate GET is actually spawning a new, separate TCP connection each time. So aside from the additional overhead of the HTTP requests, our servers have to participate in the three-way handshake for each duplicate request. This may or may not have any bearing on the iRule, since we still need to dive into the layer 7 portion of the packets and identify a cookie, but I just thought I’d mention it. Thanks again for your help Aaron. - L4L7_53191
Nimbostratus
For this particular use case 10.0.1 may come to the rescue.
We may be able to use the 'after' command here to setup a simple timer per workstationid cookie. If they request the same URI more than a couple of times, drop them into an 'after' code block to start the counter - after .5 seconds (or whatever interval) if they've passed some threshold of the same request, drop the connection.
I won't be in a position to set this up and write an example for a few days, unfortunately. But I wanted to get this idea out there in the event that someone else may want to take a crack at this and see if it's a good fit.
-Matt - L4L7_53191
Nimbostratus
FWIW, here is a link to Colin's post on the after command, with sample code. This one is obviously all l4 stuff, but conceptually it's very similar.
-Matt
- ryan_111816
Nimbostratus
Matt,
Being new to iRules, I'm unsure how hard your suggestion would be to implement. But stepping back and looking at this from a high level, I think what you have suggested is exactly what we want to do. I'd be greatful if you could come up with a test rule for us. This is something I'd like to implement in the next few weeks, so I do have a little time to get something worked out.
Thanks. - hoolio
Cirrostratus
Hi Matt / Ryan,
I was thinking of just tracking the number of requests made to a restricted URI over the course of a timeout period using the session table. A request would only be allowed if the cookie / path combination was under a limit of requests over a configured time interval.
One downside I can see with using the session table like this is that the count of requests could get distorted if requests are spaced out in the timeout interval. For example if the timeout interval was 5 seconds, and there was a request every four seconds, the count would accumulate all of the requests until the count reached the limit. This might not be an issue based on the problem statement of lots of requests over a very short period of time.
The other option I can think of for handling this with the session table would be to use the cookie/path as the key and insert a list of the request times. You could then loop through the list and clear out any requests that are older than the timeout period. This could get very expensive in terms of CPU usage, especially if the request limit is set to a high value.
Anyhow, here is one approach to the scenario:when RULE_INIT { Log debug messages to /var/log/ltm? 1=yes, 0=no set static::get_debug 1 Name of the session cookie that the application sets set static::session_cookie "workstationid" Datagroup name containing the paths to limit GET requests to The requested path is checked to see if it starts with any path string in this class set static::get_class "get_limit_paths_class" Timeout to track requests for (in seconds) set static::timeout 10 Max number of requests to a restricted path per the timeout period set static::max_requests 1 HTML content to send to clients who exceed the max number of requests You shouldn't need to escape any characters in between the curly braces (except curly braces). set static::reject_html [subst -nocommands {Give it a restYou're making too many requests}] } when HTTP_REQUEST { if {$static::get_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: [HTTP::method] to [HTTP::host][HTTP::uri]"} Exit this event in this rule, if request is not a GET if {not ([HTTP::method] eq "GET")}{ if {$static::get_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Exiting for method [HTTP::method]"} return } Exit this event in this rule if request is not to a restricted path if {[class search $static::get_class starts_with [HTTP::path]]}{ if {$static::get_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Exiting for path [HTTP::path]"} return } Exit this event in this rule if request does not include the session cookie if {[HTTP::cookie $static::session_cookie] eq ""}{ if {$static::get_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Exiting for no session cookie"} return } If we're still executing here, it's a GET request to a restricted URI with a session cookie Check if the cookie / path combo has a request count set count [session lookup uie "[HTTP::cookie $static::session_cookie][HTTP::path]"] if {$static::get_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: count: $count"} Check if session table entry exists if {$count eq ""}{ No session table entry for this cookie/path, so add one session add uie "[HTTP::cookie $static::session_cookie][HTTP::path]" 1 $static::timeout if {$static::get_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Initialized count for\ [HTTP::cookie $static::session_cookie][HTTP::path]"} } else { Check if count is less than max allowed if {$count < $static::max_requests}{ Increment the session table entry count for this cookie / path session add uie "[HTTP::cookie $static::session_cookie][HTTP::path]" [incr count] $static::timeout if {$static::get_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Incremented count to $count\ for [HTTP::cookie $static::session_cookie][HTTP::path]"} } else { Client is over maximum allowed number of requests to this URI HTTP::respond 503 content $static::reject_html if {$static::get_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: $count over limit $static::max_requests\ for [HTTP::cookie $static::session_cookie][HTTP::path]"} } } }
Aaron - hoolio
Cirrostratus
Another thing to keep in mind is that using a global array to track the requests would break CMP compatibility (Click here) for the VIP the iRule is enabled on.
Aaron
Help guide the future of your DevCentral Community!
What tools do you use to collaborate? (1min - anonymous)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
