Skip to content

Instantly share code, notes, and snippets.

@ricksalsa
Last active August 29, 2015 14:20
Show Gist options
  • Save ricksalsa/8683298a587794afe6fa to your computer and use it in GitHub Desktop.
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
#####################################################################
## 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