v10.1 - iRules rate limiting with the table command

One of the new features added to BIG-IP in version 10.1 was the table command, implemented in iRules. Of all the new features I've seen, I have to say this one is easily one of my favorites. Hopefully by now you've seen Spark's amazing series of articles on the table command and have gotten some sort of understanding of what it is and can do. If not, I highly recommend checking out the first of his many fantastic docs here, to get started, then browse through them all when you can. They’re awesome, and well worth the read. Given the many capabilities of the new commands, it really pushes the boundaries for what you're able to accomplish with iRules. It not only makes for more options, but makes some things you could already do much, much simpler.

One of the many things that people seem to want to do often via iRules is rate limit connections. Whether they're limiting the number of HTTP connections to a given URI a user is allowed to have open, or how many connections a given virtual can have, they're always looking to track a given number of connections of a certain type over a given amount of time. While this was possible before, it was relatively clunky and complex. With the new table command, it has gotten significantly easier.

It has gotten so easy, in fact, that I found myself sitting in a meeting months ago when the table command was being revealed internally to a group of us iRuleish types and thinking about the possibilities. The person sitting next to me and I quickly found ourselves engaged in a conversation about just how simple it would be to write iRules to do this kind of thing now with the new command, and how much of a pain it was before, comparatively. He was talking about limiting the number of SSH connections to his system to keep out would be intruders, and we quickly white-boarded a simple version out, sans white board (verbally). When I later saw the finished product I had to share.

Here's a look at just how simple previously complex tasks can be made thanks to the table command and its ability to manage sub table entries. In this example you'll find a simple way to check for a given number of connections coming from a single IP address, within a configurable time period, and deny-list those connections for a separately configurable period of time if they exceed your allowed number of tries.

For instance, let's say you have some resource you want to rate limit access to. In this example it's an ssh server, but it could be anything. You want to make sure that any given IP address only opens up 1 connection every 10 seconds. This may seem strict, but for a resource like SSH, there shouldn't be many people that need to open more connections than that. Plus this is easily configurable, it's just an example. If, in this case, someone opens more than a single connection in a 10 second period, they get dropped any time they try to connect for 10 seconds. If they continue to offend and try to connect, they get blocked for even longer.

To look a bit more deeply at the way this is set up we simply look at the sub tables set up with the table command. When the initial request comes in, we look to see if it's in the "long_haul" table, meaning they're a double offender and locked out for 24 hours (86400 seconds). If they're not in that table, we check to see if they're in the "short_haul" table. Entries in this table only last 10 seconds, so they won't be in there unless they've tried to connect  within that period. If they're not in either sub table, then we add them to the shorter duration table and pass the traffic normally. Assuming they don't try to re-connect for 10 seconds, that's the last interaction they'll have with the iRule until next time they log in and all will be well.

If they do try again within 10 seconds, however, the iRule will find them in the short table, since the entry hasn't expired yet, and it will drop the connection. It will also then promote them to the longer duration table. This means that, because they are now suspected of mischievous activity, their connections to this resource will be dropped for 24 hours. Strict? Well yeah, but a great example of the kind of logic you can quickly and easily build with the table command in iRules. I might change these settings to allow for 3 connections in 10 seconds, rather than one, and to time someone out for 10 minutes, not 24 hours..but hey, that part's up to you. That's half of the beauty of a language like iRules. A great way to do this would be to simply increment the value found in the short_haul table until it hits whatever threshold you want, rather than promoting them the first time you find them in the short table. Once it hits say 5 or 10 tries, then promote their IP to the long_haul table for a longer lasting slap on the wrist. But those details are all up to you and your situation.

Now keep in mind with anything that's going to automatically start dropping connections you'll want to build some kind of a fail safe. Maybe a simple iRule on an internal only virtual that you can send IP addresses to to un-block them so they stop getting dropped. The last thing you want to do is start dropping your boss' connections because he got his login wrong, and not have an easy way to fix it. Just keep that in mind and tune the settings to your preference and you'll be set.

 

   1: rule rule_block_ssh_attack {
   2:   when RULE_INIT {
   3:     set static::short_life 10
   4:     set static::long_life 86400
   5:   }
   6:   when CLIENT_ACCEPTED {
   7:     set ip_addr [IP::client_addr]
   8:     set dest_addr [IP::local_addr]
   9:     set key "$ip_addr:$dest_addr"
  10:     set val [table lookup -notouch -subtable ssh_hosts_long $key]
  11:     if {$val == 1} {
  12:       log local0. "Dropped $ip_addr -> $dest_addr for the long haul."
  13:       drop
  14:     } else {
  15:       set val2 [table lookup -notouch -subtable ssh_hosts_short $key]
  16:       if {$val2 == 1} {
  17:         table set -subtable ssh_hosts_long $ip_addr 1 $static::long_life
  18:         log local0. "Dropped $ip_addr -> $dest_addr for the short haul."
  19:         drop
  20:       } else {
  21:         table set -subtable ssh_hosts_short $key 1 $static::short_life
  22:       }
  23:     }
  24:   }
  25: }

I’m positive there will be a thousand more examples of how cool the table command is and what you can do with it, but here’s one more added to Frank’s already great examples. Big thanks to user knox for the chat months ago, and the code.

 
Published Jan 28, 2010
Version 1.0

Was this article helpful?