Forum Discussion
Hi I am looking for some help with an irule that can call out to an api
I been pulled into a project that is replacing a mobile app. For the project they need f5 to do the following :
- listen for a http request on a vs and extract the bearer token fro that request
- append the token to an api query and send it to another application
- parse the json response for query and capture the username sent back
- Replace header values in the original request and send it to the backend servers on the vs.
Basically replacing the bearer token with other headers. This is what I came up with as a skelton but having trouble on how to make an api call from an irule and parse the json response. Also the vendor mentioned that a solution with these steps have been implemented at other clients who use Netscaler. I am waiting for tech documentation on the netscaler solution to see if can help me out.
when when HTTP_REQUEST {
if {[HTTP::uri match "/Auth/OAuth2/CREATESESSION"] and [string tolower [HTTP::header Authorization]] contains "bearer"} {
#Extract the bearer token from the auth header an dsave it to a variable
set bearer_token [string map {bearer ""} [string to lower[HTTP::header value Authorization]]
#Send an api query to https://TBD/api/scim/Me?attributes=userName that appends the bearer token to the end
set api_endpoint "https://TBD/api/scim/Me?attributes="
append api_endpoint $bearer_token
??? --> how to call a api from irule
#Parse the json response and retrieve the username.
??? --> set euid [parse json for username key pair ]
#Rewrite the original HTTP request replace the User ID in the header with the new user ID and update the Authorization header to static basic header with auth credentials.
HTTP::header replace Authorization ""
HTTP::header replace User-ID $euid
HTTP::header replace User-IDType "SYSTEMLOGIN"
}
}
irules is not good at parsing JSON key value in HTTP Payload, Initiating HTTPS connections on irules' sideband is also cumbersome
I suggest you use irules and cooperate with iRulesLX to initiate a sideband https connection using node.js,Node.js can use its built-in JSON library to handle JSON, which is very advantageous
- xuwenCumulonimbus
irules is not good at parsing JSON key value in HTTP Payload, Initiating HTTPS connections on irules' sideband is also cumbersome
I suggest you use irules and cooperate with iRulesLX to initiate a sideband https connection using node.js,Node.js can use its built-in JSON library to handle JSON, which is very advantageous
- sajmethodAltostratus
Thank for the suggestion . I don't have much experience with irulesLX or node .js for that matter. I will look into this. I was also wondering if a per request policy with an http connector my be a more optimized solution. I can trigger the access policy based on request. I have not used http connectors so the flow is still fuzzy in my mind.
You can use this for call out to another http based service: https://clouddocs.f5.com/api/irules/HTTP-Super-SIDEBAND-Requestor-Client-Handles-Redirects-Cookies-Chunked-Transfer-APM-Access-etc.html
iRules have only string parsing capabilities, but if the json is simple enough, there is no need for a full json parser.
This thread reminds me, that I always wanted to write a simple json_get_key function for iRules...
- sajmethodAltostratus
I do not know how big the reponse JSON will be yet, per example it does not look like a lot of lines. I will take a look at the sideband request as I have not used that but the all the responses are pointing to sideband solution. I will look through the link you provided to get a better understandng of it and see if it can solve the problem.
As juergen said, I'd script a procedure that calls API via a sideband connection.
You can use this as a starting point: (it's a working code that I'm running on a customer)edit : if you want to debug it, i'd recommend logging $payload value to understand better what the api is returning
from there you can use string operators syntax to match the intended KV pair and estract the valuewhen RULE_INIT { set static::calls_timeout 5 set static::first_recv_timeout 50 set static::first_recv_tries 25 } proc apicall {ip port uri} { set api_res "" set retries 0 set api_req "GET $uri HTTP/1.1\r\nUser-Agent: F5 API service\r\nHost: TBD\r\n\r\n" set api_conn [connect -timeout $static::calls_timeout -status conn_status $ip:$port] if {[send -timeout $static::calls_timeout $api_conn $api_req]} { while {$retries < $static::first_recv_tries} { set api_res [recv -timeout $static::first_recv_timeout -status recv_status $api_conn] if {$api_res ne ""} { if {[string match -nocase "*Content-Length: *" $api_res]} { set header_len [expr {[string first "\r\n\r\n" $api_res] + 4}] set payload_len [findstr [string tolower $api_res] "content-length: " 16 "\r"] set api_res_len [string length $api_res] if {$payload_len ne "" and $payload_len > 0 and [expr {$header_len + $payload_len}] != $api_res_len} { set api_res "$api_res[recv -peek -timeout $static::calls_timeout [expr {$header_len + $payload_len - $api_res_len}] $api_conn]" set api_res_len [string length $api_res] } } set payload [string range $api_res $header_len $api_res_len] #the two lines below are used to match a string in response. In this example i want to retrieve VALUE from key KEY, formatted as {"KEY":"VALUE"} in response set search_start [string first "\{" $payload] set api_res_ret [string trim [string map {"\{\"KEY\":" ""} [string range $payload $search_start [expr {[string first "," $payload $search_start] - 1}]]] "\""] break } else { set retries [expr {$retries + 1}] log local0.debug "No response after $retries retries, T= [expr {$retries * $static::first_recv_timeout}]ms."} set api_res_ret "FAILED" } } } close $api_conn return $api_res_ret } when HTTP_REQUEST { # < your code here > # set token [HTTP::header Authorization] # set uri "/api/scim/Me?attributes=$token" set key [call apicall $api_ip $api_port $uri] if {$key eq "FAILED"}{ # api call failed, do something } else { # < your code here > HTTP::header replace Authorization "" HTTP::header replace User-ID $euid HTTP::header replace User-IDType "SYSTEMLOGIN" } }
- xuwenCumulonimbus
Personally, it is recommended to use irules in conjunction with iRulesLX. Using iRulesLX to initiate HTTPS sideband connections and handle JSON is simple, while the official TCL super HTTPS sideband connection code is too long, which affects device performance.
Your code appears to be an http sideband, not an encrypted https sideband
Thank you xuwen for poiting this out.
You're right, it's implemented unencrypted in my environment. I must have missed the requirement for SSL.
- sajmethodAltostratus
Thanks , I will try this out in my test environment and see how it works.
- sajmethodAltostratus
I started with Ca-valli solution because i was more comfortable with it but with all the extra irules ( sideband, helper sideband) I needed to incorporate it seemed bulky and seemed difficult to document and maintain by others. I decided to go with the irulelx as the json parsing was easier and the whole setup once I understood how it works is easier to maintain. Unfortunately the app group has not tested it yet. I will update this post once they get around to testing it.
Thanks for all the help on this guys!
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