Forum Discussion
F5 irule - Failing to include the key from subtable in the generated log message
Borrowing ideas from other iRule posts, I have created an iRule that is supposed to generate an alert when a user (MSISDN) performs more that four requests per minute. The alert is triggered as expected but the issue is that the log generated does not bear the MSISDN. Below is the iRule and the alert generated. Kindly help to have the MSISDN included in the log message. another approach is also welcome.
when RULE_INIT { set static::maxRate 5; set static::timeout 60;} when HTTP_REQUEST { set key [HTTP::header "MSISDN"] set getCount [table lookup -notouch -subtable requests $key] if { $getCount equals "" } { log local0. "New one: getCount=$getCount $key [clock seconds]" table set -subtable requests $key "1" $static::timeout $static::timeout } else { if { $getCount < $static::maxRate } { table incr -notouch -subtable requests $key] } else { if {$getCount == $static::maxRate } { log local0. "User @ $key [clock seconds] has reached $getCount requests in $static::timeout seconds." table incr -notouch -subtable requests $key } } } }
GENERATED ALERT
adc01 info tmm1[18644]: Rule /Common/Security_Alert : User @ has reached 5 requests in 60 seconds.
5 Replies
- ekaleido
Cirrus
After
set key [HTTP::header "MSISDN"]
Try setting
log local0. the value of key is $key
To see if you're even setting the variable properly.
- John_Mwaura_193
Nimbostratus
ekailado: Please elaborate since i already have " log local0. "User @ $key ....." set
- Stanislas_Piro2
Cumulonimbus
Hi,
if the header is not found, all the irule is useless. that's why ekailado asked to log the key value first.
your irule format is wrong:
instead of using
if { condition } { actions } else { if {other conditions} { other actions } else { if {another condition } ... } } }use:
if { condition } { actions } elseif {other conditions} { other actions } elseif {another condition } ... }another optimization is the last condition is wrong...
if there is more than 5 requests, there is no more logs. is it the expected behavior?
key is not the only string not in log... [clock seconds] result does dot appears.
- VernonWells
Employee
I disagree just slightly with
@Stanislashere. While your conditional structure is perhaps more unwieldy than the proposed alternative, it isn't functionally incorrect to do it your way, nor is computationally more (or less) expensive.For posterity, I'm re-including your iRule, formatted for improved readability:
when RULE_INIT { set static::maxRate 5 set static::timeout 60 } when HTTP_REQUEST { set key [HTTP::header "MSISDN"] set getCount [table lookup -notouch -subtable requests $key] if { $getCount equals "" } { log local0. "New one: getCount=$getCount $key [clock seconds]" table set -subtable requests $key "1" $static::timeout $static::timeout } else { if { $getCount < $static::maxRate } { table incr -notouch -subtable requests $key] } else { if { $getCount == $static::maxRate } { log local0. "User @ $key [clock seconds] has reached $getCount requests in $static::timeout seconds." table incr -notouch -subtable requests $key } } } }Far more importantly, and just as
@Stanislassays, it's most likely that the MSISDN header is simply missing. You will, in that case, still write a table entry. The key will be the empty string. Here is an alternative (not tested!):when RULE_INIT { set static::maxRate 5 set static::timeout 60 } when HTTP_REQUEST { if { [HTTP::header exists MSISDN] } { set key "count-msisdn-[HTTP::header MSISDN]" set count [table incr $key] if { $count == 1 } { do this to set the timeout and lifetime, overriding 'table incr' defaults table set $key 1 $static::timeout $static::timeout } elseif { $count == $static::maxRate } { log local0. "User @ $key has reached $count requests in $static::timeout seconds." reject } elseif { $count > $static::maxRate } { reject } } }I'm just assuming you wish to reject when the threshold is met. Naturally, you can do whatever you wish. Notice I perform a table incr then see if it is 1 (which it will be if it didn't previously exist). In your code, you perform two table commands for each request message. My code executes two table commands only for the first request with a given MSISDN in a single interval period. For subsequent requests with that same MSISDN in the same period, only a single table command is executed. table commands are somewhat expensive, so reducing the count is useful. Notice, further, that I dropped the -notouch. Since your lifetime is the same as your timeout, the -notouch (which only relates to the timeout) shouldn't be needed. Finally, I moved this from a sub-table to the main table. In general, sub-tables should only be used if you need to enumerate the keys.
I cut the clock seconds because clock operations are also fairly expensive, and the syslog entry will be timestamped automatically. For production, I'd recommend using High-Speed Logging rather than local logging.
- John_Mwaura_193
Nimbostratus
Finally, i have this working. It might not be the optimal code in terms of resources utilization and structure but it is giving the desired results. Initially, i was searching for the "MSISDN" in the header fields but after analyzing the request, i realized that the "MSISDN" is in the payload and it is identified in various ways. Below is the working code,
Note: The code is not meant to reject requests after a certain number but to generate logs which are then sent to security team to do further analysis
when RULE_INIT {
This is the max requests allowed during "interval" specified below.set static::maxRate 5; set static::timeout 60; }
when HTTP_REQUEST {
HTTP::collect [HTTP::header Content-Length] }
when HTTP_REQUEST_DATA { set first_search [findstr [HTTP::payload] "" 9 "/"] if {$first_search ne "" } { set MSISDN $first_search } else { set second_search [findstr [HTTP::payload] "" 12 "/"] if {$second_search ne "" } { set MSISDN $second_search } else { set MSISDN [findstr [HTTP::payload] "" 16 "/"] } } set getCount [table lookup -notouch -subtable requests $MSISDN] if { $getCount equals "" } { log local0. "New one: $MSISDN [clock seconds]" table set -subtable requests $MSISDN "1" $static::timeout $static::timeout } else { if { $getCount < $static::maxRate } { table incr -notouch -subtable requests $MSISDN } else { if {$getCount == $static::maxRate } { log local0. "User @ MSISDN $MSISDN has reached $getCount requests in $static::timeout seconds." table incr -notouch -subtable requests $MSISDN }
} } }
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