ASM/WAF rate limit and block clients by source ip (or device_id fingerprint) if there are too many violations
Hello the F5 Session Awareness is a great feature but it is CPU intensive and this solution if for blocking the bad actors after the tcp_handshake (CLIENT_ACCEPTED) when the system does not havevery much free CPU or after the first HTTP checks at the HTTP profile (HTTP_REQUEST). What could I have done is to make the irule with ASM::fingerprint event (https://clouddocs.f5.com/api/irules/ASM__fingerprint.html) but then the code will look like the one below as the ASM::fingerprint command can only be used under ASM events and because the processing will be all at the ASM event better then to use the Session Awareness as the CPU difference may not be much. The only point is if the client starts attacking to be blocked after 3 or 4 detected attempts as there could an attack that even the F5 ASM/WAF may not catch but as you mentioned Session Awareness is better in this case if we want to use the device ID or we have enough CPU for to also use session awareness for the client address or user session if we have APM + ASM.
The example is like the one in https://clouddocs.f5.com/training/community/irules/html/class2/module1/lab2.html but modified for ASM with fingerprint
when RULE_INIT {
# The max requests served within the timing interval per the static::timeout variable
set static::maxReqs 4
# Timer Interval in seconds within which only static::maxReqs Requests are allowed.
# (i.e: 10 req per 2 sec == 5 req per sec)
# If this timer expires, it means that the limit was not reached for this interval and
# the request counting starts over. Making this timeout large increases memory usage.
# Making it too small negatively affects performance.
set static::timeout 2
}
when ASM_REQUEST_DONE {
#log local0.debug "\[ASM::status\] = [ASM::status]"
if { [ASM::status] equals "blocked" } {
# Allows throttling for only specific URIs. List the URIs_to_throttle in a data group.
# Note: a URI is everything after the hostname: e.g. /path1/login.aspx?name=user1
if { ( [class match [HTTP::uri] equals URIs_to_throttle] ) and not ( [class match [IP::client_addr] equals whitelist] ) } {
# The following expects the IP addresses in multiple X-forwarded-for headers.
# It picks the first one. If XFF isn’t defined it can grab the true source IP.
set cIP_finger [ASM::fingerprint]
set getcount [table lookup -notouch $cIP_finger]
if { $getcount equals "" } {
table set $cIP_finger "1" $static::timeout $static::timeout
# Record of this session does not exist, starting new record
# Request is allowed.
} else {
if { $getcount < $static::maxReqs } {
log local0. "Request Count for $cIP_finger is $getcount"
table incr -notouch $cIP_finger
# record of this session exists but request is allowed.
} else {
#drop
HTTP::respond 403 content {
<html>
<head><title>HTTP Request denied</title></head>
<body>Your HTTP requests are being throttled.</body>
</html>
}
}
}
}
}
}