Learn F5 Technologies, Get Answers & Share Community Solutions Join DevCentral

Filter by:
  • Solution
  • Technology
Answers

LTM :: iRule to Limit Source Addresses

So... here's a weird one. And I understand it's not optimal...

...but say there is a crisis and management sends down the proclamation: "Only allow X sources at a time access to the server pool until the admins can fix XYZ on the systems involved". So we rush to figure out a way to do so... and come up with the below.

Other than blasting the table full of addresses (such as a resource exhaustion DDoS against the F5), are there any other caveats that I might not be thinking about here?

when CLIENT_ACCEPTED {
    set hsl [HSL::open -proto UDP -pool syslog-servers.pool] 
}

when HTTP_REQUEST {
    set source_ip [IP::client_addr]
    set ip_limit 2000

    # Delete all IPs
    # table delete -subtable conns -all

    if { [table lookup -notouch -subtable conns $source_ip] != 1 } {
        # Source IP doesn't exist in table, add to table
        table add -subtable conns $source_ip 1 900
    } else {
        # Source IP is in the table, actively involved, renew the timer
        table lookup -subtable conns $source_ip
    }

    if { [table keys -subtable conns -count] <= $ip_limit } {
        # The current IP count is less than alloted, allow pool access
        pool $pool_name
        # above variable acquired in prior logic
    } else {
        # The IP count has been reached. Do not provide pool access.
        HSL::send $hsl ":: Source IP limit ($ip_limit) hit for pool, redirecting to maintenance page."
        call maintenance_page.irule::display_page
    }
}
0
Rate this Question

Answers to this Question

placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

Hi Ryan,

the problem with your iRule is, that it would DoS your entire application once [table keys -subtable conns -count] has reached its configured limit. Even the [IP::client_addr] which has been allowed before will become blocked.

You may take a look to the iRule below how I would solve the puzzle. My iRule would allow the already known [IP::client_addr] until their [table -subtable] entry expires and then allow another [IP::client_addr] to access the application until its entry expires...

when RULE_INIT {
    set static::ip_limit 2000
    set static::ip_timeout 900
}
when CLIENT_ACCEPTED {
    set hsl [HSL::open -proto UDP -pool syslog-servers.pool] 
}
when HTTP_REQUEST {
    # Delete all IPs
    # table delete -subtable conns -all
    if { [table lookup -subtable conns [IP::client_addr]] ne "" } then {
        # The client is currently known in the table. 
        # Already refreshed the clients table entry.
        # Allow the request...
    } elseif { [table keys -subtable conns -count] < $static::ip_limit } then {
        # The client is not known in the table. 
        # The connection limit has not been reached. 
        # Create a new entry for the client.
        table set -subtable conns [IP::client_addr] 1 $static::ip_timeout indef
        # Allow the request...
    } else {
        # The client is not known in the table. 
        # The connection limit has been reached. 
        # Log the request...
        HSL::send $hsl ":: Source IP limit ($ip_limit) hit for pool, redirecting to maintenance page."
        # Display maintenance page...
        call maintenance_page.irule::display_page
    }
}

Note: I've removed the pool logic, since it shouldn't considered as security control. The premptive [HTTP::respond] of your meintenance macro should be more than sufficent...

Cheers, Kai

0
Comments on this Answer
Comment made 01-Feb-2018 by Ryan 508

Good call. Thanks for your input.

Yeah, the pool declaration isn't a security control, but rather used for dynamic resource assignment (not shown in example).

Another note, I noticed you changed to indef for timer. What then ages-out the source address?

Thanks again.

0
Comment made 01-Feb-2018 by Ryan 508

Combination -

set static::source_ip [IP::client_addr]
set static::ip_limit 2000

if { [table lookup -subtable conns $static::source_ip] ne "" } {
    # User was already provided access, allow to continue. Renew timer (via lookup above).
} elseif { [table keys -subtable conns -count] <= $static::ip_limit } {
    # New connection, below limit, add source to the connection table and allow to continue.
    table add -subtable conns $static::source_ip 1 900
} else {
    # Not an existing connection, connection limit has been hit, provide maintenance page
    HSL::send $hsl ":: Source IP limit, redirecting to maintenance page."
    call maintenance_page.irule::display_page
    return
}
pool $pool_name
0
Comment made 02-Feb-2018 by Kai Wilke 6942

Hi Ryan,

Dont use the combined iRule!

Manipulating static::* variables during runtime (e.g. HTTP_REQUEST event) should be avoided - unless you really know what you‘re doing and what side effects may happen.

In your specific example every client would now share the same $static::source_ip variable within a given TMM core. You set initially the $static::source_ip variable based on the currently processed client ip. The [table] commands may then suspend the processing of the ongoing connection (if the requested data is stored on a different TMM) and so that TMM may in the meantime start to process another client connection (but with different IP). When the previous suspended [table] command finally received the requested data and continues its processing, the $static::source_ip variable would not match anymore the currently processed client ip, since the value has been changed by another TCP connection in the meantime...

I recommend to keep the iRule I‘ve posted. The used events and variables are already very well selected...

Cheers, Kai

0
Comment made 06-Feb-2018 by Ryan 508

Yours was static. I didn't initially have them as static. So I don't need them now?

Your table add never expires. Why isn't there an expiration on the entry? It is set as indef.

0