Forum Discussion

Packeteer_69831's avatar
Packeteer_69831
Icon for Nimbostratus rankNimbostratus
Jan 26, 2016

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

  • 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.