Forum Discussion
Irule advice?
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!
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 } } }
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" } } }
- Kevin_StewartEmployee
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 } } }
- JD_TomzakCirrus
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_StewartEmployee
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" } } }
- JD_TomzakCirrus
Works perfect on port 80. Https not so much...thoughts? Thanks,
- Kevin_StewartEmployee
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?
- JD_TomzakCirrus
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,
- Kevin_StewartEmployee
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
- Kevin_StewartEmployee
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" } } }
- Kevin_StewartEmployee
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" } } }
- JD_TomzakCirrus
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...
- Kevin_StewartEmployee
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).
- JD_TomzakCirrus
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
- Kevin_StewartEmployee
Just replace the log statement with whatever you want.
- Reject/drop
- HTTP response
- JD_TomzakCirrus
Reject works fine. Sorry I wasn't clear...I want the log entry and the reject.
Thanks,
- Kevin_StewartEmployee
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.
- JD_TomzakCirrus
I am decrypting on that VIP. We also, re-encrypt on the wat to the poll members.
- JD_TomzakCirrus
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_TomzakCirrus
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_StewartEmployee
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" } } } } }
- JD_TomzakCirrus
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?
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