Forum Discussion
Am I being overly repetitive?
Hey folks,
I've been asked to put together an iRule to support a multi-tenancy initiative. This iRule is intended to sit on a public facing VS which can handle connections for multiple customers which are separated by the subdomain of a wildcard FQDN.
The premise of the rule is that we should check that it's a valid customer and if they are, then make sure the request is being sourced from a permitted IP, and finally select an appropriate pool for the requested URI. The permitted IPs are customer specific as are the pools.
The rule should also requires no logic changes as customers, IPs and pool mappings are added/removed/changed over time.
Finally if any condition fails or there is something missing (e.g. a pool, or DGL etc.) then the connection must be closed and an appropriate log made.
I've messed with iRules for a number of years but I'm not a particularly clever coder. I get by as needed by scraping things together from DevCentral and other places. This is by far the longest rule I've stitched together (below) which seems to work okay with the testing I've done so far.
So I'm not really asking for anyone to check the rule in terms of its functionality (but any pointers on that front welcome), however I just can't shake the feeling that I'm being overly repetitive with the deny/error handling parts of the code. Any suggestions here would be appreciated.
Thanks in advance.
Leo
when RULE_INIT {
Set environment domain
set static::DOMAIN_ID ".mydomain"
Set debug logging on/off (0 for none, 1 for deny/error and 2 for all logging)
set static::DEBUG 2
}
when HTTP_REQUEST {
Check if our customer exists
if { [class match [string tolower [HTTP::host]] equals CUSTOMER_DGL] }{
Use the first string that comes before the environment domain to set our customer ID variable
set CUSTOMER_ID [getfield [HTTP::host] $static::DOMAIN_ID 1]
Debug Logging
if {$static::DEBUG==2}{log local0. "[virtual name] - Allow - Source IP [IP::client_addr] - The requested subdomain customer ID is:$CUSTOMER_ID"}
Our customer doesn't exist
} else {
Issue 403 response
HTTP::respond 403 -version auto content "Forbidden" noserver
Gracefully close the connection here
TCP::close
Debug Logging
if {$static::DEBUG>=1}{log local0. "[virtual name] - Deny - Source IP [IP::client_addr] - Blocked accessing HTTP host:[HTTP::host] - client does not exist"}
Stop processing iRule event
return
}
Set a variable to check for the customer ID resource DGL
append CUSTOMER_CLASS [string toupper $CUSTOMER_ID] "_RESOURCE_DGL"
Set a variable to check for the customer ID allowed source IP
append WHITELIST_CLASS [string toupper $CUSTOMER_ID] "_WHITELIST_DGL"
Determine if the whitelist DGL exists
if { [class exists $WHITELIST_CLASS] }{
Check if connection is from an allowed Source IP
if { [class match [IP::client_addr] equals $WHITELIST_CLASS] } {
Debug Logging
if {$static::DEBUG==2}{log local0. "[virtual name] - Allow - Source IP [IP::client_addr] - Is a permitted IP for customer $CUSTOMER_ID"}
Determine if the resource DGL exists
if { [class exists $CUSTOMER_CLASS] }{
If the resource DGL exists, check if we have a valid resource pool
if { [class match [string tolower [HTTP::uri]] starts_with $CUSTOMER_CLASS] } {
Set our pool selection variable
set POOLSELECTION [class match -value [string tolower [HTTP::uri]] starts_with $CUSTOMER_CLASS]
A valid resource exists but the pool it references doesn't exist
if [ catch { pool $POOLSELECTION } ] {
Issue 403 response
HTTP::respond 403 -version auto content "Forbidden" noserver
Gracefully close the connection here
TCP::close
Debug Logging
if {$static::DEBUG>=1}{log local0. "[virtual name] - Error - Source IP [IP::client_addr] - A pool named $POOLSELECTION doesn't exist for customer ID $CUSTOMER_ID and the resource [HTTP::uri]"}
Stop processing iRule event
return
The pool exists
} else {
Debug Logging
if {$static::DEBUG==2}{log local0. "[virtual name] - Allow - Source IP [IP::client_addr] - The pool selected was [LB::server]"}
}
A valid resource and it's associated pool doesn't exist
} else {
Issue 403 response
HTTP::respond 403 -version auto content "Forbidden" noserver
Gracefully close the connection here
TCP::close
Debug Logging
if {$static::DEBUG>=1}{log local0. "[virtual name] - Deny - Source IP [IP::client_addr] - No pool was found for customer ID $CUSTOMER_ID and the resource [HTTP::uri]"}
Stop processing iRule event
return
}
The resource DGL doesn't exist
} else {
Issue 403 response
HTTP::respond 403 -version auto content "Forbidden" noserver
Gracefully close the connection here
TCP::close
Debug Logging
if {$static::DEBUG>=1}{log local0. "[virtual name] - Error - Source IP [IP::client_addr] - A DGL named $CUSTOMER_CLASS does not exist"}
Stop processing iRule event
return
}
Request is not coming from an allowed source IP
} else {
Issue 403 response
HTTP::respond 403 -version auto content "Forbidden" noserver
Gracefully close the connection here
TCP::close
Debug Logging
if {$static::DEBUG>=1}{log local0. "[virtual name] - Deny - Source IP [IP::client_addr] - This IP isn't permitted for customer $CUSTOMER_ID"}
Stop processing iRule event
return
}
The whitelist DGL doesn't exist
} else {
Issue 403 response
HTTP::respond 403 -version auto content "Forbidden" noserver
Gracefully close the connection here
TCP::close
Debug Logging
if {$static::DEBUG>=1}{log local0. "[virtual name] - Error - Source IP [IP::client_addr] - A DGL named $WHITELIST_CLASS does not exist"}
Stop processing iRule event
return
}
}3 Replies
Hi Leo,
TCL pretty much loves repetitive code! Its way faster than using [proc] (aka. procedures), [eval] (aka. TCL code macros), or storing results in variable and let the later error handle perform the desired action.
But unfortunately Humans don't like repetitive code. Its somewhat hard to maintain if you need to change it.
But your code has some potential to avoid the repetive code while keeping an identical performance. It trick would be to change the default action of your iRule to send the "Forbidden" response and [TCP::close] the connection and just return the good actions. See the code below for an example how this could be done...
Note: I guess the iRule below should be fine. But I haven't tested the iRule at all^^
when RULE_INIT { Set environment domain set static::DOMAIN_ID ".mydomain" Set debug logging on/off (0 for none, 1 for deny/error and 2 for all logging) set static::DEBUG 2 } when HTTP_REQUEST { Check if our customer exists if { [class match [string tolower [HTTP::host]] equals CUSTOMER_DGL] }{ Use the first string that comes before the environment domain to set our customer ID variable set CUSTOMER_ID [getfield [HTTP::host] $static::DOMAIN_ID 1] Debug Logging if {$static::DEBUG==2}{log local0. "[virtual name] - Allow - Source IP [IP::client_addr] - The requested subdomain customer ID is:$CUSTOMER_ID"} Set a variable to check for the customer ID resource DGL append CUSTOMER_CLASS [string toupper $CUSTOMER_ID] "_RESOURCE_DGL" Set a variable to check for the customer ID allowed source IP append WHITELIST_CLASS [string toupper $CUSTOMER_ID] "_WHITELIST_DGL" Determine if the whitelist DGL exists if { [class exists $WHITELIST_CLASS] }{ Check if connection is from an allowed Source IP if { [class match [IP::client_addr] equals $WHITELIST_CLASS] } { Debug Logging if {$static::DEBUG==2}{log local0. "[virtual name] - Allow - Source IP [IP::client_addr] - Is a permitted IP for customer $CUSTOMER_ID"} Determine if the resource DGL exists if { [class exists $CUSTOMER_CLASS] }{ If the resource DGL exists, check if we have a valid resource pool if { [class match [string tolower [HTTP::uri]] starts_with $CUSTOMER_CLASS] } { Set our pool selection variable set POOLSELECTION [class match -value [string tolower [HTTP::uri]] starts_with $CUSTOMER_CLASS] A valid resource exists but the pool it references doesn't exist if [ catch { pool $POOLSELECTION } ] { Debug Logging if {$static::DEBUG>=1}{log local0. "[virtual name] - Error - Source IP [IP::client_addr] - A pool named $POOLSELECTION doesn't exist for customer ID $CUSTOMER_ID and the resource [HTTP::uri]"} The pool exists } else { Debug Logging if {$static::DEBUG==2}{log local0. "[virtual name] - Allow - Source IP [IP::client_addr] - The pool selected was [LB::server]"} Stop processing iRule event return } A valid resource and it's associated pool doesn't exist } else { Debug Logging if {$static::DEBUG>=1}{log local0. "[virtual name] - Deny - Source IP [IP::client_addr] - No pool was found for customer ID $CUSTOMER_ID and the resource [HTTP::uri]"} } The resource DGL doesn't exist } else { Debug Logging if {$static::DEBUG>=1}{log local0. "[virtual name] - Error - Source IP [IP::client_addr] - A DGL named $CUSTOMER_CLASS does not exist"} } Request is not coming from an allowed source IP } else { Debug Logging if {$static::DEBUG>=1}{log local0. "[virtual name] - Deny - Source IP [IP::client_addr] - This IP isn't permitted for customer $CUSTOMER_ID"} } The whitelist DGL doesn't exist } else { Debug Logging if {$static::DEBUG>=1}{log local0. "[virtual name] - Error - Source IP [IP::client_addr] - A DGL named $WHITELIST_CLASS does not exist"} } Our customer doesn't exist } else { Debug Logging if {$static::DEBUG>=1}{log local0. "[virtual name] - Deny - Source IP [IP::client_addr] - Blocked accessing HTTP host:[HTTP::host] - client does not exist"} } Issue 403 response HTTP::respond 403 -version auto content "Forbidden" noserver Gracefully close the connection here TCP::close }Cheers, Kai
- Packeteer_69831
Nimbostratus
Hey Kai,
Thanks for the response and suggested alternative. This has helped me see how a simple logic change can help the code flow more nicely. No doubt it will work a charm.
Thanks again!!!
Cheers. Leo.
- You're welcome! ;-)
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