Simple Connection Rate Limit

jmos5156jmos5156 Member
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
}
}

Comments

  • mischamischa Member
    edited March 2014
    Try the attached file.
    #################################################
    #
    # 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
        }
      }
    }
    
  • mischamischa Member
    edited March 2014
    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!"
      }
    }
    
Sign In or Register to comment.