LTM
191 TopicsUltimate irule debug - Capture and investigate
Problem this snippet solves: I decided to share this Irule for different reasons. When I help our community on devcentral, I regularly see people making recurring requests: How do I do to capture the queries header. How do I do to capture the response header. How do I check the information in the POST Request. How do I check response data (body). What cypher/protocol I use (SSL/TLS). I set up client certificate authentication but I do not know if it works and if I pass my certificate auth. I want to retrieve information from my authentication certificate (subject, issuer, …). My authenticating by certificate does not work and I get an error of what I have to do. I have latencies when dealing with my request. where does the latency come from (F5, server,..). I set up sso (kerberos delegation, json post, Form sso). I do not feel that my request is sent to the backend (or the kerberos token). Does F5 add information or modify the request/response. Which pool member has been selected My VS don’t answer (where does the problem come from) … instead of having an Irule for each request why not consolidate everything and provide a compact Irule. this Irule can help you greatly during your investigations and allows you to capture these different items: How to use this snippet: you have a function that allows you to activate the desired logs (1 to activate and 0 to disable) as describe below: array set app_arrway_referer { client_dest_ip_port 1 client_cert 1 http_request 1 http_request_release 1 http_request_payload 0 http_lb_selected 1 http_response 0 http_response_release 0 http_response_payload 0 http_time_process 0 } the posted logs will be preceded by a UID which will allow you to follow from the beginning to the end of the process of your request / answer. you can for example make a grep on the log to follow the complete process (request / answer). the UID is generated in the following way: `set uid [string range [AES::key 256] 15 23] client_dest_ip_port: this section will allow you to see source IP/Port and destination IP/Port. <CLIENT_ACCEPTED>: ----------- client_dest_ip_port ----------- <CLIENT_ACCEPTED>: uid: 382951fe9 - Client IP Src: 10.20.30.4:60419 <CLIENT_ACCEPTED>: uid: 382951fe9 - Client IP Dest:192.168.30.45:443 <CLIENT_ACCEPTED>: ----------- client_dest_ip_port ----------- client_cert: this section will allow you to check the result code for peer certificate verification ( and also if you have provide a certificate auth). moreover you will be able to recover the information of your authentication certficat (issuer, subject, …). if your authentication certificate that you provid is not valid an error message will be returned (ex: certificate chain too long, invalid CA certificate, …). all errors are listed in the link below: https://devcentral.f5.com/wiki/iRules.SSL__verify_result.ashx <HTTP_REQUEST>: ----------- client_cert ----------- <HTTP_REQUEST>: uid: 382951fe9 - cert number: 0 <HTTP_REQUEST>: uid: 382951fe9 - subject: OU=myOu, CN=youssef <HTTP_REQUEST>: uid: 382951fe9 - Issuer Info: DC=com, DC=domain, CN=MobIssuer <HTTP_REQUEST>: uid: 382951fe9 - cert serial: 22:00:30:5c:de:dd:ec:23:6e:b5:e6:77:bj:01:00:00:22:3c:dc <HTTP_REQUEST>: ----------- client_cert ----------- OR <HTTP_REQUEST>: ----------- client_cert ----------- <HTTP_REQUEST>: uid: 382951fe9 - No client certificate provided <HTTP_REQUEST>: ----------- client_cert ----------- http_request: This section allow you to retrieve the complete client HTTP request headers (that is, the method, URI, version, and all headers). I also added the protocol, the ciphers and the name of the vs used. <HTTP_REQUEST>: ----------- http_request ----------- <HTTP_REQUEST>: uid: 382951fe9 - protocol: https <HTTP_REQUEST>: uid: 382951fe9 - cipher name: ECDHE-RSA-AES128-GCM-SHA256 <HTTP_REQUEST>: uid: 382951fe9 - cipher version: TLSv1.2 <HTTP_REQUEST>: uid: 382951fe9 - VS Name: /Common/vs-myapp-443 <HTTP_REQUEST>: uid: 382951fe9 - Request: POST myapp.mydomain.com/browser-management/users/552462/playlist/play/api <HTTP_REQUEST>: uid: 382951fe9 - Host: myapp.mydomain.com <HTTP_REQUEST>: uid: 382951fe9 - Connection: keep-alive <HTTP_REQUEST>: uid: 382951fe9 - Content-Length: 290 <HTTP_REQUEST>: uid: 382951fe9 - Accept: application/json, text/javascript, */*; q=0.01 <HTTP_REQUEST>: uid: 382951fe9 - X-Requested-With: XMLHttpRequest <HTTP_REQUEST>: uid: 382951fe9 - User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36 <HTTP_REQUEST>: uid: 382951fe9 - Referer: https://myapp.mydomain.com/ <HTTP_REQUEST>: uid: 382951fe9 - Accept-Encoding: gzip, deflate, sdch, br <HTTP_REQUEST>: uid: 382951fe9 - Accept-Language: en-US,en;q=0.8 <HTTP_REQUEST>: uid: 382951fe9 - Cookie: RLT=SKjpfdkFDKjkufd976HJhldds=; secureauth=true; STT="LKJSDKJpjslkdjslkjKJSHjfdskjhoLHkjh78dshjhd980szKJH"; ASP.SessionId=dsliulpoiukj908798dsjkh <HTTP_REQUEST>: uid: 382951fe9 - X-Forwarded-For: 10.10.10.22 <HTTP_REQUEST>: ----------- http_request ----------- http_request_release: This section triggered when the system is about to release HTTP data on the serverside of the connection. This event is triggered after modules process the HTTP request. So it will allow you to check request after F5 process. suppose that you have put APM with SSO kerberos, you will be able to see the kerberos token insert by F5. Or XFF insert by HTTP profile… <HTTP_REQUEST_RELEASE>: ----------- http_request_release ----------- <HTTP_REQUEST_RELEASE>: uid: 382951fe9 - VS Name: /Common/vs-myapp-443 <HTTP_REQUEST_RELEASE>: uid: 382951fe9 - Request: GET myapp.mydomain.com/browser-management/users/552462/playlist/play/api <HTTP_REQUEST_RELEASE>: uid: 382951fe9 - Host: myapp.mydomain.com <HTTP_REQUEST_RELEASE>: uid: 382951fe9 - Connection: keep-alive <HTTP_REQUEST_RELEASE>: uid: 382951fe9 - Accept: application/json, text/javascript, */*; q=0.01 <HTTP_REQUEST_RELEASE>: uid: 382951fe9 - X-Requested-With: XMLHttpRequest <HTTP_REQUEST_RELEASE>: uid: 382951fe9 - User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36 <HTTP_REQUEST_RELEASE>: uid: 382951fe9 - Referer: https://myapp.mydomain.com/ <HTTP_REQUEST_RELEASE>: uid: 382951fe9 - Accept-Encoding: gzip, deflate, sdch, br <HTTP_REQUEST_RELEASE>: uid: 382951fe9 - Accept-Language: en-US,en;q=0.8 <HTTP_REQUEST_RELEASE>: uid: 382951fe9 - Cookie: RLT=SKjpfdkFDKjkufd976HJhldds=; secureauth=true; STT="LKJSDKJpjslkdjslkjKJSHjfdskjhoLHkjh78dshjhd980szKJH"; ASP.SessionId=dsliulpoiukj908798dsjkh <HTTP_REQUEST_RELEASE>: uid: 382951fe9 - X-Forwarded-For: 10.10.10.22 <HTTP_REQUEST_RELEASE>: ----------- http_request_release ----------- http_request_payload: This section will allow you to retrieve the HTTP request body. <HTTP_REQUEST>: ----------- http_request_payload ----------- <HTTP_REQUEST>: uid: 382951fe9 - Content-Length header null in request If GET or POST withtout content) <HTTP_REQUEST>: ----------- http_request_payload ----------- or <HTTP_REQUEST>: ----------- http_request_payload ----------- <HTTP_REQUEST>: uid: 382951fe9 - post payload: { id: 24, retrive: 'identity', service: 'IT'} <HTTP_REQUEST>: ----------- http_request_payload ----------- http_lb_selected This section will allow you to you to see which pool member has been selected. Once the pool memeber has been selected, you will not see this logs again until another load balancing decision will be made. If you want to see the selected pool memeber for each request you can see this information in "http_response". <HTTP_REQUEST>: ----------- http_lb_selected ----------- <LB_SELECTED>: uid: 382951fe9 - pool member IP: /Common/pool-name 10.22.33.54 443 <HTTP_REQUEST>: ----------- http_lb_selected ----------- http_response: This section will allow you to retrieve the response status and header lines from the server response. You can also see which pool member has been selected. <HTTP_RESPONSE>: ----------- http_response ----------- <HTTP_RESPONSE>: uid: 382951fe9 - status: 200 <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - pool member IP: /Common/pool-name 10.22.33.54 443 <HTTP_RESPONSE>: uid: 382951fe9 - Cache-Control: no-cache <HTTP_RESPONSE>: uid: 382951fe9 - Pragma: no-cache <HTTP_RESPONSE>: uid: 382951fe9 - Content-Type: application/json; charset=utf-8 <HTTP_RESPONSE>: uid: 382951fe9 - Expires: -1 <HTTP_RESPONSE>: uid: 382951fe9 - Server: Microsoft-IIS/8.5 <HTTP_RESPONSE>: uid: 382951fe9 - X-Powered-By: ASP.NET <HTTP_RESPONSE>: uid: 382951fe9 - Date: Fri, 28 Oct 2018 06:46:59 GMT <HTTP_RESPONSE>: uid: 382951fe9 - Content-Length: 302 <HTTP_RESPONSE>: ----------- http_response ----------- http_response_release: This section triggered when the system is about to release HTTP data on the clientside of the connection. This event is triggered after modules process the HTTP response. you can make sure that the answer has not been altering after the f5 process. You can also see which pool member has been selected. <HTTP_RESPONSE_RELEASE>: ----------- http_response_release ----------- <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - status: 200 <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - pool member IP: /Common/pool-name 10.22.33.54 443 <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - Cache-Control: no-cache <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - Pragma: no-cache <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - Content-Type: application/json; charset=utf-8 <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - Expires: -1 <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - Server: Microsoft-IIS/8.5 <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - X-Powered-By: ASP.NET <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - Date: Fri, 28 Oct 2018 06:46:59 GMT <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - Content-Length: 302 <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - Strict-Transport-Security: max-age=16070400; includeSubDomains <HTTP_RESPONSE_RELEASE>: ----------- http_response_release ----------- http_response_payload: This section will allow you to Collects an amount of HTTP body data that you specify. <HTTP_RESPONSE_DATA>: ----------- http_response_payload ----------- <HTTP_RESPONSE_DATA>: uid: 382951fe9 - Response (Body) payload: { "username" : "youssef", "genre" : "unknown", "validation-factors" : { "validationFactors" : [ { "name" : "remote_address", "value" : "127.0.0.1" } ] }} <HTTP_RESPONSE_DATA>: ----------- http_response_payload ----------- http_time_process: this part will allow you to put back information which can be useful to you to target the latency problematic. it is clear that it is not precise and that f5 offers other tools for that. but you will be able to quickly see which elements take the most time to be processed. you will be able to see how long f5 takes to process the request, the response and how long the backend server takes time to respond. <HTTP_RESPONSE_RELEASE>: ----------- http_time_process ----------- <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - Time to request (F5 request time) = 5 (ms) <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - Time to response (F5 response time) = 0 (ms) <HTTP_RESPONSE_RELEASE>: uid: 382951fe9 - Time to server (server backend process time) = 4 (ms) <HTTP_RESPONSE_RELEASE>: ----------- http_time_process ----------- Code : when CLIENT_ACCEPTED { # set a unique id for transaction set uid [string range [AES::key 256] 15 23] # set what's you want to retrieve 0 or 1 array set app_arrway_referer { client_dest_ip_port 1 client_cert 1 http_request 1 http_request_release 1 http_request_payload 1 http_lb_selected 1 http_response 1 http_response_release 1 http_response_payload 1 http_time_process 1 } if {$app_arrway_referer(client_dest_ip_port)} { log local0. " ----------- client_dest_ip_port ----------- " clientside { log local0. "uid: $uid - Client IP Src: [IP::client_addr]:[TCP::client_port]" } log local0. "uid: $uid - Client IP Dest:[IP::local_addr]:[TCP::local_port]" log local0. " ----------- client_dest_ip_port ----------- " log local0. " " } } when HTTP_REQUEST { set http_request_time [clock clicks -milliseconds] # Triggered when the system receives a certificate message from the client. The message may contain zero or more certificates. if {$app_arrway_referer(client_cert)} { log local0. " ----------- client_cert ----------- " # SSL::cert count - Returns the total number of certificates that the peer has offered. if {[SSL::cert count] > 0}{ # Check if there was no error in validating the client cert against LTM's server cert if { [SSL::verify_result] == 0 }{ for {set i 0} {$i < [SSL::cert count]} {incr i}{ log local0. "uid: $uid - cert number: $i" log local0. "uid: $uid - subject: [X509::subject [SSL::cert $i]]" log local0. "uid: $uid - Issuer Info: [X509::issuer [SSL::cert $i]]" log local0. "uid: $uid - cert serial: [X509::serial_number [SSL::cert $i]]" } } else { # https://devcentral.f5.com/s/wiki/iRules.SSL__verify_result.ashx (OpenSSL verify result codes) log local0. "uid: $uid - Cert Info: [X509::verify_cert_error_string [SSL::verify_result]]" } } else { log local0. "uid: $uid - No client certificate provided" } log local0. " ----------- client_cert ----------- " log local0. " " } if {$app_arrway_referer(http_request)} { log local0. " ----------- http_request ----------- " if { [PROFILE::exists clientssl] == 1 } { log local0. "uid: $uid - protocol: https" log local0. "uid: $uid - cipher name: [SSL::cipher name]" log local0. "uid: $uid - cipher version: [SSL::cipher version]" } log local0. "uid: $uid - VS Name: [virtual]" log local0. "uid: $uid - Request: [HTTP::method] [HTTP::host][HTTP::uri]" foreach aHeader [HTTP::header names] { log local0. "uid: $uid - $aHeader: [HTTP::header value $aHeader]" } log local0. " ----------- http_request ----------- " log local0. " " } set collect_length_request [HTTP::header value "Content-Length"] set contentlength 1 if {$app_arrway_referer(http_request_payload)} { if { [catch { if { $collect_length_request > 0 && $collect_length_request < 1048577 } { set collect_length $collect_length_request } else { set collect_length 1048576 } if { $collect_length > 0 } { HTTP::collect $collect_length_request set contentlength 1 } }] } { # no DATA in POST Request log local0. " ----------- http_request_payload ----------- " log local0. "uid: $uid - Content-Length header null in request" log local0. " ----------- http_request_payload ----------- " log local0. " " set contentlength 0 } } } when HTTP_REQUEST_DATA { if {$app_arrway_referer(http_request_payload)} { log local0. " ----------- http_request_payload ----------- " if {$contentlength} { set postpayload [HTTP::payload] log local0. "uid: $uid - post payload: $postpayload" #HTTP::release } log local0. " ----------- http_request_payload ----------- " log local0. " " } } when HTTP_REQUEST_RELEASE { if {$app_arrway_referer(http_request_release)} { log local0. " ----------- http_request_release ----------- " if { [PROFILE::exists clientssl] == 1 } { log local0. "uid: $uid - cipher protocol: https" log local0. "uid: $uid - cipher name: [SSL::cipher name]" log local0. "uid: $uid - cipher version: [SSL::cipher version]" } log local0. "uid: $uid - VS Name: [virtual]" log local0. "uid: $uid - Request: [HTTP::method] [HTTP::host][HTTP::uri]" foreach aHeader [HTTP::header names] { log local0. "uid: $uid - $aHeader: [HTTP::header value $aHeader]" } log local0. " ----------- http_request_release ----------- " log local0. " " } set http_request_time_release [clock clicks -milliseconds] } when LB_SELECTED { if {$app_arrway_referer(http_lb_selected)} { log local0. " ----------- http_lb_selected ----------- " log local0. "uid: $uid - pool member IP: [LB::server]" log local0. " ----------- http_lb_selected ----------- " log local0. " " } } when HTTP_RESPONSE { set http_response_time [clock clicks -milliseconds] set content_length [HTTP::header "Content-Length"] if {$app_arrway_referer(http_response)} { log local0. " ----------- http_response ----------- " log local0. "uid: $uid - status: [HTTP::status]" log local0. "uid: $uid - pool member IP: [LB::server]" foreach aHeader [HTTP::header names] { log local0. "uid: $uid - $aHeader: [HTTP::header value $aHeader]" } log local0. " ----------- http_response ----------- " log local0. " " } if {$app_arrway_referer(http_response_payload)} { if { $content_length > 0 && $content_length < 1048577 } { set collect_length $content_length } else { set collect_length 1048576 } if { $collect_length > 0 } { HTTP::collect $collect_length } } } when HTTP_RESPONSE_DATA { if {$app_arrway_referer(http_response_payload)} { log local0. " ----------- http_response_payload ----------- " set payload [HTTP::payload] log local0. "uid: $uid - Response (Body) payload: $payload" log local0. " ----------- http_response_payload ----------- " log local0. " " } } when HTTP_RESPONSE_RELEASE { set http_response_time_release [clock clicks -milliseconds] if {$app_arrway_referer(http_response_release)} { log local0. " ----------- http_response_release ----------- " log local0. "uid: $uid - status: [HTTP::status]" log local0. "uid: $uid - pool member IP: [LB::server]" foreach aHeader [HTTP::header names] { log local0. "uid: $uid - $aHeader: [HTTP::header value $aHeader]" } log local0. " ----------- http_response_release ----------- " log local0. " " } if {$app_arrway_referer(http_time_process)} { log local0. " ----------- http_time_process ----------- " log local0.info "uid: $uid - Time to request (F5 request time) = [expr $http_request_time - $http_request_time_release] (ms)" log local0.info "uid: $uid - Time to response (F5 response time) = [expr $http_response_time - $http_response_time_release] (ms)" log local0.info "uid: $uid - Time to server (server backend process time) = [expr $http_request_time_release - $http_response_time] (ms)" log local0. " ----------- http_time_process ----------- " log local0. " " } } Tested this on version: 13.05.4KViews6likes12CommentsProxy Protocol v2 Initiator
Problem this snippet solves: Proxy Protocol v1 related articles have already been posted on DevCentral, but there is no v2 support iRule code available. A customer wanted to support Proxy Protocol v2, so I wrote an iRule code for supporting v2. Proxy protocol for the BIG-IP (f5.com) How to use this snippet: Back-end server must handle Proxy header prior data exchange. Code : when CLIENT_ACCEPTED { # DEBUG On/Off set DEBUG 0 set v2_proxy_header "0d0a0d0a000d0a515549540a" # v2 version and command : 0x21 - version 2 & PROXY command set v2_ver_command "21" # v2 address family and transport protocol : 0x11 - AF_INET (IPv4) & TCP protocol set v2_af_tp "11" # v2 Address Size : 0x000C - 12 bytes for IPv4 + TCP set v2_address_length "000c" # Get TCP port - 2 byte hexadecimal format set src_port [format "%04x" [TCP::client_port]] set dst_port [format "%04x" [TCP::local_port]] # Get Src Address and convert to 4 byte hexadecimal format foreach val [split [IP::client_addr] "."] { append src_addr [format "%02x" $val] } # Get Dst Address and convert to 4 byte hexadecimal format foreach val [split [IP::local_addr] "."] { append dst_addr [format "%02x" $val] } # Build proxy v2 data set proxy_data [binary format H* "${v2_proxy_header}${v2_ver_command}${v2_af_tp}${v2_address_length}${src_addr}${dst_addr}${src_port}${dst_port}"] if { $DEBUG } { binary scan $proxy_data H* proxy_dump log local0. "[IP::client_addr]:[TCP::client_port]_[IP::local_addr]:[TCP::local_port] - proxy_data dump : $proxy_dump" } } when SERVER_CONNECTED { TCP::respond $proxy_data }641Views2likes1CommentBIG-IP Report
Problem this snippet solves: Overview This is a script which will generate a report of the BIG-IP LTM configuration on all your load balancers making it easy to find information and get a comprehensive overview of virtual servers and pools connected to them. This information is used to relay information to NOC and developers to give them insight in where things are located and to be able to plan patching and deploys. I also use it myself as a quick way get information or gather data used as a foundation for RFC's, ie get a list of all external virtual servers without compression profiles. The script has been running on 13 pairs of load balancers, indexing over 1200 virtual servers for several years now and the report is widely used across the company and by many companies and governments across the world. It's easy to setup and use and only requires auditor (read-only) permissions on your devices. Demo/Preview Interactive demo http://loadbalancing.se/bigipreportdemo/ Screen shots The main report: The device overview: Certificate details: How to use this snippet: Installation instructions BigipReport REST This is the only branch we're updating since middle of 2020 and it supports 12.x and upwards (maybe even 11.6). Downloads: https://loadbalancing.se/downloads/bigipreport-v5.7.13.zip Documentation, installation instructions and troubleshooting: https://loadbalancing.se/bigipreport-rest/ Docker support https://loadbalancing.se/2021/01/05/running-bigipreport-on-docker/ Kubernetes support https://loadbalancing.se/2021/04/16/bigipreport-on-kubernetes/ BIG-IP Report (Legacy) Older version of the report that only runs on Windows and is depending on a Powershell plugin originally written by Joe Pruitt (F5) BIG-IP Report (only download this if you have v10 devices): https://loadbalancing.se/downloads/bigipreport-5.4.0-beta.zip iControl Snapin https://loadbalancing.se/downloads/f5-icontrol.zip Documentation and Installation Instructions https://loadbalancing.se/bigip-report/ Upgrade instructions Protect the report using APM and active directory Written by DevCentral member Shann_P: https://loadbalancing.se/2018/04/08/protecting-bigip-report-behind-an-apm-by-shannon-poole/ Got issues/problems/feedback? Still have issues? Drop a comment below. We usually reply quite fast. Any bugs found, issues detected or ideas contributed makes the report better for everyone, so it's always appreciated. --- Join us on Discord: https://discord.gg/7JJvPMYahA Code : BigIP Report Tested this on version: 12, 13, 14, 15, 1615KViews20likes97Commentssnmp-check external monitor
Problem this snippet solves: This external monitor script runs an snmpget to pool members and marks the members up or down based upon the result. Specifically created for this GTM/APM use case, but can be modified as needed. How to use this snippet: copy the contents of this file into /config/monitors/snmp-check, and then in the external monitor configuration, reference the monitor and provide the following variable key/value pairs: result=<result> community=<community> OID=<oid> Code : #!/bin/sh # # (c) Copyright 1996-2005 F5 Networks, Inc. # # This software is confidential and may contain trade secrets that are the # property of F5 Networks, Inc. No part of the software may be disclosed # to other parties without the express written consent of F5 Networks, Inc. # It is against the law to copy the software. No part of the software may # be reproduced, transmitted, or distributed in any form or by any means, # electronic or mechanical, including photocopying, recording, or information # storage and retrieval systems, for any purpose without the express written # permission of F5 Networks, Inc. Our services are only available for legal # users of the program, for instance in the event that we extend our services # by offering the updating of files via the Internet. # # @(#) $Id: sample_monitor,v 1.3 2005/02/04 18:47:17 saxon Exp $ # # # these arguments supplied automatically for all external pingers: # $1 = IP (nnn.nnn.nnn.nnn notation or hostname) # $2 = port (decimal, host byte order) # $3 and higher = additional arguments # # $MONITOR_NAME = name of the monitor # # In this sample script, $3 is the regular expression # #These lines are required to control the process ID of the monitor pidfile="/var/run/$MONITOR_NAME.$1..$2.pid" if [ -f $pidfile ] then kill -9 `cat $pidfile` > /dev/null 2>&1 fi echo "$$" > $pidfile #Since version9 uses the ipv6 native version of the IP address, parse that down #for usage node_ip=`echo $1 | sed 's/::ffff://'` #Log the variables for debugging #echo IP= $node_ip Port =$2 OID= $OID comm= $community result= $result >> /var/tmp/test #Create a variable called answer that contains the result of the snmpwalk. answer=`snmpget $node_ip -c $community -O v $OID | awk '{print $2}'` #Log the answer for debugging #echo Answer= $answer >> /var/tmp/test if [ $answer -lt $result ] then echo "up" fi rm -f $pidfile Tested this on version: No Version Found2KViews2likes5CommentsTACACS+ External Monitor (Python)
Problem this snippet solves: This script is an external monitor for TACACS+ that simulates a TACACS+ client authenticating a test user, and marks the status of a pool member as up if the authentication is successful. If the connection is down/times out, or the authentication fails due to invalid account settings, the script marks the pool member status as down. This is heavily inspired by the Radius External Monitor (Python) by AlanTen. How to use this snippet: Prerequisite This script uses the TACACS+ Python client by Ansible (tested on version 2.6). Create the directory /config/eav/tacacs_plus on BIG-IP Copy all contents from tacacs_plus package into /config/eav/tacacs_plus. You may also need to download six.py from https://raw.githubusercontent.com/benjaminp/six/master/six.py and place it in /config/eav/tacacs_plus. You will need to have a test account provisioned on the TACACS+ server for the script to perform authentication. Installation On BIG-IP, import the code snippet below as an External Monitor Program File. Monitor Configuration Set up an External monitor with the imported file, and configure it with the following environment variables: KEY: TACACS+ server secret USER: Username for test account PASSWORD: Password for test account MOD_PATH: Path to location of Python package tacacs_plus, default: /config/eav TIMEOUT: Duration to wait for connectivity to TACACS server to be established, default: 3 Troubleshooting SSH to BIG-IP and run the script locally $ cd /config/filestore/files_d/Common_d/external_monitor_d/ # Get name of uploaded file, e.g.: $ ls -la ... -rwxr-xr-x. 1 tomcat tomcat 1883 2021-09-17 04:05 :Common:tacacs-monitor_39568_7 # Run the script with the corresponding variables $ KEY=<my_tacacs_key> USER=<testuser> PASSWORD=<supersecure> python <external program file, e.g.:Common:tacacs-monitor_39568_7> <TACACS+ server IP> <TACACS+ server port> Code : #!/usr/bin/env python # # Filename : tacacs_plus_mon.py # Author : Leon Seng # Version : 1.2 # Date : 2021/09/21 # Python ver: 2.6+ # F5 version: 12.1+ # # ========== Installation # Import this script via GUI: # System > File Management > External Monitor Program File List > Import... # Name it however you want. # Get, modify and copy the following modules: # ========== Required modules # -- six -- # https://pypi.org/project/six/ # Copy six.py into /config/eav # # -- tacacs_plus -- # https://pypi.org/project/tacacs_plus/ | https://github.com/ansible/tacacs_plus # Copy tacacs_plus directory into /config/eav # ========== Environment Variables # NODE_IP - Supplied by F5 monitor as first argument # NODE_PORT - Supplied by F5 monitor as second argument # KEY - TACACS+ server secret # USER - Username for test account # PASSWORD - Password for test account # MOD_PATH - Path to location of Python package tacacs_plus, default: /config/eav # TIMEOUT - Duration to wait for connectivity to TACACS server to be established, default: 3 import os import socket import sys if os.environ.get('MOD_PATH'): sys.path.append(os.environ.get('MOD_PATH')) else: sys.path.append('/config/eav') # https://github.com/ansible/tacacs_plus from tacacs_plus.client import TACACSClient node_ip = sys.argv[1] node_port = int(sys.argv[2]) key = os.environ.get("KEY") user = os.environ.get("USER") password = os.environ.get("PASSWORD") timeout = int(os.environ.get("TIMEOUT", 3)) # Determine if node IP is IPv4 or IPv6 family = None try: socket.inet_pton(socket.AF_INET, node_ip) family = socket.AF_INET except socket.error: # not a valid address try: socket.inet_pton(socket.AF_INET6, node_ip) family = socket.AF_INET6 except socket.error: sys.exit(1) # Authenticate against TACACS server client = TACACSClient(node_ip, node_port, key, timeout=timeout, family=family) try: auth = client.authenticate(user, password) if auth.valid: print "up" except socket.error: # EAV script marks node as DOWN when no output is present pass Tested this on version: 12.11.3KViews1like0CommentsEspecial Load Balancing Active-Passive Scenario (I)
Problem this snippet solves: This code was written to solve this issue REF - https://devcentral.f5.com/s/feed/0D51T00006i7jWpSAI Specification: 2 clusters with 2 nodes each one. each cluster will be served as active-passive method. each node in the cluster will be served as round robin. when a cluster changes to active, it will keep this status although the initial active cluster change back to up status. Only one BIG-IP device. There are many topics suggesting to use "Manual Resume" trying to goal this specifications, but this requires to manually restore each node when is back online. My initial idea was to have an unattended virtual server. To do so, I use a combination of persistence and an internal virtual server loadbalancing (Vip-targeting-Vip in the same device). How to use this snippet: This scenario is composed by the next set of objects: 4 nodes (Node1, Node2, Node3, Node4) 1 additional node called "internal_node" (which represents the VIP used on VIP-Targeting-VIP) 2 pools called "ClusterA_pool" and "ClusterB_pool" (which points to each pair of nodes) 1 additional pool called "MyPool" (which points the two internal VIP) 2 virtual servers called "ClusterA_vs" and "ClusterB_vs" (which use RoundRobin to the pools of the same name) 1 virtual server called "MyVS" (which is the visible VS and points to "MyPool") By the way, I use a "Slow Ramp Time" of 0 to reduce the failover time. Following you can find an example of configuration: ----------------- ltm virtual MyVS { destination 10.130.40.150:http ip-protocol tcp mask 255.255.255.255 persist { universal { default yes } } pool MyPool profiles { tcp { } } rules { MyRule } source 0.0.0.0/0 translate-address enabled translate-port enabled vs-index 53 } ltm virtual ClusterA_vs { destination 10.130.40.150:1001 ip-protocol tcp mask 255.255.255.255 pool ClusterA_pool profiles { tcp { } } source 0.0.0.0/0 translate-address enabled translate-port enabled vs-index 54 } ltm virtual ClusterB_vs { destination 10.130.40.150:1002 ip-protocol tcp mask 255.255.255.255 pool ClusterB_pool profiles { tcp { } } source 0.0.0.0/0 translate-address enabled translate-port enabled vs-index 55 } ltm pool ClusterA_pool { members { Node1:http { address 10.130.40.201 session monitor-enabled state up } Node2:http { address 10.130.40.202 session monitor-enabled state up } } monitor tcp slow-ramp-time 0 } ltm pool ClusterB_pool { members { Node3:http { address 10.130.40.203 session monitor-enabled state up } Node4:http { address 10.130.40.204 session monitor-enabled state up } } monitor tcp slow-ramp-time 0 } ltm node local_node { address 10.130.40.150 } ----------------- Code : when CLIENT_ACCEPTED { set initial 0 set entry "" } when LB_SELECTED { incr initial # Checks if persistence entry exists catch { set entry [persist lookup uie [virtual name]] } # Loadbalancing selection base on persistence if { $entry eq "" } { set selection [LB::server port] } else { set selection [lindex [split $entry " "] 2] set status [LB::status pool MyPool member [LB::server addr] $selection] if { $status ne "up" } { catch { [persist delete uie [virtual name]] } set selection [LB::server port] } } # Adds a new persistence entry catch { persist add uie [virtual name] } # Applies the selection switch $selection { # This numbers represents the ports used at the VIP-targeting-VIP "1001" { LB::reselect virtual ClusterA_vs } "1002" { LB::reselect virtual ClusterB_vs } } } Tested this on version: 12.12.5KViews0likes1CommentPowerShell module for the F5 LTM REST API
Problem this snippet solves: To report an issue with the F5-LTM or F5-BIGIP modules, please use the Issues sections of the GitHub repos (here and here) instead of commenting here. Thanks! This PowerShell module uses the iControlREST API to manipulate and query pools, pool members, virtual servers, and iRules. It aims to support version 11.5.1 and higher, and to conform to the schedule for technical support of versions, though this may eventually prove to become difficult. The module currently includes some functionality that, strictly speaking, is outside the scope of the LTM module. Hence, there is an active effort to wrap this LTM module into a larger BIG-IP module, and relocate that functionality elsewhere within that parent module, as well as expand the scope of functionality to include BIG-IP DNS (formerly GTM) and possibly other areas. Both the LTM module and the parent BIG-IP module are projects on github. Please use these projects to report any issues you discover. Thanks! The module contains the following functions. Add-iRuleToVirtualServer Add-iRuleToVirtualServer Add-PoolMember Add-PoolMonitor Disable-PoolMember Disable-VirtualServer Enable-PoolMember Enable-VirtualServer Get-CurrentConnectionCount (deprecated; use Get-PoolMemberStats | Select-Object -ExpandProperty 'serverside.curConns') Get-F5Session (will be deprecated in future versions. use New-F5Session) Get-F5Status Get-HealthMonitor Get-HealthMonitorType Get-iRule Get-iRuleCollection (deprecated; use Get-iRule) Get-Node Get-BIGIPPartition Get-Pool Get-PoolList (deprecated; use Get-Pool) Get-PoolMember Get-PoolMemberCollection (deprecated; use Get-PoolMember) Get-PoolMemberCollectionStatus Get-PoolMemberDescription (deprecated; use Get-PoolMember) Get-PoolMemberIP (deprecated; use Get-PoolMember) Get-PoolMembers (deprecated; use Get-PoolMember) Get-PoolMemberStats Get-PoolMemberStatus (deprecated; use Get-PoolMember) Get-PoolMonitor Get-PoolsForMember Get-StatusShape Get-VirtualServer Get-VirtualServeriRuleCollection (deprecated; use Get-VirtualServer | Where rules | Select -ExpandProperty rules) Get-VirtualServerList (deprecated; use Get-VirtualServer) Invoke-RestMethodOverride New-F5Session New-HealthMonitor New-Node New-Pool New-VirtualServer Remove-HealthMonitor Remove-iRule Remove-iRuleFromVirtualServer Remove-Pool Remove-PoolMember Remove-PoolMonitor Remove-ProfileRamCache Remove-Node Remove-VirtualServer Set-iRule Set-PoolLoadBalancingMode (deprecated; use Set-Pool) Set-PoolMemberDescription Set-Pool Set-VirtualServer Sync-DeviceToGroup Test-F5Session Test-Functionality Test-HealthMonitor Test-Node Test-Pool Test-VirtualServer How to use this snippet: To use the module, click 'Download Zip', extract the files, and place them in a folder named F5-LTM beneath your PowerShell modules folder. By default, this is %USERPROFILE%\Documents\WindowsPowerShell\Modules. The WindowsPowerShell and Modules folders may need to be created. You will most likely need to unblock the files after extracting them. Use the Unblock-File PS cmdlet to accomplish this. The Validation.cs class file (based on code posted by Brian Scholer) allows for using the REST API with LTM devices with self-signed SSL certificates. Nearly all of the functions require an F5 session object as a parameter, which contains the base URL for the F5 LTM and a credential object for a user with privileges to manipulate the F5 LTM via the REST API. Use the New-F5session function to create this object. This function expects the following parameters: The name or IP address of the F5 LTM device A credential object for a user with rights to use the REST API An optional TokenLifespan value for extending the life of the authentication token past the default 20 minutes You can create a credential object using Get-Credential and entering the username and password at the prompts, or programmatically like this: $secpasswd = ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential "username", $secpasswd Thanks to Kotesh Bandhamravuri and his blog entry for this snippet. There is a function called Test-Functionality that takes an F5Session object, a new pool name, a new virtual server, an IP address for the virtual server, and a computer name as a pool member, and validates nearly all the functions in the module. I've also contributed this code sample for how to gather some basic info about your LTM with this PS module. The module has been tested on: 11.5.1 Build 8.0.175 Hotfix 8 and later 11.6.0 Build 5.0.429 Hotfix 4 and later 12.0 / 12.1 13.0 Code : https://github.com/joel74/POSH-LTM-Rest Tested this on version: 11.520KViews3likes150CommentsFQDN nodes in non-default route domains
Problem this snippet solves: Currently there is no support for FQDN nodes in non-default route domains as per Article. With recent increase in cloud deployment, most of the time there is a requirement from F5 to load balance to servers or ELB in the cloud which has FQDN names as those are having dyanamic IP addresses. If you are using route domains in your BIGIP environment then this becomes a challenge. Below iRule script can be used in those scenerios to use F5 to send traffic to FQDN nodes in non-default route domains. How to use this snippet: If FQDN needs to be resolved by your internal DNS, create performance layer 4 VIP (dns_53) load balancing your DNS servers. DNS server can be used directly in the iRule itself, but it would be better to use a VIP to have redundancy. Create iRule and apply to the VIP Code : when CLIENT_ACCEPTED { set host [RESOLV::lookup @dns_53 "server.example.com"] set ip [getfield $host " " 1] node [lindex $ip 0]%<Rd> 443 } Tested this on version: 14.11.5KViews1like3CommentsDNS Query Name Parsing iRule
Problem this snippet solves: This iRule will extract the DNS Query Name in the absence of a DNS profile being applied to a Virtual Server. How to use this snippet: # This is a shameless rip from an old Devcentral post DNS Hostname Parsing iRule that, to the best of my knowledge, never made it to a Code Share. To use this code, simply apply this to a UDP Virtual Server that processes DNS traffic. (No DNS Profile necessary). Code : when FLOW_INIT { #extract QNAME from QUESTION header #${i} is a sanity check so this logic won't spin on invalid QNAMEs set i 0 #initialize our position in the QNAME parsing and the text QNAME set offset 12 set length 1 set endlength 1 set name "" #/extract QNAME from QUESTION header while {${length} > 0 && ${i} < 10} { #length contains the first part length binary scan [string range [DATAGRAM::udp payload] ${offset} ${offset}]] c foo #make the length an unsigned integer set length [expr {${foo} & 0xff}] if {${length} > 0} { #grab a part and put it in our text QNAME section append name [string range [DATAGRAM::udp payload] [expr {${offset} + 1}] [expr {${offset} + ${length}}]] #Watch the DNS QNAME get built during the loop. Remove the following line for production use. log local0.info "BUILDING DNS NAME: [IP::client_addr] queried ${name} offset ${offset} length ${length}" #grab a part and put it in our text QNAME section set offset [expr {${offset} + ${length} +1}] #endlength contains the Last part length binary scan [string range [DATAGRAM::udp payload] ${offset} ${offset}]] c foo #make the length an unsigned integer set endlength [expr {${foo} & 0xff}] if { ${endlength} > 0} { #put a dot between parts like a normal DNS name append name "." } incr i } } #/extract QNAME from QUESTION header #Input the required action here, where "${name}" is the variable that is reviewed for decision making. #Sample action would be a pool statement. The below log statement should be removed for production use. log local0.info "FINAL DNS NAME: [IP::client_addr] queried ${name}" } Tested this on version: 12.1729Views2likes1CommentSFTP file existence monitor
Problem this snippet solves: SFTP file existence monitor How to use this snippet: This monitor definition allows for a monitor to connect to a SFTP server and check for the existence of a file using username/password. Written for a specific implementation where they wouldn't use key pairs, plus it turns out that curl on F5's was compiled with sftp support disabled, so I had to use expect instead. It's based off of the default sample_monitor. Create a monitor definition with 3 variables: $monitor_sftp_USER = Username of SFTP server $monitor_sftp_PASS = Password for $monitor_sftp_USER $monitor_sftp_STRING` = String/Filename to search for I have also written a modified version whereby you can encrypt the password manually using the unit master-key and add that as the password variable, which I can post if wanted. Code : #!/bin/sh # # (c) Copyright 1996-2006, 2010-2013 F5 Networks, Inc. # # This software is confidential and may contain trade secrets that are the # property of F5 Networks, Inc. No part of the software may be disclosed # to other parties without the express written consent of F5 Networks, Inc. # It is against the law to copy the software. No part of the software may # be reproduced, transmitted, or distributed in any form or by any means, # electronic or mechanical, including photocopying, recording, or information # storage and retrieval systems, for any purpose without the express written # permission of F5 Networks, Inc. Our services are only available for legal # users of the program, for instance in the event that we extend our services # by offering the updating of files via the Internet. # # @(#) $Id: //depot/maint/bigip12.1.1/tm_daemon/monitors/sample_monitor#1 $ # # # these arguments supplied automatically for all external pingers: # $1 = IP (::ffff:nnn.nnn.nnn.nnn notation or hostname) # $2 = port (decimal, host byte order) # # The following must all be set as variables in the monitor definition # $monitor_sftp_USER = Username of SFTP server # $monitor_sftp_PASS = Password for $monitor_sftp_USER # $monitor_sftp_STRING` = String/Filename to search for # # $MONITOR_NAME = name of the monitor # # In this sample script, $3 is the regular expression # # Name of the pidfile pidfile="/var/run/$MONITOR_NAME.$1..$2.pid" # Send signal to the process group to kill our former self and any children # as external monitors are run with SIGHUP blocked if [ -f $pidfile ] then kill -9 -`cat $pidfile` > /dev/null 2>&1 fi echo "$$" > $pidfile # Remove the IPv6/IPv4 compatibility prefix node_ip=`echo $1 | sed 's/::ffff://'` # Using expect and sftp to get directory listing from the server. # Search the data received for the expected string. expect -c " spawn sftp -oStrictHostKeyChecking=no -oPort=$2 $monitor_sftp_USER@$node_ip; expect \"password:\"; send $monitor_sftp_PASS\r; expect \"sftp>\"; send \"ls -l\r\"; expect \"sftp>\"; send \"exit\r\" " | grep $monitor_sftp_STRING > /dev/null status=$? if [ $status -eq 0 ] then # Remove the pidfile before the script echoes anything to stdout and is killed by bigd rm -f $pidfile echo "up" fi # Remove the pidfile before the script ends rm -f $pidfile Tested this on version: 12.1699Views0likes1Comment