Forum Discussion

alargent_168199's avatar
alargent_168199
Icon for Nimbostratus rankNimbostratus
Feb 12, 2016

iRules not evaluating as expected?

I am hosting many URLs on a single IP address, directing them to the various pools based on URL. I want to block certain HTTP methods to all, but allow PUT to an URL that hosts an API app. So I have stacked the rules, and then I even set a priority, but it is still evaluating the rule that blocks PUT after I direct it to the API pool.

The rules are stacked in this order in the GUI

 

when HTTP_REQUEST priority 400 {
    set methods [list "CONNECT" "DELETE" "OPTIONS" "TRACE" "PROPFIND" ]
    if { not ( [string tolower [HTTP::host]] ends_with "abc.com" ) } {
        reject
    }
    if { [lsearch $methods [HTTP::method]] ne -1 } {
        reject
    }
}

when HTTP_REQUEST priority 425 {
    if { ( [string tolower [HTTP::host]] equals "api.abc.com" ) } {
      log local0. "Request for [HTTP::host] - client is [IP::client_addr] port [TCP::client_port] method [HTTP::method]"
          pool API_pool
          return
    }
}

when HTTP_REQUEST priority 450 {
    set sec_put_methods [list "PUT" ]

    if { [lsearch $sec_put_methods [HTTP::method]] ne -1 } {
        log local0. "Client is [IP::client_addr] port [TCP::client_port] method [HTTP::method] - DENIED"
                reject
    }
}

 

PUT calls to API are still being blocked. Logs look like:

Feb 12 10:32:14 vlb info tmm3[12053]: Rule /Common/xxx : Request for api.abc.com - client is 1.2.3.4 port 60170 method PUT Feb 12 10:32:14 vlb info tmm3[12053]: Rule /Common/xxxx : Client is 1.2.3.4 port 60170 method PUT - DENIED

What am I missing here?

4 Replies

    • alargent_168199's avatar
      alargent_168199
      Icon for Nimbostratus rankNimbostratus
      I *thought* that was the point of the "return" statement. Otherwise "return" seems pointless. I will check out event disable also.
    • Josiah_39459's avatar
      Josiah_39459
      Historic F5 Account
      If you put them all in a single irule, return would work that way. But yours are stacked irules.
  • Hi Alargent,

    the [return] command will just stop the current iRule from further processing, but every iRule with higher priority will still getting executed.

    In my opinion the absolute best thing you could do, is to consosidate your iRule into a single iRule, in this case [return] would be enought to stop everything...

     

    when HTTP_REQUEST priority 500 {
        set methods [list "CONNECT" "DELETE" "OPTIONS" "TRACE" "PROPFIND" ]
        if { not ( [string tolower [HTTP::host]] ends_with "abc.com" ) } {
            reject
            return
        }
        if { [lsearch $methods [HTTP::method]] ne -1 } {
            reject
            return
        }
        if { ( [string tolower [HTTP::host]] equals "api.abc.com" ) } {
            log local0. "Request for [HTTP::host] - client is [IP::client_addr] port [TCP::client_port] method [HTTP::method]"
            pool API_pool
            return
        }
        set sec_put_methods [list "PUT" ]
        if { [lsearch $sec_put_methods [HTTP::method]] ne -1 } {
            log local0. "Client is [IP::client_addr] port [TCP::client_port] method [HTTP::method] - DENIED"
            reject
            return
        }
    }
    

     

    As already outlined by Josiah, [event disable] can be used to stop further iRule processing of the same or even every other iRule event. But this command is a little tricky to use, since it would also stop the event processing on consecutive request using the same TCP connection (aka. HTTP Keep-Alive). So you would most likely issue a [TCP::close], [reject] or [drop] right after, to not bypass your iRule logic on the very next request using the same TCP connection.

    To make an effective use of [event disable] while still supporting Keep-Alive connections, you would need either need a LTM Policy, to issue a [event enable all] on every ariving request (very stable), or use other iRule events on the same HTTP request to re-enable the previously disabled iRule event (somewhat tricky and does still not support every usecase).

    Note: Let me know, if you want to go the LTM Policy route. I'll lookup some old Devcentral posts for you where this method is described... 😉

    The very last option you have is to assign a $variable to inform the later iRule to not execute their contained code. But this would add a certain overhead to your iRule...

     

    when HTTP_REQUEST priority 400 {
        set execute_irule 1
        set methods [list "CONNECT" "DELETE" "OPTIONS" "TRACE" "PROPFIND" ]
        if { not ( [string tolower [HTTP::host]] ends_with "abc.com" ) } {
            set execute_irule 0
            reject
    
        }
        if { [lsearch $methods [HTTP::method]] ne -1 } {
            set execute_irule 0
            reject
        }
    }
    when HTTP_REQUEST priority 425 {
        if { $execute_irule } then {
            if { ( [string tolower [HTTP::host]] equals "api.abc.com" ) } {
                log local0. "Request for [HTTP::host] - client is [IP::client_addr] port [TCP::client_port] method [HTTP::method]"
                pool API_pool
                set execute_irule 0
                return
            }
        }
    }
    when HTTP_REQUEST priority 450 {
        if { $execute_irule } then {
            set sec_put_methods [list "PUT" ]
            if { [lsearch $sec_put_methods [HTTP::method]] ne -1 } {
                log local0. "Client is [IP::client_addr] port [TCP::client_port] method [HTTP::method] - DENIED"
                set execute_irule 0
                reject
            }
        }
    }
    

     

    Cheers, Kai