Last active
August 29, 2015 14:20
-
-
Save ricksalsa/8683298a587794afe6fa to your computer and use it in GitHub Desktop.
This iRule is applied to the APM virtual to obtain an OAuth token and do a simple device status check based on Mac Address
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
##################################################################### | |
## APM OPSWAT Gears REST API Device Status Check | |
## Version: 1.0 | |
# | |
# Applies to an APM VIP to enable collection of a mobile device's | |
# status from OPSWAT Gears into the APM session database. | |
#-------------------------------------------------------------------- | |
when RULE_INIT { | |
################################################################ | |
# CONFIGURATION | |
# Only these settings can be modified. | |
# | |
# - Set OPSWAT Gears API host details | |
set static::gears_api_endpoint "gears.opswat.com" | |
# | |
# - Set client ID and secret for API access | |
set static::gears_client_id "REPLACE_WITH_ID" | |
set static::gears_client_secret "REPLACE_WITH_ID" | |
# | |
# - Set name of SSL sideband handling virtual to use. | |
set static::gears_virtual_name "/Common/gears_sideband_virtual" | |
# | |
# - Set the IP address of a DNS resolver to use | |
set static::gears_resolver "10.0.1.1" | |
# | |
# - Set a timeout for connecting to the OPSWAT REST API | |
set static::gears_timeout 300 | |
set static::interval 100 | |
set static::retries 50 | |
# | |
# - Set debug mode for all transactions. | |
set static::gears_debug 1 | |
############################################################### | |
} | |
when ACCESS_POLICY_AGENT_EVENT { | |
if { $static::gears_debug } { set sess_id [ACCESS::session data get "session.user.sessionid"] } | |
#### Set the Access Session ID | |
set conn_sid [ACCESS::session sid] | |
#### Set the devices IP Address to use for searching it's status with the Gears API. | |
set conn_ip [ACCESS::session data get "session.user.clientip"] | |
if { $static::gears_debug } { log local0. "$sess_id - Client IP Address: $conn_ip" } | |
#### Need to obtain an Access Token in order to make an API call. | |
if { $static::gears_debug } { log local0. "$sess_id - Obtaining Access Token." } | |
set conn [connect -timeout 1500 -idle 30 -status conn_status $static::gears_virtual_name] | |
if {$conn eq ""} { | |
if { $static::gears_debug } { log local0. "$sess_id - Connection not established with Gears API." } | |
log local0. "Could not establish the connection. Please check to see whether DNS is set up properly on this LTM." | |
return | |
} | |
set conn_info [connect info -idle -status $conn] | |
set OApost "client_id=${static::gears_client_id}" | |
append OApost "&client_secret=${static::gears_client_secret}" | |
append OApost "&grant_type=client_credentials" | |
set OApostlen [string length $OApost] | |
set OAhead "POST /o/oauth/token HTTP/1.1\r\n" | |
append OAhead "Host: ${static::gears_api_endpoint}\r\n" | |
append OAhead "Content-Length: $OApostlen\r\n" | |
append OAhead "Content-Type: application/x-www-form-urlencoded\r\n" | |
append OAhead "User-Agent: F5-APM-GEARS-Client/1.0\r\n" | |
set OApost_REQUEST "$OAhead\r\n$OApost\r\n\r\n" | |
if { $static::gears_debug } { log local0. "$sess_id - Sending oauth token request [subst $OApost_REQUEST]" } | |
set send_info [send -timeout 1500 -status send_status $conn [subst $OApost_REQUEST]] | |
set recv_data [recv -peek -status peek_status -timeout 3000 $conn] | |
if { $static::gears_debug } { log local0. "$sess_id - Collected response - $peek_status - $recv_data" } | |
if {[string match "*200\ OK*" $recv_data]} { | |
if { $static::gears_debug } { log local0. "$sess_id - Matched expected response for oauth token request (200 OK)." } | |
set success 1 | |
} else { | |
if { $static::gears_debug } { log local0. "$sess_id - Did not match expected response for oauth token request (200 OK)." } | |
set success 0 | |
} | |
close $conn | |
if { $success } { | |
#### Time to extract the Access Token for additional calls | |
regsub -- {.*"access_token"*:*"([^"]*)",.*} $recv_data {\1} ACCESS_TOKEN | |
if { $static::gears_debug } { log local0. "$sess_id - ACCESS_TOKEN: $ACCESS_TOKEN" } | |
} else { | |
ACCESS::session data set -sid $conn_sid session.gears.connection_status "invalid" | |
} | |
if { $success } { | |
switch [ACCESS::policy agent_id] { | |
"gears_device_status" { | |
if { $static::gears_debug } { log local0. "$sess_id - Checking Device Status." } | |
set conn [connect -timeout 1500 -idle 30 -status conn_status $static::gears_virtual_name] | |
if {$conn eq ""} { | |
if { $static::gears_debug } { log local0. "$sess_id - Connection not established with Gears API." } | |
log local0. "Could not establish the connection. Please check to see whether DNS is set up properly on this LTM." | |
ACCESS::session data set -sid $conn_sid session.gears.device.wakeup.connection 0 | |
ACCESS::session data set -sid $conn_sid session.gears.device.wakeup.connection_status "connect_failed" | |
return | |
} | |
set conn_info [connect info -idle -status $conn] | |
set OArequest "GET /o/api/v2/devices/search?q=$conn_ip&in=ip_address&access_token=$ACCESS_TOKEN HTTP/1.1\r\n" | |
append OArequest "Host: ${static::gears_api_endpoint}\r\n" | |
append OArequest "User-Agent: F5-APM-GEARS-Client/1.0\r\n\r\n" | |
if { $static::gears_debug } { log local0. "$sess_id - Searching for client's device address via IP: [subst $OArequest]" } | |
set send_info [send -timeout 1500 -status send_status $conn [subst $OArequest]] | |
set recv_data [recv -peek -status peek_status -timeout 5000 $conn] | |
if { $static::gears_debug } { log local0. "$sess_id - Collected response - $peek_status - $recv_data" } | |
if {[string match "*200\ OK*" $recv_data]} { | |
if { $static::gears_debug } { log local0. "$sess_id - Matched expected response for device search (200 OK)." } | |
set success 1 | |
} else { | |
if { $static::gears_debug } { log local0. "$sess_id - Did not match expected response for device status (200 OK)." } | |
set success 0 | |
} | |
close $conn | |
if { $success } { | |
#Let's now grab the first results device id from the search results: | |
regsub -- {.*"hwid"*:*"([^"]*)",.*} $recv_data {\1} conn_mac | |
if { $static::gears_debug } { log local0. "$sess_id - Client Device ID: ${conn_mac}." } | |
#Now let's search for the actual status of the device itself: | |
if { $static::gears_debug } { log local0. "$sess_id - Checking Device Status." } | |
set conn [connect -timeout 1500 -idle 30 -status conn_status $static::gears_virtual_name] | |
if {$conn eq ""} { | |
if { $static::gears_debug } { log local0. "$sess_id - Connection not established with Gears API." } | |
log local0. "Could not establish the connection. Please check to see whether DNS is set up properly on this BIG-IP." | |
return | |
} | |
set conn_info [connect info -idle -status $conn] | |
set OArequest "GET /o/api/v2.1/devices/$conn_mac?access_token=$ACCESS_TOKEN HTTP/1.1\r\n" | |
append OArequest "Host: ${static::gears_api_endpoint}\r\n" | |
append OArequest "User-Agent: F5-APM-GEARS-Client/1.0\r\n\r\n" | |
if { $static::gears_debug } { log local0. "$sess_id - Searching for client's device details: [subst $OArequest]" } | |
set send_info [send -timeout 1500 -status send_status $conn [subst $OArequest]] | |
# Response payload is larger, so we need to loop to collect all the data | |
set start [clock clicks -milliseconds] | |
for {set i 0} {$i <= $static::retries} {incr i}{ | |
# See what data we have received so far on the connection within the interval timeout | |
set recv_data [recv -peek -status peek_status -timeout $static::interval $conn] | |
if {$static::gears_debug > 1} { log local0. "$sess_id - Peek ([string length $recv_data]): $recv_data" } | |
# Check if we have received the response headers (terminated by two CRLFs) | |
if {[string match "HTTP/*\r\n\r\n*" $recv_data]}{ | |
if {$static::gears_debug} { log local0. "$sess_id - Found the end of the HTTP headers" } | |
# Look for a content-length header in the data we have received so far | |
if {[string match -nocase "*Content-Length: *" $recv_data]}{ | |
# Calculate the length of the response headers plus the payload by parsing the Content-Length header | |
# Get the index of the end of the HTTP headers | |
set header_length [expr {[string first "\r\n\r\n" $recv_data] + 4}] | |
set payload_length [findstr [string tolower $recv_data] "content-length: " 16 "\r"] | |
if {$static::gears_debug} { log local0. "$sess_id - \$header_length: $header_length, \$payload_length: $payload_length" } | |
# If the payload length is greater than 0 | |
if {$payload_length ne "" and $payload_length > 0}{ | |
if {$static::gears_debug} { log local0. "$sess_id - Peeking for [expr {$header_length + $payload_length}] bytes to get the payload" } | |
set recv1_data [recv -peek -timeout $static::gears_timeout -status recv_status [expr {$header_length + $payload_length}] $conn] | |
if { $static::gears_debug } { log local0. "$sess_id - Collected response - $peek_status - $recv_data" } | |
# Exit the while loop | |
break | |
} else { | |
# Content-Length header is 0 so no payload to wait for. | |
# Exit the while loop | |
break | |
} | |
} else { | |
# No Content-Length header so assume there is no payload to wait for. | |
# Exit the while loop | |
break | |
} | |
} | |
} | |
### Set the default device status based on the following status values from the Gears Cloud API: | |
### 0 – the endpoint is in compliance with Gears account’s policies (in Gears Cloud console the device is depicted as Blue); | |
### 1 – the endpoint is not in compliance with Gears account’s policies (in Gears Cloud console the device is depicted as Orange); | |
### 3 – the endpoint is still sending information to Gears Cloud and not yet completed | |
if {[string match "*200\ OK*" $recv_data]} { | |
if { $static::gears_debug } { log local0. "$sess_id - Matched expected response for device status (200 OK)." } | |
if {[string match "*\"status\":0*" $recv_data]} { | |
if { $static::gears_debug } { log local0. "$sess_id - Device status ok!" } | |
# Define that the session has been completed and returned valid data. | |
ACCESS::session data set -sid $conn_sid session.gears.connection_status "valid" | |
} else { | |
if { $static::gears_debug } { log local0. "$sess_id - Device status invalid!" } | |
regsub -- {.*"remediation_link"*:*"([^"]*)",.*} $recv_data {\1} remediation_link | |
if { $static::gears_debug } { log local0. "$sess_id - Remediation required: $remediation_link" } | |
ACCESS::session data set -sid $conn_sid session.gears.connection_status "invalid" | |
ACCESS::session data set -sid $conn_sid session.gears.remediation_url $remediation_link | |
} | |
} else { | |
if { $static::gears_debug } { log local0. "$sess_id - Did not match expected response for device status (200 OK)." } | |
ACCESS::session data set -sid $conn_sid session.gears.connection_status "invalid" | |
} | |
close $conn | |
} else { | |
ACCESS::session data set -sid $conn_sid session.gears.connection_status "invalid" | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment