Version 9.x session limiting
Problem this snippet solves:
This iRule limits the amount of sessions. The limits of the Session command in version 9.x forces us to use a global array so we know the count of sessions. There is also a reaper function to ensure that stale sessions don't continue to eat up sessions
This iRule is tailored for an application which uses JSessionID cookies or tokens in the URI. If the application does not use a JSessionID, you'll need to remove the URI checking logic and customize the name of the application's session cookie. If the application doesn't use a session cookie, you could modify this iRule to set one in the response if the request doesn't contain one already.
Code :
#timing on when RULE_INIT { #Defines session limit set ::limit 4 #Defines Debug Level (0=no logging 1=logging) set ::session_debug 1 #Defines Session Timeout set ::timeout 300 #Defines the session array array set ::sessionar "" #Defines the lastrun global variable used by the reaping process set ::lastrun 0 #Defines the timeout in seconds that the reaper is to run set ::sessreap 60 #Defines the redirect webpage set ::busy_redirect "http://www.example.com/busypage.html" } when HTTP_REQUEST { #if {$::session_debug}{log local0. "Got an http request..."} #Sets the current time in seconds. set currtime [clock seconds] #if { $::session_debug }{ log local0. "Session List: [array names ::sessionar]"} #if { $::session_debug }{ log local0. "Value of lastrun: $::lastrun"} #if {$::session_debug}{log local0. "Value of currtime: $currtime"} if { [info exists ::sessionar(loggedoutcontinue)] }{ unset ::sessionar(loggedoutcontinue) } #This is the reaping process. This checks upon every connection the amount of time since the last reap #and if that reap is greater than the value of the $::sessreap global it executes. The reap process will #remove sessions that have been inactive for 301 seconds or more and leave any sessions 300 or lower. set since [expr {$currtime - $::lastrun}] if {$::session_debug}{log local0. "Seconds since last reap: $since"} if { $since >= $::sessreap }{ set ::lastrun $currtime if {$::session_debug}{log local0. "At least one minute has passed. Reaping Session Array"} foreach sesskey [array names ::sessionar] { #if {$::session_debug}{log local0. "SessionID: $sesskey"} if {$::session_debug}{log local0. "Value of $sesskey: $::sessionar($sesskey)"} set lastconn $::sessionar($sesskey) set elapsedtime [expr {$currtime - $lastconn}] if { $elapsedtime > $::timeout }{ unset ::sessionar($sesskey) if { $::session_debug }{ log local0. "Session: $sesskey exceeded timeout. Removed from session table."} } } } #Since the array contains unique sessions the following variable provides for an accurate count #of the current sessions. The "array size" command gives use the amount of elements within the array #in the form of an integer set currsess [array size ::sessionar] if {$::session_debug}{log local0. "Current Sessions: $currsess"} #Here we check that the HTTP URI starts with "/licensemgmt" as this rule only pertains to #the license management application if { [HTTP::uri] starts_with "/licensemgmt" } { if { $::session_debug }{ log local0. "URL received: [HTTP::uri]"} #reap away session on logout if { [HTTP::uri] contains "/invalidateSession.lic" } { if {$::session_debug}{log local0. "sessions before reaping: $currsess"} set sesscookie [URI::query [HTTP::uri] "ID"] unset ::sessionar($sesscookie) if { $::session_debug }{ log local0. "session reaped away due to logout: $sesscookie"} set currsess_new [array size ::sessionar] if {$::session_debug}{log local0. "sessions after reaping: $currsess_new"} } #Check for the existence of the ObSSOCookie and extract the unique value of said cookie if { [HTTP::cookie exists "JSESSIONID"] }{ if { $::session_debug }{ log local0. "has cookie..."} set sesscookie [HTTP::cookie "JSESSIONID"] if { $::session_debug }{ log local0. "Value of JSESSIONID: $sesscookie"} #Check whether this cookie's value is contained as an element of the array. If it is #the iRule updates the last accessed time which is the data of each element. This is #in clock seconds. If it doesn't exist we treat is as a new session. This involves a check #of whether the threshold has been reach and if so a redirect, otherwise we add the unique #id to the array with its time in clock seconds if { [info exists ::sessionar($sesscookie)] } { if { $::session_debug }{ log local0. "Session Already Exists"} set ::sessionar($sesscookie) $currtime return } else { if { $currsess >= $::limit }{ #if {$::session_debug}{loglocal0. "Redirected to: [HTTP::header "Host"]$::busy_redirect"} #HTTP::busy_redirect [HTTP::header "Host"]$::busy_redirect HTTP::busy_redirect $::busy_redirect if {$::session_debug}{log local0. "Over Threshold and not an existing session"} if {$::session_debug}{log local0. "List of Sessions:"} foreach sesslist [array names ::sessionar] { if {$::session_debug}{log local0. "[IP::client_addr]: $sesslist"} } STATS::incr throttle "Rejected Sessions" } else { set ::sessionar($sesscookie) $currtime STATS::incr throttle "Allowed Sessions" return } } #If the client didn't have the JSESSIONID than we treat it as a new client and only allow through #if the threshold has not been met. } else { STATS::incr throttle "Total Sessions" if { $currsess <= $::limit }{ STATS::incr "throttle" "Allowed Sessions" } else { if { $::session_debug }{ log local0. "[IP::client_addr] was denied. Over Threshold" } HTTP::busy_redirect $::busy_redirect STATS::incr "throttle" "Rejected Sessions" } } } }
Published Mar 18, 2015
Version 1.0David_Homoney
Nimbostratus
Joined November 10, 2004
David_Homoney
Nimbostratus
Joined November 10, 2004
No CommentsBe the first to comment