15-Sep-2022 07:20
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!
Solved! Go to Solution.
15-Sep-2022 09:58
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
}
}
}
19-Sep-2022 12:20
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"
}
}
}
15-Sep-2022 07:58
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"
}
}
}
15-Sep-2022 08:56
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
15-Sep-2022 08:58
Just replace the log statement with whatever you want.
15-Sep-2022 13:33
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...
15-Sep-2022 13:53
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).
15-Sep-2022 14:18
Now I see what I'm missing. Need better understanding of what makes up an HTTP request. Off to the youtubes then...
Thanks,
15-Sep-2022 09:27
Reject works fine. Sorry I wasn't clear...I want the log entry and the reject.
Thanks,
15-Sep-2022 09:58
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
}
}
}
16-Sep-2022 09:00
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,
16-Sep-2022 09:09
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"
}
}
}
16-Sep-2022 09:52
Works perfect on port 80. Https not so much...thoughts? Thanks,
16-Sep-2022 09:58
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.
16-Sep-2022 10:21
I am decrypting on that VIP. We also, re-encrypt on the wat to the poll members.
16-Sep-2022 10:28
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?
16-Sep-2022 10:51
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,
16-Sep-2022 11:07
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
16-Sep-2022 11:13
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
19-Sep-2022 08:46
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
19-Sep-2022 09:24
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,
19-Sep-2022 09:45
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"
}
}
}
}
}
19-Sep-2022 12:09
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?
19-Sep-2022 12:20
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"
}
}
}
19-Sep-2022 13:55
Thanks again