Hey ChatGPT, can you write iRules?
Find the most requested iRules
- Redirect HTTP to HTTPS.
- Rewrite the Host header in the request and response.
- Do a loadbalancing decission based on the Host header.
- Do a loadbalancing decission based on the URI.
- Different ASM blocking pages based on the Host header.
- A recent example: https://community.f5.com/t5/technical-forum/need-help-for-irule/m-p/310444
- Another recent example: https://community.f5.com/t5/technical-forum/irule-redirect-irule-help-required/m-p/304943
Challenge 1: Redirect HTTP to HTTPS
Forwarding HTTP requests to HTTPS is a feature that every load balancer can handle and that is often asked for in the forum. The BIG-IP can meet this requirement with both iRules and Local Traffic Policies. Each BIG-IP comes with a collection of pre-installed iRules, an iRule that redirects HTTP to HTTPS is also included (IMHO not a great example, because it sends a 302 Found redirect response that indicates that the resource requested has been temporarily moved to a different URL, I prefer the 301 Moved Permanently redirect status).
The iRule ChatGPT suggests as a solution is the following:
when HTTP_REQUEST {
if { [HTTP::header exists "X-Forwarded-Proto"] && [HTTP::header value "X-Forwarded-Proto"] eq "http" } {
HTTP::respond 301 Location "https://[HTTP::host][HTTP::uri]"
}
}
This iRule executes on the HTTP_REQUEST event and checks for the presence of an X-Forwarded-Proto header in the request. Such header would only be inserted from another upstream device in front of the BIG-IP, for example another loadbalancer or a CDN. I like that it uses HTTP status 301 (Moved Permanently) instead of HTTP::redirect. However, I would add the options version auto and noserver.
Challenge 2: Rewrite the Host header in the request and response
A common use case for this functionality is when your application servers only accept specific Host headers, but you are publishing your application with a differen FQDN. You want to rewrite the Host header for incoming requests and the Location header for responses, so that the application continues to work with the public FQDN.
ChatGPTs answer:
when HTTP_REQUEST {
HTTP::header replace "Host" "newhost.example.com"
}
when HTTP_RESPONSE {
if { [HTTP::header exists "Location"] } {
HTTP::header replace "Location" [string map {"oldhost.example.com" "newhost.example.com"} [HTTP::header value "Location"]]
}
}
No remarks here. Challenge completed.
Challenge 3: Loadbalancing based on the Host header
This function is commonly referred to as Layer 7 loadbalancing or content switching. Behind a VIP there are different web applications, the loadbalancing decision is made either on the basis of the host name or the path portion of the URL. The latter is Challenge 4.
when HTTP_REQUEST {
switch -glob [HTTP::host] {
"www.example.com" {
pool my_pool1
}
"api.example.com" {
pool my_pool2
}
default {
pool my_default_pool
}
}
}
No remarks here either.
Challenge 4: Loadbalancing based on the URI
when HTTP_REQUEST {
if { [HTTP::uri] starts_with "/app1/" } {
pool my_pool1
}
elseif { [HTTP::uri] starts_with "/app2/" } {
pool my_pool2
}
else {
pool my_default_pool
}
}
Looking at the solution for example 4, I'd say that you should know, from experience, that a switch statement will be more performant than an if/else clause at a certain scale. If you are just having 3 or 4 alternatives, the performance gain from using a switch statement won't be significant. However, I asked ChatGPT to rewrite the same iRule using datagroups to map URLs to pools. This way the solution could easily scale. It did so and even gave a tmsh example to create a datagroup and populate some sample data.
tmsh create ltm data-group internal my_datagroup type string
tmsh modify ltm data-group my_datagroup records add { /path1 pool1 /path2 pool2 /path3 pool3 }
Challenge 5: Different ASM blocking pages based on the Host header.
Sometimes you want to show a custom blocking page, for example to add extra information like the URL or the client IP. Or to add some corporate identity to your blocking page.
when ASM_REQUEST_DONE {
if { [ASM::blocking_active] } {
switch -glob [HTTP::host] {
"www.example.com" {
HTTP::respond 403 content "<html><body><h1>Access to this site is blocked!</h1></body></html>"
}
"www.example2.com" {
HTTP::respond 403 content "<html><body><h1>Sorry, this site is not available!</h1></body></html>"
}
default {
HTTP::respond 403 content "<html><body><h1>Access to this site is blocked!</h1></body></html>"
}
}
}
}
I have a few comments on this proposed solution. The command [ASM::blocking_active] does not exist. You could probably fix the iRule by using [ASM::status] equals "blocked" or when ASM_REQUEST_BLOCKING instead. Besides that, the usual HTTP response code ASM uses to send a blocking page is 200, also this solution does not include the support ID. I recommend taking a look at Support Solution K22017023 and combining the best of solutions. https://my.f5.com/manage/s/article/K22017023
I have of course asked ChatGPT to adjust the iRule to take my suggestions into account. The new iRule that ChatGPT proposed was even worse.
when ASM_REQUEST_BLOCKED {
set block_type [ASM::block type]
set host_header [HTTP::header Host]
switch -glob $host_header {
"www.example.com" {
ASM::respond $block_type "<html><body><h1>Access blocked by ASM</h1><p>Your request has been blocked by ASM. Please contact support with the following ID:</p><p>ASM Block ID: [ASM::block id]</p></body></html>"
}
"www.anotherexample.com" {
ASM::respond $block_type "<html><body><h1>Access blocked by ASM</h1><p>Your request has been blocked by ASM. Please contact support with the following ID:</p><p>ASM Block ID: [ASM::block id]</p></body></html>"
}
default {
ASM::respond $block_type "<html><body><h1>Access blocked by ASM</h1><p>Your request has been blocked by ASM. Please contact support with the following ID:</p><p>ASM Block ID: [ASM::block id]</p></body></html>"
}
}
}
This iRule is supposed to execute on the ASM_REQUEST_BLOCKED event, which doesn't exist. Also the commands [ASM::respond] and the [ASM::block] don't exist. I think the AI got a little bit too creative here 🙂
Challenge 6: Redirect HTTP request matching a specific path and add a query parameter
The author of this thread asks for an iRule that will redirect the user to a new URL and uses the subdomain portion of the FQDN a query parameter in the new URI. The thread is not marked as "solved", so I cannot say whether this satisfies the requirements of the user. For the sake of brevity I removed all comments that ChatGTP added. It is worth mentioning, that the comments ChatGPT adds are indeed helpful.
Here is what ChatGPT suggests.
when HTTP_REQUEST {
if {[string starts_with [HTTP::path] "/age/signup"]} {
set xyz_value [string range [HTTP::host] 0 [expr {[regexp -indices "\\.test\\.com" [HTTP::host]]-1}]]
set redirect_url "https://abc.test.com/#/inSign/invite?account=$xyz_value&[HTTP::query]"
HTTP::redirect $redirect_url 302
}
}
Here is again a lot that goes wrong. The if condition if {[string starts_with [HTTP::path] "/age/signup"]} is just wrong syntax. Correct syntax would be if {[HTTP::path] starts_with "/age/signup"}. Also the next line set xyz_value [string range [HTTP::host] 0 [expr {[regexp -indices "\\.test\\.com" [HTTP::host]]-1}]] won't do what you'd expect it to do. If the FQDN is aaa.test.com this won't extract the full subdomain portion of the FQDN, it will just take the first letter. And the [HTTP::redirect] command does not accept 302 as a flag. Furthermore, what might be missing here, is a check for the value of [HTTP::host] to ensure both conditions of matching the hostname and the URL path are matched. I already mentioned, I am not a fan of the HTTP::redirect command because it uses a 302 Found redirect response rather than a 301 Moved Permanently response. But what I like about this
Here is my working solution.
when HTTP_REQUEST {
if {[string tolower [HTTP::path]] starts_with "/age/signup"} {
HTTP::redirect "https://abc.test.com/#/inSign/invite?account=[getfield [HTTP::host] ".test.com" 1]&[HTTP::query]"
}
}
Notice that I use string tolower here. I often use it to make sure my iRule can compare strings case-insensitive.
Challenge 7: Redirect users from one URL to a new URL
Here the is quite similar, the author of this thread asks for an iRule that redirects from https://abc.xyz.com/Projectsite/* to https://bbb.xyz.com/page/projectdocs-users/.
when HTTP_REQUEST {
if {[string starts_with [HTTP::path] "/Projectsite/"]} {
set redirect_url "https://bbb.xyz.com/page/projectdocs-users/"
append redirect_url [string range [HTTP::path] 11 end]
HTTP::redirect $redirect_url 302
}
}
ChatGPT makes the same syntax mistake here, the if condition will cause an error when saving the iRule. if {[string starts_with [HTTP::path] "/age/signup"]} is not valid tcl code, correct is if {[HTTP::path] starts_with "/Projectsite/"}. The string range is off by two characters. In order to remove the string "/Projectsite/" from the HTTP::path you need to count 13 characters, not 11. I wonder how an AI can be bad at counting... Also here second check for matching the hostname as well as the URL Path would be useful, the use a variable for the redirect URL seems unnecessary and the HTTP::redirect command... enough said about that.
Here is my quick and dirty solution that will work.
when HTTP_REQUEST {
if {[HTTP::path] starts_with "/Projectsite/"} {
HTTP::redirect "http://bbb.xyz.com/page/projectdocs-users/[string range [HTTP::path] 13 end]&[HTTP::query]"
}
}
Conclusion
After ChatGPT had solved the first tasks I it quite convincingly, I was quite optimistic that it could also correctly answer the more complex tasks or questions from the devcentral forum. Unfortunately this was not the case, the AI was quite creative in finding solutions but often with wrong syntax and with wasteful use of variables. To correct the syntax errors, you need an F5 specialist again. He will then also have enough experience to develop iRules not only correctly but also in a performant and scalable way. Of course we can expect that the AI will improve over time and hopefully it will soon be able to solve more complex iRule questions for us - I will for sure test it again.
Update
JRahm hinted me to take a another look at the second solution ChatGPT provided. The AI made another mistake here, it reversed the order of the string map. Apologies, I should have noticed that.
ChatGPT:
[string map {"oldhost.example.com" "newhost.example.com"} [HTTP::header value "Location"]
Correct:
[string map {"newhost.example.com" "oldhost.example.com"} [HTTP::header value "Location"]
This is cool to read about, especially with AI being a hot topic right now. Thanks for doing this test and sharing with the community!
THIS article, is IMO, included in the definition of community. Helping others to clarify, utilize, and understand the power (and dangers) of tools in the shop. ChatGPT and other Generative AI is certainly one of those tools.
Well done Daniel_Wolf
In one of my hobbies, woodworking, any person with access to the power button can start up a tablesaw and run wood through...but only someone with experience or expertise (tend to go hand in hand) can describe the conditions that cause kickback and potentially save life and limb.- JRahmAdmin
Cool stuff, Daniel_Wolf!!
I've been playing around as well, and I like this as a startoff point, but find nits in each result that I then refine. For example, on your challenge 3 I'd make sure to prevent injection by adding -- to the command, and since there is no glob style matching in the example, i'd remove the -glob flag. But if I was going to do matching, I'd make sure to set the nomenclature of my pools to match the host in some way so I can make that iRule extremely simple.
One catch though...shouldn't the string map in Challenge 2 be reversed? if the server is newhost, then that needs to be mapped back to oldhost, right? I think in a couple runs I made on other stuff it gets the string map backwards as well. Something to watch for...
JRahm - you might be right with challenge 3. Since the solution ChatGPT provided was not obvious rubbish, I didn't do a test run with this answer. Will put it to a test and do an update if required.
Adding the -- for preventing command injection is a valid point. As LiefZimmerman said, this will save you a limb and it seperates the experts from the AI 🙂
Anyone who is interested in the details of command injection should read: K15650046: Tcl code injection security exposure and/or read the Whitepaper from Blackhat 2019.Thanks for pointing both out!
- Tamer-ZainCirrus
cool article
- PSilvaRet. Employee
This is awesome! Thanks Daniel_Wolf !!
- xuwenCumulonimbus
if ChatGPT can not access internet,will it can pass F5 certification exams?
f5-101,f5-201,f5-301,f5-401
- buulamAdmin
That's interesting that it started to make up non-existent iRule events. If you were to use this as a tool for a total newbie to learn, that would really throw you off!
- Jerry666YNimbostratus
thanks for sharing