Advertisement
gusto2

F5 Sideband info

Jul 11th, 2014
661
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.03 KB | None | 0 0
  1. F5 Sideband info
  2.  
  3. https://devcentral.f5.com/wiki/iRules.SIDEBAND.ashx
  4.  
  5. parse headers:
  6. set _SBrlist [regexp -line -all -inline -- {^([^:]+):[ \t]*([^\r]*)} $_SBhdrs]
  7.  
  8. iRule example:
  9.  
  10. #iRule to load user data from an external source
  11. # source: http://10.0.1.105/data/data.php?uid=parameter
  12.  
  13. # based on a sideband rule https://devcentral.f5.com/wiki/iRules.HTTP-Super-SIDEBAND-Requestor-Client-Handles-Redirects-Cookies-Chunked-Transfer-APM-Access-etc.ashx
  14.  
  15.  
  16. when ACCESS_POLICY_AGENT_EVENT {
  17.  
  18. #===iRule Source===
  19.  
  20. #(Put "set SB_uri "http://foohost/barpath" et-cetera here. Vide infra*)
  21. #
  22. #== Mark Seecof's HTTP Super SIDEBAND Requestor! =======================
  23. #
  24. #This blob of code implements a nifty HTTP client using SIDEBAND. It
  25. #handles challenges like redirects (even with cookies-- lets you access
  26. #APM-protected services!), chunked transfer coding, and Basic AuthN.
  27. #
  28. #You supply (*before entering):
  29. # $SB_uri = absolute URI of target, plus optionally...
  30. # $SB_virt = (optional) name of target virtual server--if set, used
  31. # in lieu of host/port in $SB_uri (also see $SB_redir below)
  32. # If you set $SB_virt then host in $SB_uri can be anything (xxx), but if
  33. # you do not set $SB_virt you must either put IP (to use port 80) or
  34. # IP:port in host part of $SB_uri, or ensure your LTM's DNS setup is
  35. # valid so [RESOLV::lookup] will work.
  36. #
  37. #You should supply as well:
  38. # $SB_tag = a short name for your target just for local logging
  39. #
  40. #Optionally, you may also supply:
  41. # $SB_key = a short ID for this particular request in the local log,
  42. # such as [ACCESS::session sid]. Default is [IP::client_addr]
  43. #
  44. # $SB_method = [GET|POST|...], default is GET
  45. # $SB_body = data to send (e.g., when method is POST**). Default empty.
  46. # $SB_headers = a list of alternating HTTP header names and values, like
  47. # {Accept text/* Accept-Language fr-CA}. Any header set
  48. # in $SB_headers replaces corresponding default. Default
  49. # headers: {Accept */* Accept-Language en-US User-Agent
  50. # {Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1)}
  51. # Accept-Encoding identity}
  52. # $SB_cookies = list of alternating cookie names and values. Dfl none.
  53. # $SB_userid and $SB_passwd for Basic Authentication. Default none.
  54. # $SB_redir = [0|1|2|3] where 0=ignore redirects; 1=follow redirects and
  55. # retry POST/PUT after redir out-and-back (so you can POST to
  56. # an APM-protected resource), but if $SB_virt is set send all
  57. # requests to it; 2=like 1 except do not retry POST; 3=like
  58. # 2 but follow redirects away from $SB_virt. Default is 1.
  59. # $SB_debug = [0|1] where 1 means log debug info. Default is 0.
  60. #
  61. #Then we do the rest! Afterward, look at $SB_status, $SB_body,
  62. #$SB_headers, and $SB_cookies for reply from remote server. ($SB_status
  63. #"555" indicates some local problem--check LTM log.)
  64. #
  65. #**If you POST don't forget your Content-Type header, something like:
  66. # set SB_headers [list Content-Type "application/json; charset=utf-8"]
  67. #
  68. #Copyright &copr; 2013 Mark Seecof. Permission to modify and distribute
  69. #granted gratis on condition that you include this notice with each copy.
  70. #
  71.  
  72. #
  73. set uid [ACCESS::session data get session.user.sessionid]
  74. set SB_uri "http://10.0.1.105/data/data.php?uid=$uid"
  75. set SB_virt http-aaa-vs
  76. set SB_tag "GetExtUserInfo"
  77. set SB_key $uid
  78. set SB_method "GET"
  79. set SB_debug 0
  80.  
  81. if {![info exists SB_virt]} { set SB_virt "" }
  82. if {![info exists SB_tag]} { set SB_tag "HTTP Requestor" }
  83. if {![info exists SB_key]} { set SB_key "client [IP::client_addr]" }
  84. if {![info exists SB_method]} { set SB_method "GET" }
  85. if {![info exists SB_body]} { set SB_body "" }
  86. if {![info exists SB_headers]} { set SB_headers [list] }
  87. if {![info exists SB_cookies]} { set SB_cookies [list] }
  88. if {![info exists SB_userid]} { set SB_userid "" }
  89. if {![info exists SB_passwd]} { set SB_passwd "" }
  90. if {![info exists SB_redir]} { set SB_redir 1 }
  91. if {![info exists SB_debug]} { set SB_debug 0 }
  92. set _SBdone [list Host Content-Length Accept Accept-Language User-Agent Accept-Encoding]
  93. set SB_status 555
  94. set _SBprot [set _SBhost [set _SBport ""]]
  95. set _SBpath /
  96. set _SBtry -2
  97. if {![info exists SB_uri]} {
  98. log local0.err "${SB_tag} ${SB_key}: you must supply \$SB_uri!"
  99. } elseif {![regexp -nocase {^(https?)://([^:/]+)(:[0-9]+)*(/[^\x23]*)} $SB_uri _SBjunk _SBprot _SBhost _SBport _SBpath]} {
  100. log local0.err "${SB_tag} ${SB_key}: malformed \$SB_uri: ${SB_uri}"
  101. } elseif {[string equal -nocase $_SBprot "https"] && ($SB_virt eq "")} {
  102. log local0.err "${SB_tag} ${SB_key}: SIDEBAND to TLS needs helper virtual-server"
  103. } else {
  104. #max redirects we will follow
  105. set _SBtry 5
  106. }
  107.  
  108. set _SBdest ""
  109. set _SBwho [set _SBwhere [set _SBhow [set _SBwhat ""]]]
  110. while {[incr _SBtry -1] >= 0} {
  111. #what will we connect to?
  112. if {$_SBdest eq ""} {
  113. if {$SB_virt ne ""} {
  114. set _SBdest $SB_virt
  115. } else {
  116. if {[catch {IP::addr $_SBhost mask 255.255.255.255}]} {
  117. if {[set _SBaddr [RESOLV::lookup -a $_SBhost]] eq ""} {
  118. log local0.err "${SB_tag} ${SB_key}: cannot resolve ${_SBhost} to IP address"
  119. break
  120. }
  121. } else {
  122. set _SBaddr $_SBhost
  123. }
  124. if {$_SBport eq ""} {set _SBport ":80"}
  125. set _SBdest "${_SBaddr}${_SBport}"
  126. }
  127. }
  128.  
  129. #retry POST after redirect(s)?
  130. if {($_SBwhat ne "") && [string equal -nocase $_SBdest $_SBwho] &&
  131. ($_SBpath eq $_SBwhere)} {
  132. set SB_method $_SBhow
  133. set SB_body $_SBwhat
  134. set _SBwhat ""
  135. }
  136.  
  137. #assemble HTTP request
  138. set _SBreq "${SB_method} ${_SBpath} HTTP/1.1\r\n"
  139. set _SBv $_SBhost
  140. if {[set _SBx [lsearch -regexp $SB_headers "(?i)Host"]] > -1} {
  141. set _SBv [lindex $SB_headers [incr _SBx]]
  142. }
  143. set _SBreq "${_SBreq}Host: ${_SBv}\r\n"
  144. set _SBv [string length $SB_body]
  145. if {[set _SBx [lsearch -regexp $SB_headers "(?i)Content-Length"]] > -1} {
  146. set _SBv [lindex $SB_headers [incr _SBx]]
  147. }
  148. set _SBreq "${_SBreq}Content-Length: ${_SBv}\r\n"
  149. set _SBv "*/*"
  150. if {[set _SBx [lsearch -regexp $SB_headers "(?i)Accept"]] > -1} {
  151. set _SBv [lindex $SB_headers [incr _SBx]]
  152. }
  153. set _SBreq "${_SBreq}Accept: ${_SBv}\r\n"
  154. set _SBv "en-US"
  155. if {[set _SBx [lsearch -regexp $SB_headers "(?i)Accept-Language"]] > -1} {
  156. set _SBv [lindex $SB_headers [incr _SBx]]
  157. }
  158. set _SBreq "${_SBreq}Accept-Language: ${_SBv}\r\n"
  159. set _SBv "Mozilla/4.0 (compatible;)"
  160. if {[set _SBx [lsearch -regexp $SB_headers "(?i)User-Agent"]] > -1} {
  161. set _SBv [lindex $SB_headers [incr _SBx]]
  162. }
  163. set _SBreq "${_SBreq}User-Agent: ${_SBv}\r\n"
  164. set _SBv "identity"
  165. if {[set _SBx [lsearch -regexp $SB_headers "(?i)Accept-Encoding"]] > -1} {
  166. set _SBv [lindex $SB_headers [incr _SBx]]
  167. }
  168. set _SBreq "${_SBreq}Accept-Encoding: ${_SBv}\r\n"
  169. if {($SB_userid ne "") && ($SB_passwd ne "")} {
  170. set _SBreq "${_SBreq}Authorization: Basic [b64encode "${SB_userid}:${SB_passwd}"]\r\n"
  171. }
  172. if {[set _SBe [llength $SB_headers]] > 1} {
  173. set _SBx 0
  174. while {$_SBx < $_SBe} {
  175. set _SBn [lindex $SB_headers $_SBx]
  176. set _SBv [lindex $SB_headers [incr _SBx]]
  177. if {[lsearch -regexp $_SBdone "(?i)${_SBn}"] == -1} {
  178. set _SBreq "${_SBreq}${_SBn}: ${_SBv}\r\n"
  179. }
  180. incr _SBx
  181. }
  182. }
  183. if {[set _SBe [llength $SB_cookies]] > 1} {
  184. set _SBx 0
  185. while {$_SBx < $_SBe} {
  186. set _SBreq "${_SBreq}Cookie: [lindex $SB_cookies $_SBx]=[lindex $SB_cookies [incr _SBx]]\r\n"
  187. incr _SBx
  188. }
  189. }
  190. set _SBreq "${_SBreq}\r\n${SB_body}"
  191.  
  192. if {$SB_debug} {
  193. log local0.info "${SB_tag} ${SB_key}: ${_SBdest} request=${_SBreq}"
  194. log local0.info "${SB_tag} ${SB_key}: attempt connect to ${_SBdest}, 5 sec timeout"
  195. }
  196. set _SBstat "error"
  197. if {[catch {connect -timeout 5000 -idle 15 -status _SBstat $_SBdest} _SBconn] ||
  198. ($_SBconn eq "")} {
  199. log local0.err "connect to ${SB_tag} ${_SBdest} for ${SB_key} fails: ${_SBstat}"
  200. break
  201. }
  202.  
  203. if {$SB_debug} {
  204. log local0.info "${SB_tag} ${SB_key}: connected to ${_SBdest}, sending [string length $_SBreq]"
  205. }
  206. set _SBsent [send -timeout 5000 -status _SBstat $_SBconn $_SBreq]
  207. if {($_SBstat ne "sent") || ($_SBsent < [string length $_SBreq])} {
  208. log local0.err "send ${SB_tag} req for ${SB_key} to ${_SBdest} fails: ${_SBstat}"
  209. break
  210. }
  211.  
  212. #poll every 50ms for server reply, max delay 10 sec
  213. set _SBctr 200
  214. set _SBstat [set _SBhdrs [set _SBbody ""]]
  215. while {($_SBctr > 0) && ($_SBstat ne "failed")} {
  216. set _SBreply [recv -peek -timeout 50 -status _SBstat $_SBconn]
  217. set _SBhbrk [string first "\r\n\r\n" $_SBreply]
  218. if {$_SBhbrk > 0} {
  219. #encode-decode shuffle below required until f5 fixes regexp copy-on-write corruption bug
  220. set _SBhdrs [URI::decode [URI::encode [recv [expr {$_SBhbrk + 4}] $_SBconn]]]
  221. if {$SB_debug} {
  222. log local0.info "${SB_tag}: reply hdrs [string map [list "\r\n" ", "] $_SBhdrs]"
  223. }
  224. if {([regexp -nocase {Content-Length:[ \t]*([0-9]+)} $_SBhdrs _SBjunk _SBclen]) &&
  225. ($_SBclen > 0)} {
  226. set _SBbody [recv -timeout [expr {$_SBctr * 50}] -status _SBstat $_SBclen $_SBconn]
  227. if {[string length $_SBbody] < $_SBclen} {
  228. log local0.err "recv ${_SBclen} bytes from ${SB_tag} ${_SBdest} for ${SB_key} fails: ${_SBstat}"
  229. set _SBhdrs ""
  230. set _SBbody ""
  231. }
  232. } elseif {[regexp -nocase {Transfer-Encoding:[ \t]*Chunked} $_SBhdrs]} {
  233. while {($_SBctr > 0) && ($_SBstat ne "failed")} {
  234. set _SBchunk [recv -peek -timeout 50 -status _SBstat $_SBconn]
  235. if {[regexp -nocase {^([0-9a-f]+)[^\r]*[\r][\n]} $_SBchunk _SBjunk _SBhex] &&
  236. ($_SBhex ne "0")} {
  237. recv [string length $_SBjunk] $_SBconn
  238. scan $_SBhex "%x" _SBchunksz
  239. if {$_SBchunksz > 0} {
  240. set _SBchunk [recv -timeout [expr {$_SBctr * 50}] -status _SBstat $_SBchunksz $_SBconn]
  241. if {[string length $_SBchunk] == $_SBchunksz} {
  242. set _SBbody "${_SBbody}${_SBchunk}"
  243. } else {
  244. log local0.err "recv chunk ${_SBhex} from ${SB_tag} ${_SBdest} for ${SB_key} fails: ${_SBstat}"
  245. set _SBhdrs ""
  246. set _SBbody ""
  247. set _SBctr 0
  248. }
  249. }
  250. } elseif {($_SBhex eq 0) || ($_SBchunk starts_with "\r\n")} {
  251. #ignore any trailing headers (Yeah? So sue me!)
  252. close $_SBconn
  253. set _SBctr 0
  254. } else {
  255. incr _SBctr -1
  256. }
  257. }
  258. }
  259. #here we have reply headers, plus body if any, so stop polling
  260. set _SBctr 0
  261. }
  262. incr _SBctr -1
  263. }
  264. if {$_SBhdrs eq ""} {
  265. log local0.err "recv reply from ${SB_tag} ${_SBdest} for ${SB_key} fails: ${_SBstat}"
  266. break
  267. }
  268.  
  269. #extract HTTP status code to $_SBrslt
  270. regexp {HTTP[^ ]+ ([0-9]+)} $_SBhdrs _SBjunk _SBrslt
  271.  
  272. #process reply headers.
  273. #This code will not handle headers broken across lines--
  274. #those are legal but rare enough to disregard here
  275. set _SBckys [list]
  276. set _SBrlist [regexp -line -all -inline -- {^([^:]+):[ \t]*([^\r]*)} $_SBhdrs]
  277. set _SBhlist [list]
  278. set _SBe [llength $_SBrlist]
  279. set _SBx 1
  280. while {$_SBx < $_SBe} {
  281. set _SBn [lindex $_SBrlist $_SBx]
  282. set _SBv [lindex $_SBrlist [incr _SBx]]
  283.  
  284. if {[string equal -nocase $_SBn "Location"] && $SB_redir &&
  285. [regexp {^30[1237]$} $_SBrslt]} {
  286. if {($_SBrslt <= 302) && ($SB_redir == 1) && ($_SBwhat eq "") &&
  287. ([string first $SB_method "GET HEAD"] < 0)} {
  288. set _SBwho $_SBdest
  289. set _SBwhere $_SBpath
  290. set _SBhow $SB_method
  291. set _SBwhat $SB_body
  292. }
  293. if {$_SBrslt <= 303} {
  294. set SB_method "GET"
  295. set SB_body ""
  296. }
  297. set _SBp ":80"
  298. if {$_SBv starts_with "/"} {
  299. #relative path so contact same host
  300. set _SBpath $_SBv
  301. } elseif {[regexp -nocase {^(https?)://([^:/]+)(:[0-9]+)*(/[^\x23]*)} $_SBv _SBjunk _SBt _SBh _SBp _SBa]} {
  302. set _SBpath $_SBa
  303. if {![string equal -nocase $_SBhost $_SBh] || ($_SBport ne $_SBp)} {
  304. set _SBdest ""
  305. if {$SB_redir == 3} {
  306. set SB_virt ""
  307. if {[string equal -nocase $_SBt "https"]} {
  308. log local0.err "${SB_tag} ${SB_key}: redir to ${_SBv} TLS requires helper virtual-server"
  309. break
  310. }
  311. }
  312. set _SBprot $_SBt
  313. }
  314. } else {
  315. #oops, Location not HTTP URI
  316. log local0.err "${SB_tag}: cannot follow redirect from ${_SBdest} to ${_SBv} for ${SB_key}"
  317. break
  318. }
  319. }
  320. if {[string equal -nocase $_SBn "Set-Cookie"]} {
  321. set _SBcky [set _SBval ""]
  322. regexp {^([^=]+)=([^;]*);} $_SBv _SBjunk _SBcky _SBval
  323. if {$_SBcky ne ""} {
  324. lappend _SBckys $_SBcky $_SBval
  325. }
  326. }
  327. if {[string equal -nocase $_SBn "WWW-Authenticate"] && ($_SBrslt eq "401") &&
  328. [regexp -nocase {Basic } $_SBv] && ($SB_userid ne "") && ($SB_passwd ne "")} {
  329. #We already sent "Authorization: Basic xxx" header but some servers
  330. #(f5 APM) will 401 first request and only look for creds on second
  331. #request (and change cookies, too)
  332. set $_SBrslt "455"
  333. }
  334.  
  335. lappend _SBhlist $_SBn $_SBv
  336. incr _SBx 2
  337. }
  338.  
  339. #update cookie list (we ignore lifetimes and elide duplicate-name cookies)
  340. set _SBtmp [list]
  341. if {[llength $SB_cookies] > 1} {
  342. foreach {_SBcky _SBval} $SB_cookies {
  343. #cookie names ARE case-sensitive
  344. if {([set _SBx [lsearch $_SBckys $_SBcky]] < 0) || ($_SBx & 1)} {
  345. lappend _SBtmp $_SBcky $_SBval
  346. }
  347. }
  348. }
  349. set SB_cookies [concat $_SBtmp $_SBckys]
  350.  
  351. if {$_SBrslt eq "455"} {
  352. #retry Basic authz
  353. continue
  354. }
  355.  
  356. #do we follow a redirect, or are we finished?
  357. if {!$SB_redir || ![regexp {^30[1237]} $_SBrslt] || ($_SBtry < 1)} {
  358. set SB_status $_SBrslt
  359. set SB_body [URI::decode [URI::encode $_SBbody]]
  360. set SB_headers $_SBhlist
  361. #SB_cookies already set
  362. break
  363. }
  364. }
  365. if {$_SBtry == -1} {
  366. log local0.err "${SB_tag} ${SB_key}: too many redirects"
  367. }
  368. #At this point, you may inspect $SB_status, $SB_body, $SB_headers,
  369. #and $SB_cookies for results of your request.
  370. #
  371. #== End of Mark Seecof's HTTP Super SIDEBAND Requestor! ================
  372.  
  373. set _SBrlist [regexp -line -all -inline -- {^([^:]+):[ \t]*([^\r]*)} $SB_body]
  374. set _SBx 1
  375. while {$_SBx < $_SBe} {
  376. set _SBn [lindex $_SBrlist $_SBx]
  377. set _SBv [lindex $_SBrlist [incr _SBx]]
  378. if {[string equal -nocase $_SBn "X-user-name"] } {
  379. ACCESS::session data set session.user.xname "$_SBv"
  380. }
  381. elseif {[string equal -nocase $_SBn "X-user-id"]} {
  382. ACCESS::session data set session.user.xuid "$_SBv"
  383. }
  384. }
  385.  
  386. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement