Another FSE iRules Challenge, Even More Surprising Results

I have an awesome job. I get to play with cool technology, with good people, at an awesome company, and actually don’t get in trouble for doing so. I get to blend writing and talking and blathering on endlessly to anyone that will listen with completely geeking out and diving into the nuts and bolts of things to see what makes things work. This doesn’t suck.

One of the things that doesn’t suck the most is getting to kick on the light bulb for people that haven’t quite gotten their hands around our programmability technologies just yet. F5 is laden with opportunities to get your script on. From iRules to iControl to iCall and TMSH scripting, there is no shortage of opportunities to get down and dirty with some code. That being said, not everyone is up to speed on such things yet and I take particular joy in being able to help them connect the wires, get the first flickers of that “Holy crap this stuff is cool!” halogen, and then go on their merry.

Lucky as I am, what with the awesome job and all, I get many opportunities for just such interactions. More seasoned readers may be familiar with the one on which today’s post is focused, the FSE challenge. I haven’t posted one of these in a little bit so let’s have a refresher from days of yore.

 

First off, What is an FSE?

An FSE is a Field Sales Engineer. FSEs are the engineering lifeblood of the sales force here at F5.  They’re the ones out in the trenches dealing with customer requirements and issues, building real world solutions, and generally doing all the cool stuff that I get to talk about theoretically, but in the real world.  I’ve got mad respect for those FSEs that take their jobs seriously and learn how to build full fledged F5 solutions that leverage our crazy broad product set and, you guessed it, our out of the box tools like iControl and iRules.  Those that choose to flex those muscles garner a special place in my encrypted little heart.

 

Next, What is with this challenge business?

Every time we get a new batch of FSEs in at corporate for brainwashing err, training we put them through what we lovingly refer to as a boot camp. This is, as you might expect given the name, a rapid way of getting folks up to speed on not only F5 technology but all of the surrounding whats-its and know-how that is expected of someone out in the field slinging our tech. This invariably includes a delve into iRules. There is formal training, of course, but the challenge is a different beast all together. I effectively pose as a customer in the field with a complex (at least complex by beginners’ standards) problem that needs solving. I present it to the batch of keyboard jockeys, give them time to ask questions, take notes, etc., then cut them loose. In their “free time” (See: sleepless hours well into the night) they get to hash out the solution to the problem. They are expected to write, test, and lightly document an iRules solution to eradicate the posed problem point by point. Points are awarded for effectiveness, efficiency, and exportability, meaning ease of use and hand-off. I come back in a week later, after pouring over the proffered code snippets, and announce the winners (top3) based on said criteria, who are then awarded fabulous cash and prizes! (Bold + italics means it must be true, right? Even when it’s not. Since it’s not. At all.)

 

Lastly, What was the challenge?

People are always curious to hear what the actual challenge was when looking at the submissions, so here you go:

Scenario:

A client has an https based application that is undergoing upgrades and large changes, and they need to create business logic in the network layer to allow for a smooth transition and consistent user experience.

 

Desired Solution:

  1. Ensure that all requests from the client to the BIG-IP are SSL encrypted
  2. Ensure all traffic to the back end is plain-text
  3. For all canonical names of domain.com (I.E. bob.domain.com, app1.domain.com, etc.) remove the canonical name and prepend to URI (I.E. bob.domain.com/my/app becomes domain.com/bob/my/app).
  4. Standard canonicals are excluded from this re-writing (mail, smtp, www, ns, ns1)
  5. These host/uri changes must happen transparently to the clients accessing the application.
  6. Anyone accessing the application from the internal network (10.1.*) with an appropriate auth cookie (Name: X-Int-Auth. Value=True) bypasses the above logic and accesses the old structure.
  7. Log any request (IP of client and URI requested) to a canonical name x.domain.com that is non standard (mail, smtp, www, ns, ns1) so that data can be collected as to when users have fully transitioned.

 

 

So now that the table is set, on with the feast!

 

This time around I had another killer host of entries into the hopper. There were people of all experience levels from “newbie, never coded, what does this double equals thing do?” levels to one heck of a ringer, who would make himself known eventually, even though he flew under the radar at first, sly dog that he is. Out of the raft of valiant attempts and solid efforts, my arduous duty was to narrow it down to the top three and announce them to the group, and later (now) the world. Such was my task, and such was performed. I bring you this quarter’s FSE iRules Challenge winners:

 

3rd Place – Benn Alp

Complete with ASCII art, Benn put in an heroic effort on this submission. He ended up with a heck of a lot of code, and most of it was extremely valid, which just goes to show that while he didn’t have the most efficient solution, he stuck with things until he got where he wanted. I encourage less meandering approaches to coding, but I was impressed by the thought that went into this one and the potential that is apparent in his thought process, logic and effort. Way to go Benn!

 

 

   1: ###############################################################################
<!--CRLF-->
   2: #  _ ____        _              ____ _           _ _                         #
<!--CRLF-->
   3: # (_)  _ \ _   _| | ___  ___   / ___| |__   __ _| | | ___ _ __   __ _  ___   #
<!--CRLF-->
   4: # | | |_) | | | | |/ _ \/ __| | |   | '_ \ / _` | | |/ _ \ '_ \ / _` |/ _ \  #
<!--CRLF-->
   5: # | |  _ <| |_| | |  __/\__ \ | |___| | | | (_| | | |  __/ | | | (_| |  __/  #
<!--CRLF-->
   6: # |_|_| \_\\__,_|_|\___||___/  \____|_| |_|\__,_|_|_|\___|_| |_|\__, |\___|  #
<!--CRLF-->
   7: #                                                               |___/        #
<!--CRLF-->
   8: ##############################################################################
<!--CRLF-->
   9: #
<!--CRLF-->
  10: #  Benn Alp b.alp@f5.com
<!--CRLF-->
  11: #
<!--CRLF-->
  12:  
<!--CRLF-->
  13: when RULE_INIT {
<!--CRLF-->
  14: # CONFIGURABLE ITEMS
<!--CRLF-->
  15: #----------------------------------------------------------------------------------------------------------
<!--CRLF-->
  16: # Debug Logging. (Note: Irrispective of how this is set logs to satisfy requirement 6 will be sent -
<!--CRLF-->
  17: # 6 Log any request (IP of client and URI requested) to a canonical name x.domain.com that is non standard
<!--CRLF-->
  18: # (mail, smtp, www, ns, ns1) so that data can be collected as to  when users have fully transitioned" is irrespective of this setting.
<!--CRLF-->
  19: # 0 - Disabled
<!--CRLF-->
  20: # 1 - Enabled
<!--CRLF-->
  21: #----------------------------------------------------------------------------------------------------------
<!--CRLF-->
  22: set static::DebugLogging 1
<!--CRLF-->
  23: #----------------------------------------------------------------------------------------------------------
<!--CRLF-->
  24: # Behaviour when requirement 1 is violated for transformed apps (Ensure that all requests from the client to the BIG-IP are SSL encrypted)
<!--CRLF-->
  25: # Legacy apps continue to work as per normal.
<!--CRLF-->
  26: #  0 - Reject
<!--CRLF-->
  27: #  1 - Redirect to https
<!--CRLF-->
  28: #----------------------------------------------------------------------------------------------------------
<!--CRLF-->
  29: set static::SSLBehaviour 1
<!--CRLF-->
  30:  
<!--CRLF-->
  31: }
<!--CRLF-->
  32: when HTTP_REQUEST {
<!--CRLF-->
  33:         set Rewrite 0
<!--CRLF-->
  34:     if { $static::DebugLogging } { log local0. "Trigger HTTP_REQUEST"}
<!--CRLF-->
  35:         # Requirement 5 - Anyone accessing the application from the Internal Network (10.1.x.x) and with an appropriate auth cookie (Name: X-Int-Auth Value=True) bypassess the above logic and access the old structure, or anybody using domain.com bypass/return
<!--CRLF-->
  36:         if {[IP::client_addr] starts_with "10.1." and [HTTP::header "X-Init-Auth"] equals "True" or [string tolower [HTTP::host]] equals "domain.com" } {  if { $static::DebugLogging } { log local0. "Trigger return based on Requirement 5 or domain=domain.com"} return
<!--CRLF-->
  37:         } else {
<!--CRLF-->
  38:                 if { [HTTP::host] contains ".domain.com" } {
<!--CRLF-->
  39:                 # Requirement 3.1 Standard canonicals are excluded from this re-writing (mail, smtp, www, ns, ns1) and # Requirement 6 - Log any request (IP of Client and URI requested) to a canonical name x.domain.com that is non standard (Mail.smtp,www,ns,ns1) so that data can be collected as to when users have fully transitioned
<!--CRLF-->
  40:                 # Switch -exact was faster than data groups..
<!--CRLF-->
  41:                 switch -exact [string tolower [HTTP::host]] {
<!--CRLF-->
  42:                         "mail.domain.com" { log local0. "Legacy Connection - USERIP [IP::client_addr] - mail.domain.com/[HTTP::uri]" }
<!--CRLF-->
  43:                         "smtp.domain.com" { log local0. "Legacy Connection - USERIP [IP::client_addr] - smtp.domain.com/[HTTP::uri]" }
<!--CRLF-->
  44:                         "www.domain.com" { log local0. "Legacy Connection - USERIP [IP::client_addr] - URI www.domain.com/[HTTP::uri]" }
<!--CRLF-->
  45:                         "ns.domain.com" { log local0. "Legacy Connection - USERIP [IP::client_addr] - URI ns.domain.com/[HTTP::uri]" }
<!--CRLF-->
  46:                         "ns1.domain.com" { log local0. "Legacy Connection - USERIP [IP::client_addr] - URI ns1.domain.com/[HTTP::uri]" }
<!--CRLF-->

 

... And that's all I'm showing of Benn's solution. It goes on for a while, and was an awesome effort but it's...rather long. ;)

 

 

2nd Place – Max Iftikhar

Max set a high bar indeed with his submission which used the uber efficient stream profile, a solid cut at response re-writing, one of the pitfalls of this particular challenge, and some handy dandy string manipulation. This one was efficient, brief, and looked like it could have been the overall winner. All things being equal, in many other FSE classes this very well could have won, as it is a darn fine effort, and Max should hold his head high while coding. Unless of course he can’t see the monitor, then hold it rather normally and just know you kicked some tail, Max.

 

   1: when HTTP_REQUEST {
<!--CRLF-->
   2: if { [TCP::local_port] == 80 } {
<!--CRLF-->
   3:     # redirect to https
<!--CRLF-->
   4:     HTTP::redirect "https://[getfield [HTTP::host] ":" 1][HTTP::uri]"
<!--CRLF-->
   5: }
<!--CRLF-->
   6:  
<!--CRLF-->
   7: when HTTP_REQUEST {
<!--CRLF-->
   8:                
<!--CRLF-->
   9: set rewrite 0
<!--CRLF-->
  10: set canonical [getfield [HTTP::host] "." 1]
<!--CRLF-->
  11: set host1 [HTTP::host]
<!--CRLF-->
  12: set host2 [getfield [HTTP::host] "$canonical" 1]
<!--CRLF-->
  13: set uri1 "[HTTP::uri]"
<!--CRLF-->
  14: set uri2 ""/"$canonical[HTTP::uri]"
<!--CRLF-->
  15:  
<!--CRLF-->
  16: if {[IP::addr [IP::client_addr] equals 10.1.x.x/16] and [HTTP::cookie exists"X-Int-Auth"}
<!--CRLF-->
  17: {
<!--CRLF-->
  18:   pool http_pool
<!--CRLF-->
  19: }
<!--CRLF-->
  20:  
<!--CRLF-->
  21: else {
<!--CRLF-->
  22:   
<!--CRLF-->
  23: log local0. "Received request from [IP::client_addr] -> [HTTP::host][HTTP::uri]"}  
<!--CRLF-->
  24:  
<!--CRLF-->
  25:    # Rewrite the Host header  
<!--CRLF-->
  26:     HTTP::header replace "Host" $::host2  
<!--CRLF-->
  27:                 # Make uri path start with /canoncial if it doesn't already
<!--CRLF-->
  28:     if { not ([HTTP::uri] starts_with "/$canonical") } {
<!--CRLF-->
  29:   HTTP::uri [string map -nocase {$uri1 $uri2} [HTTP::uri]]
<!--CRLF-->
  30:   set urlRewrite 1
<!--CRLF-->
  31:  
<!--CRLF-->
  32: }  
<!--CRLF-->
  33:  
<!--CRLF-->
  34: when HTTP_RESPONSE {
<!--CRLF-->
  35:  
<!--CRLF-->
  36:                 if {$rewrite}{
<!--CRLF-->
  37:  
<!--CRLF-->
  38:                                 # Check if response is a redirect
<!--CRLF-->
  39:                                 if {[HTTP::is_redirect] and [HTTP::header Location] contains $find}{
<!--CRLF-->
  40:                                                 # Rewrite the redirect Location header value
<!--CRLF-->
  41:                                                 HTTP::header replace "Host" $::host1
<!--CRLF-->
  42:                                                 HTTP::header replace Location [string map -nocase "$url1 $url2" [HTTP::header Location]]
<!--CRLF-->
  43:                                 }
<!--CRLF-->
  44:  
<!--CRLF-->
  45:                                 # Check if response payload type is text
<!--CRLF-->
  46:                                 if {[HTTP::header value Content-Type] contains "text"}{
<!--CRLF-->
  47:  
<!--CRLF-->
  48:                                                 # Set the replacement strings
<!--CRLF-->
  49:                                                 STREAM::expression "@$url1@$url2@"
<!--CRLF-->
  50:  
<!--CRLF-->
  51:                                                 # Enable the stream filter for this response only
<!--CRLF-->
  52:                                                 STREAM::enable
<!--CRLF-->
  53:                                 }
<!--CRLF-->
  54:                 }
<!--CRLF-->
  55: }
<!--CRLF-->

 

 

Winner! – Joe Martin

Last but the exact inverse of least, our winner in fact, was Joe Martin. Joe seemed like a normal, average, every day FSE challenge entrant upon first blush. He didn’t even bother to out himself at the onset as having written iRules before when I asked for experience levels. Clever ploy, Joe, very clever. As I was later to find out Joe seemed to in fact be a cyborg-robot-iRules-ninja-hacker-dinosaur sent back from the future to bust the curve for all FSE iRules Challengees everywhere. Seriously, this guy knew what he was doing. This iRule is pretty darn close to the code I would churn out to solve this particular problem and, not to self aggrandize, but that’s not such a bad thing coming from the guy judging the challenge, amirite? Upon presenting the results and having shaken the hand of the Cylon (No windows, you may not autocorrect Cylon to colon. Go away, I’m making jokes here.) in charge of iRules affairs himself, I asked Joe how many iRules he’d written before, because it was obvious that he had done so. Much to his credit he admitted to having written hundreds, which makes a whole heck of a lot of sense, and makes me able to sleep just a bit better at night without keeping a light on to watch out for those cyborg iRules ninja invaders. Big congrats to Joe for a darn fine hunk of codey bits.

 

   1: when HTTP_REQUEST {
<!--CRLF-->
   2:   set request_rewrite 0
<!--CRLF-->
   3:  
<!--CRLF-->
   4:   #Check to see if this in an internal developer request (internal IP and X-Auth header)
<!--CRLF-->
   5:  
<!--CRLF-->
   6:   if { ([HTTP::header X-Int-Auth] equals "True") && ([IP::client_addr] equals "10.1.0.0/16") } {
<!--CRLF-->
   7:     pool http_pool
<!--CRLF-->
   8:  
<!--CRLF-->
   9:   } else {
<!--CRLF-->
  10:  
<!--CRLF-->
  11:     set orig_host [string tolower [HTTP::host]]
<!--CRLF-->
  12:     scan $orig_host %s.%s.%s host domain tld
<!--CRLF-->
  13:     set new_host "$domain.$tld"
<!--CRLF-->
  14:   
<!--CRLF-->
  15:     #Make sure "host" portion of DNS name is not in exclusion data group "class_no-rewrite"
<!--CRLF-->
  16:  
<!--CRLF-->
  17:     if { ![class match $host equals class_no-rewrite] } {
<!--CRLF-->
  18:       
<!--CRLF-->
  19:       #Flag connection as "request rewritten", rewrite host header and URI, and log request info"
<!--CRLF-->
  20:  
<!--CRLF-->
  21:       set request_rewrite 1
<!--CRLF-->
  22:       HTTP::header replace Host $new_host
<!--CRLF-->
  23:       HTTP::uri "/$host[HTTP::uri]"
<!--CRLF-->
  24:       log local0. "Request to $orig_host from [IP::client_addr] rewritten to [HTTP::uri]"
<!--CRLF-->
  25:       pool http_pool
<!--CRLF-->
  26:     }
<!--CRLF-->
  27:   }
<!--CRLF-->
  28: }
<!--CRLF-->
  29:  
<!--CRLF-->
  30: when HTTP_RESPONSE {
<!--CRLF-->
  31:  
<!--CRLF-->
  32:   #If the request was rewritten we need to rewrite Location headers and embedded URLs
<!--CRLF-->
  33:  
<!--CRLF-->
  34:   if {$request_rewrite} {
<!--CRLF-->
  35:     if { [HTTP::is_redirect] }{
<!--CRLF-->
  36:       HTTP::header replace Location [string map -nocase "$new_host $orig_host" [HTTP::header Location]]
<!--CRLF-->
  37:     } else {
<!--CRLF-->
  38:       STREAM::expression "@/$host/@/@ @http://$new_host@https://$orig_host@"
<!--CRLF-->
  39:       STREAM::enable
<!--CRLF-->
  40:     }
<!--CRLF-->
  41:   }
<!--CRLF-->
  42: }
<!--CRLF-->

 

All said and done it was another fine experience hosting the FSE iRules challenge. There was code, and fun, and fun code, and coding fun, and funny code, and…well you get the idea. I’m looking forward to the next crop and seeing what they’re capable of. I’ll be working on my cyborg detection methodologies in the meantime. Until then, remember kids: code hard.

#Colin

 

#iRules #iRulesChallenge #Cylons

Published Aug 29, 2013
Version 1.0

Was this article helpful?