Skip to content

Commit

Permalink
doc for f5 irule script (#302)
Browse files Browse the repository at this point in the history
* doc for f5 irule script

* fix script

* value dont change case
  • Loading branch information
sohitgore authored Jan 6, 2025
1 parent 0a14dee commit 5da3a02
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 0 deletions.
35 changes: 35 additions & 0 deletions docs/install-traffic-capture-sensors/f5-ltm-log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
sidebar_position: 9
description: Learn how to capture and analyze traffic from F5's LTM.
---

# BIG-IP LTM

## iRule-based Instrumentation

### Pre-requisites
- You have sufficient permissions to add iRule to the Virtual Servers.
- The Satellite has been successfully set up and is reachable from the BIG-IP LTM.

### Installation
To instrument your APIs from the LTM Virtual Servers, you need to:
1. Download and configure the iRule script
2. Add the iRule to the VirtualServers you want to instrument

#### Download and configure iRule Script

- Download the [iRule script](../../static/artifacts/scripts/levo-irule.tcl)

- Locate `collector_host_ip` and `collector_host_port` inside the script, and set them to appropriate values.
Note that the virtual server should be able to reach to this IP.

### Add the iRule to the VirtualServers you want to instrument

- In the BIG-IP Dashboard, go to, `Local Traffic > iRules > iRule List`
- Click on `Create` and paste the downloaded iRule inside the Definition. Give the script a name and click on `Finished`
- Next, go to, `Local Traffic > Virtual Servers > Virtual Server List`
- Click on the virtual server which you want to instrument.
- Under the `Resources` section, locate iRules. Click on Manage
- Move the Levo's iRule script to `Enabled` section. Click on `Finished`.

The iRule script will start instrumenting and send the traces to Levo's Collector.
186 changes: 186 additions & 0 deletions static/artifacts/scripts/levo-irule.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
proc isCapturableContentType {contentType} {
set contentType [string tolower $contentType]
foreach type $::static::capturable_content_types {
if {[string match *$type* $contentType]} {
return 1
}
}
return 0
}

when RULE_INIT {

# Limit payload collection to 10Kb
set static::max_collect_len 10240

# Body content types to capture
set static::capturable_content_types {json xml x-www-form-urlencoded}

# Levo.ai's Collector details
set static::collector_host_ip "<collector-host-ip>"
set static::collector_host_port "<collector-host-port>"
# Do not modify
set static::collector_uri "/v1/f5-ltm-logs"

}

when CLIENT_ACCEPTED {

set hsl [HSL::open -proto TCP -pool $static::hsl_pool]
set sessionId "[IP::client_addr][TCP::client_port][IP::local_addr][TCP::local_port][expr { int(100000000 * rand()) }]"
binary scan [md5 $sessionId] H* correlationId junk

if { [PROFILE::exists clientssl] == 1 } {
set protocol "HTTPS"
} else {
set protocol "HTTP"
}
set requestId 0
}

when HTTP_REQUEST {
incr requestId

set request_time [clock clicks -milliseconds]
set json_req_header_names_list "\["
set json_req_header_values_list "\["
set contentTypeHeaderValue ""
set is_first 1
foreach aHeader [HTTP::header names] {
if { [string tolower $aHeader] == "content-type" } {
set contentTypeHeaderValue [HTTP::header value $aHeader]
}
set lwcasekey [string map -nocase {"\"" "\\\""}[string tolower $aHeader]]
set value [HTTP::header value $aHeader]

if {$is_first} {
append json_req_header_names_list "\"$lwcasekey\""
append json_req_header_values_list "\"$value\""
set is_first 0
} else {
append json_req_header_names_list ",\"$lwcasekey\""
append json_req_header_values_list ",\"$value\""
}
}
append json_req_header_names_list "\]"
append json_req_header_values_list "\]"

set uri [HTTP::uri]
set method [HTTP::method]
set queryString [HTTP::query]
set client_addr [IP::client_addr]
set local_port [TCP::local_port]
set host [HTTP::host]
set request_time [clock clicks -milliseconds]
set request_payload ""
set request_truncated 0

set captureBody [call isCapturableContentType $contentTypeHeaderValue]
if {$captureBody} {
HTTP::collect $static::max_collect_len
}
}

when HTTP_REQUEST_DATA {
if {$captureBody && [HTTP::payload length] > 0 } {
set capture_length [HTTP::payload length]
if { $capture_length > $static::max_collect_len } {
set request_truncated 1
set capture_length $static::max_collect_len
}
set request_payload [b64encode [string range "[HTTP::payload]" 0 $capture_length ]]
}
}

when HTTP_RESPONSE {
set response_time [clock clicks -milliseconds]
set resHeaderString "${static::response_header_start}"
set json_res_header_names_list "\["
set json_res_header_values_list "\["
set contentTypeHeaderValue ""
set status [HTTP::status]

set is_first 1
foreach aHeader [HTTP::header names] {
if { [string tolower $aHeader] == "content-type" } {
set contentTypeHeaderValue [HTTP::header value $aHeader]
}
set lwcasekey [string map -nocase {"\"" "\\\""}[string tolower $aHeader]]
set value [HTTP::header value $aHeader]

if {$is_first} {
append json_res_header_names_list "\"$lwcasekey\""
append json_res_header_values_list "\"$value\""
set is_first 0
} else {
append json_res_header_names_list ",\"$lwcasekey\""
append json_res_header_values_list ",\"$value\""
}

set headers "${static::header_name}${lwcasekey}${static::header_value}${value}"
append resHeaderString $headers
}
append json_res_header_names_list "\]"
append json_res_header_values_list "\]"

set captureResponseBody [call isCapturableContentType $contentTypeHeaderValue]
if {$captureResponseBody} {
HTTP::collect $static::max_collect_len
}
set data_sent 0
set response_truncated 0
# Prepare an HTTP POST request
set http_request "POST ${static::collector_uri} HTTP/1.1\r\n"
append http_request "Host: ${static::collector_host_ip}\r\n"
append http_request "Content-Type: application/json\r\n"


if { [HTTP::header exists "Content-Length"] && [HTTP::header value "Content-Length"] == 0 } {
set response_payload ""

set log_message "{\"config\": \{ \"requestId\": \"$correlationId\", \"eventType\": \"f5_ltm_log\"\}, \"request\": \{ \"protocol\": \"$protocol\",\"uri\": \"$uri\", \"method\":\"$method\", \"domainName\": \"$host\", \"queryString\": \"$queryString\", \"clientIp\": \"$client_addr\", \"headerNamesList\": $json_req_header_names_list, \"headerValuesList\": $json_req_header_values_list, \"body\": \"$request_payload\" \}, \"response\": \{ \"headerNamesList\": $json_res_header_names_list, \"headerValuesList\": $json_res_header_values_list, \"body\": \"$response_payload\", \"status\": \"$status\" \} }"
append http_request "Content-Length: [string length $log_message]\r\n"
append http_request "\r\n"
append http_request "$log_message"

# Open a connection to the HTTP server
set conn_handle [connect ${static::collector_host_ip} ${static::collector_host_port}]

# Send the HTTP POST request
if {$conn_handle ne ""} {
send $conn_handle $http_request
close $conn_handle
}

set data_sent 1
}
}

when HTTP_RESPONSE_DATA {
set response_payload ""
if { $captureResponseBody && [HTTP::payload length] > 0 } {
set capture_length [HTTP::payload length]
if { $capture_length > $static::max_collect_len } {
set response_truncated 1
set capture_length $static::max_collect_len
}
set response_payload [b64encode [string range "[HTTP::payload]" 0 $capture_length]]
}
if { $data_sent != 1 } {
set log_message "{\"config\": \{ \"requestId\": \"$correlationId\", \"eventType\": \"f5_ltm_log\"\}, \"request\": \{ \"protocol\": \"$protocol\",\"uri\": \"$uri\", \"method\":\"$method\", \"domainName\": \"$host\", \"queryString\": \"$queryString\", \"clientIp\": \"$client_addr\", \"headerNamesList\": $json_req_header_names_list, \"headerValuesList\": $json_req_header_values_list, \"body\": \"$request_payload\" \}, \"response\": \{ \"headerNamesList\": $json_res_header_names_list, \"headerValuesList\": $json_res_header_values_list, \"body\": \"$response_payload\", \"status\": \"$status\" \} }"
append http_request "Content-Length: [string length $log_message]\r\n"
append http_request "\r\n"
append http_request "$log_message"

# log local0. $http_request

# Open a connection to the HTTP server
set conn_handle [connect ${static::collector_host_ip} ${static::collector_host_port}]

# Send the HTTP POST request
if {$conn_handle ne ""} {
send $conn_handle $http_request
close $conn_handle
}
}
}

0 comments on commit 5da3a02

Please sign in to comment.