I’m having some really odd results in using the following aFlex rule. I would expect that the logic would reject inbound requests for a period of 20 seconds following 10 consecutive bad requests. After 5 (not 10) I see the blocks and once the delay time has expired I see the requests fulfilled. However without sending any further bad requests I still get blocked after about 10 sec.
I’m writing direct to the sessions table and incrementing a value. Personally the logic seems right but obviously it’s not. Can someone help?
when RULE_INIT { set ::BAD_REQUESTS 10 set ::DELAY 20 }
when HTTP_REQUEST { set IP [IP::client_addr] set bad_request_limit [table lookup rate_limit $IP] if {$bad_request_limit >= $::BAD_REQUESTS} { reject return }
}
when HTTP_RESPONSE { if {([HTTP::status] == 404) or ([HTTP::status] == 500) or ([HTTP::status] == 503)} { set counter [table lookup rate_limit $IP] switch $counter { “” { table set rate_limit $IP 1 indefinite $::DELAY } $::BAD_REQUESTS { table incr rate_limit $IP table lifetime rate_limit $IP $::DELAY } default {table incr rate_limit $IP} } return } }
#################################################
#
# Rate-limit for Failed Requests per timeframe
# (c) A10 Networks -- MP
# v1 20140312
#
#################################################
#
# aFleX script to rate-limit based on requests
# per second and failed responses from the server.
#
# ::MAX_FAILED holds the number of requests that can be
# done before the client is blacklisted.
#
# The ::HOLDTIME_FAILED is the time in seconds.
#
# ::RATE is the amount of requests per timeframe (in seconds)
#
# ::DEBUG can be set to 1, 2 or 3.
#
# Scalability of this aFlex is unknown.
#
# Questions & comments welcome.
# mpeters AT a10networks DOT com
#
#################################################
when RULE_INIT {
set ::DEBUG 0
set ::MAX_FAILED 10
set ::HOLDTIME_FAILED 20
set ::RATE 2
}
when HTTP_REQUEST {
set IP [IP::client_addr]
if { [table lookup blacklist $IP] != "" } {
reject
if { $::DEBUG > 1 } { log "$IP -> blacklist expires in [table lifetime blacklist -remaining $IP] seconds" }
return
}
if { [table lookup tmp_blacklist $IP] == "" } {
table set tmp_blacklist $IP 1
if { $::DEBUG > 2 } { log "$IP -> request counter created" }
}
}
when HTTP_RESPONSE {
if { ([HTTP::status] == 404) or ([HTTP::status] == 500) or ([HTTP::status] == 503) } {
if { [table lookup tmp_failed $IP] == "" } {
table set tmp_failed $IP $::RATE
if { $::DEBUG > 2 } { log "$IP -> failed response counter created" }
}
set failed_count [table incr tmp_failed $IP]
if { $::DEBUG > 2 } { log "$IP -> $failed_count of $::MAX_FAILED failed requests" }
table lifetime tmp_failed $IP $::RATE
if { $failed_count > $::MAX_FAILED } {
table add blacklist $IP "failed response" indef $::HOLDTIME_FAILED
if { $::DEBUG >= 1 } { log "$IP -> blacklisted for $::HOLDTIME_FAILED seconds" }
table delete tmp_failed $IP
if { $::DEBUG > 2 } { log "$IP -> removed from tmp_failed" }
return
}
}
}
If you want to view what is inside the table you can use the attached file. Bind this to a free VIP or VPORT and use it as: http://[IP]:[PORT]/status:[table_name] For example: http://192.168.1.34:8884/status:blacklist
#################################################
#
# View / Flush contents of a table
# (c) A10 Networks -- MP
# v1 20140312
#
#################################################
#
# aFleX script to view contents of a table with
# the option to flush the complete table.
#
# Scalability of this aFlex is unknown.
#
# Questions & comments welcome.
# mpeters AT a10networks DOT com
#
#################################################
when HTTP_REQUEST {
set ACTION [getfield [HTTP::uri] ":" 1]
set TABLE [getfield [HTTP::uri] ":" 2]
if { $ACTION eq "/flush" } {
table delete $TABLE -all
HTTP::respond 200 content "Table $TABLE deleted... <a href=\"/status:$TABLE\">Back to STATUS</a>" Content-Type "text/html"
} elseif { $ACTION eq "/status" } {
set response "<html><head><meta http-equiv=\"refresh\" content=\"60\"><title>Contents of Table: $TABLE</title></head>"
append response "<body><center><h1>Contents of Table: $TABLE</h1><table border=\"1\" cellpadding=\"5\" cellspacing=\"0\">"
append response "<tr><th>Key</th><th>Value</th></tr>"
log "TABLE: [table keys $TABLE]"
set i 0
foreach tr [table keys $TABLE] {
incr i
if { $i == 1 } {
append response "<tr><td>$tr</td>"
}
if { $i == 2 } {
append response "<td>$tr</td></tr>"
set i 0
}
}
append response "</table><p>DELETE TABLE: <a href=\"/flush:$TABLE\">$TABLE</a></p>"
append response "</center></body></html>"
HTTP::respond 200 content $response Content-Type "text/html"
} else {
HTTP::respond 200 content "Usage is prohibited!"
}
}