cancel
Showing results for 
Search instead for 
Did you mean: 

Irule advice?

JD_Tomzak
Cirrus
Cirrus

Hello,

I'm seeking advice on using an Irule to drop a connection when a certain condition is met in the URI. fid= followed by non numeric charectors. fid=1234 would pass. fid=13d4 would drop. Thanks!

2 ACCEPTED SOLUTIONS

Kevin_Stewart
F5 Employee
F5 Employee
when HTTP_REQUEST {
    if { [string tolower [HTTP::query]] contains "fld" } {
        if { ![string is digit [URI::query [HTTP::uri] "fld"]] } {
            log local0. "invalid fld value, rejecting from [IP::client_addr]"
            reject
        }
    }
}

View solution in original post

Kevin_Stewart
F5 Employee
F5 Employee

The following accounts for a POST request where the payload is URL encoded or XML:

when HTTP_REQUEST { 
    if { [HTTP::method] eq "POST" } { 
        ## Trigger collection for up to 1MB of data 
        if { [HTTP::header exists "Content-Length"] && [HTTP::header "Content-Length"] <= 1048576 }{ 
            set content_length [HTTP::header "Content-Length"] 
        } else { 
            set content_length 1048576 
        } 
        ## Check if $content_length is not set to 0 
        if { $content_length > 0 } { 
            HTTP::collect $content_length 
        } 
    } 
} 
when HTTP_REQUEST_DATA { 
    set fld ""
    if { [HTTP::payload] contains "fld=" } {
        foreach x [split [HTTP::payload] "&"] {
            if { $x starts_with "fld=" } {
                set fld [lindex [split $x "="] 1]
                continue
            }
        }
    } elseif { [HTTP::payload] contains "<fld>" } {
        set fld [findstr [HTTP::payload] "<fld>" 5 "</fld>"]
    }
    if { $fld ne "" } {
        if { ![string is digit $fld] } {
            log local0. "invalid fld value, rejecting from [IP::client_addr]"
            HTTP::respond 400 content "Bad Request" "Content-Type" "text/html" "Connection" "close"
        }
    }
}

View solution in original post

23 REPLIES 23

Kevin_Stewart
F5 Employee
F5 Employee

Try this:

when HTTP_REQUEST {
    if { [string tolower [HTTP::query]] contains "fld" } {
        if { ![string is digit [URI::query [HTTP::uri] "fld"]] } {
            log local0. "invalid char"
        }
    }
}

That works well! Thanks

So the idea is to drop these connections and not send to the pool. Maybe send a 403 too. Thoughts?

-JD

Just replace the log statement with whatever you want.

  • Reject/drop
  • HTTP response

Thanks for all of the help Kevin!

One last thought, any idea why the = is not in play? (fld=) Ignored? Also, how would this work if the digits were in front of fld instead of behind it? (12345=fld) 

Just asking as I aim to get a better understanding of Irules in each of these engagements. Non programer, network guy...

 

You're using URI::query and HTTP::query to get to the querystring values in an HTTP request. Example:

https://www.example.com/foo?fld=1234&bar=blah&this=that

The querystring is fld=1234&bar=blah&this=that. The above two commands conveniently let you pull apart the key-value pairs. I don't think there'd ever be a situation where you'd see 1234=fld, unless 1234 was the key. HTTP requires a specific format for querystrings where key=value (never value=key).

Now I see what I'm missing. Need better understanding of what makes up an HTTP request. Off to the youtubes then...

Thanks,

JD_Tomzak
Cirrus
Cirrus

Reject works fine. Sorry I wasn't clear...I want the log entry and the reject.

Thanks,

 

Kevin_Stewart
F5 Employee
F5 Employee
when HTTP_REQUEST {
    if { [string tolower [HTTP::query]] contains "fld" } {
        if { ![string is digit [URI::query [HTTP::uri] "fld"]] } {
            log local0. "invalid fld value, rejecting from [IP::client_addr]"
            reject
        }
    }
}

Hi,

 

Sniffing the connection today, I see we send a rst-ack when the reject occurs. Possibe to respond with a 400 something and some verbage regarding the ivalid request data? Thanks,

Kevin_Stewart
F5 Employee
F5 Employee
when HTTP_REQUEST {
    if { [string tolower [HTTP::query]] contains "fld" } {
        if { ![string is digit [URI::query [HTTP::uri] "fld"]] } {
            log local0. "invalid fld value, rejecting from [IP::client_addr]"
            HTTP::respond 400 content "Bad Request" "Content-Type" "text/html" "Connection" "close"
        }
    }
}

Works perfect on port 80. Https not so much...thoughts?  Thanks,

Kevin_Stewart
F5 Employee
F5 Employee

You'd need to decrypt the HTTPS to trigger the HTTP iRule events, and insert HTTP responses. There wouldn't be any way to insert a HTTP 400 response without decrypting.

I am decrypting on that VIP.  We also, re-encrypt on the wat to the poll members. 

Kevin_Stewart
F5 Employee
F5 Employee

Interesting. That iRule should work as long it's HTTP or you're decrypting HTTPS. Does the iRule event fire at all?

when HTTP_REQUEST {
    log local0. "here: [HTTP::uri]"
    if { [string tolower [HTTP::query]] contains "fld" } {
        if { ![string is digit [URI::query [HTTP::uri] "fld"]] } {
            log local0. "invalid fld value, rejecting from [IP::client_addr]"
            HTTP::respond 400 content "Bad Request" "Content-Type" "text/html" "Connection" "close"
        }
    }
}

Do you get any odd messages in /var/log/ltm?

Yes, the Irule,fires, logs, etc. 

When we do this on port 80, I see our error mesage and an orderly connection shutdown. 

When https:, no message is returned and the connection ends with just us sending the rst-ack.

 

Thanks,

Just tested and it appears to work fine for me with an HTTPS VIP (decrypting and re-encrypting).

If you tail the LTM log do you see anything unusual?

tail -f /var/log/ltm

Nope, nothing there. Odd situation...

Thanks for all help anyway, will get it figured out next week.  I'm about to get started on the weekend. Have a good one!

-JD

JD_Tomzak
Cirrus
Cirrus

So I solved the issue first thing this morning. I had left an expermintal Irule in the https version of the VIP that was impacting the test. Thanks again so much for your help on this. That revelation about Irule sytax follwoing HTTP request structure really cleared up a lot of confusion for me and I will continue to study this topic. -JD 

JD_Tomzak
Cirrus
Cirrus

So, still one issue. Can we make this work for POST as well. In this case the FID= is in the body, (x-www-for-urlencoded)

Thanks,

Kevin_Stewart
F5 Employee
F5 Employee

Takes a little more work to get the HTTP payload from a POST request, but logic is mostly the same:

#}
when HTTP_REQUEST { 
    if { [HTTP::method] eq "POST" } { 
        ## Trigger collection for up to 1MB of data 
        if { [HTTP::header exists "Content-Length"] && [HTTP::header "Content-Length"] <= 1048576 }{ 
            set content_length [HTTP::header "Content-Length"] 
        } else { 
            set content_length 1048576 
        } 
        ## Check if $content_length is not set to 0 
        if { $content_length > 0 } { 
            HTTP::collect $content_length 
        } 
    } 
} 
when HTTP_REQUEST_DATA { 
    if { [HTTP::payload] contains "fld=" } {
        foreach x [split [HTTP::payload] "&"] {
            if { $x starts_with "fld=" } {
                if { ![string is digit [lindex [split $x "="] 1]] } {
                    log local0. "invalid fld value, rejecting from [IP::client_addr]"
                    HTTP::respond 400 content "Bad Request" "Content-Type" "text/html" "Connection" "close"
                }
            }
        }
    }
}

Well the good news is most of this was already in place so it is working. Thanks!

 

The only thing missing is when they send it to me in XML sometimes instead of a key/value pair.

Not sure how often that comes up. But its not "fid =" in this case.

its <fid>1234a</fid><pw>test123</pw>

 

Thoughts?

 

 

Kevin_Stewart
F5 Employee
F5 Employee

The following accounts for a POST request where the payload is URL encoded or XML:

when HTTP_REQUEST { 
    if { [HTTP::method] eq "POST" } { 
        ## Trigger collection for up to 1MB of data 
        if { [HTTP::header exists "Content-Length"] && [HTTP::header "Content-Length"] <= 1048576 }{ 
            set content_length [HTTP::header "Content-Length"] 
        } else { 
            set content_length 1048576 
        } 
        ## Check if $content_length is not set to 0 
        if { $content_length > 0 } { 
            HTTP::collect $content_length 
        } 
    } 
} 
when HTTP_REQUEST_DATA { 
    set fld ""
    if { [HTTP::payload] contains "fld=" } {
        foreach x [split [HTTP::payload] "&"] {
            if { $x starts_with "fld=" } {
                set fld [lindex [split $x "="] 1]
                continue
            }
        }
    } elseif { [HTTP::payload] contains "<fld>" } {
        set fld [findstr [HTTP::payload] "<fld>" 5 "</fld>"]
    }
    if { $fld ne "" } {
        if { ![string is digit $fld] } {
            log local0. "invalid fld value, rejecting from [IP::client_addr]"
            HTTP::respond 400 content "Bad Request" "Content-Type" "text/html" "Connection" "close"
        }
    }
}

JD_Tomzak
Cirrus
Cirrus

Thanks again