i Rules

Log URI Host - Path

when HTTP_REQUEST {

log local0. "This is the HTTP URI [HTTP::uri]"

log local0. "This is the HTTP Host [HTTP::host]"

log local0. "This is the HTTP Path [HTTP::path]"

}


Log all headers


To log all the HTTP Request headers you can use a similar code:

when HTTP_REQUEST {

foreach aHeader [HTTP::header names] {

log local0. "HTTP Request Headers: $aHeader: [HTTP::header value $aHeader]"

}

}

To log specific Request Headers you can use these example actions:

when HTTP_REQUEST {

log local0. "HTTP Method = [HTTP::method]"

log local0. "HTTP URI = [HTTP::uri]"

log local0. "HTTP Path = [HTTP::path]"

log local0. "HTTP Query = [HTTP::query]"

log local0. "HTTP Version = [HTTP::version]"

log local0. "HTTP Host Header = [HTTP::host]"

log local0. "HTTP User Agent Header = [HTTP::header value "user-agent"]"


Display VS Name and IP

when HTTP_REQUEST {

set http_reply "You have reached virtual server [virtual] [IP::local_addr],

This site is a test virtual server."

HTTP::respond 200 content $http_reply

}

Display DNS::question and DNS::answer, IP::client_addr and DNS::origin

ltm rule ltm_log {

when DNS_REQUEST {

log local2. "LTM irule: DNS Requests [DNS::question name] with record type of [DNS::question type] seen from [IP::client_addr]"

}

when DNS_RESPONSE {

log local2. "LTM irule: Client answer was provided by [DNS::origin], with the full response of [DNS::answer]"

}

}

Display DNS::rrtype, DNS::rrname and ip::client_addr

gtm rule gtm_log {

when DNS_REQUEST {

log local2. "GTM irule: A client [IP::client_addr] queried [DNS::rrname] with request type of [DNS::rrtype]"

}

}



TCP logger

when CLIENT_ACCEPTED {

set vip [IP::local_addr]:[TCP::local_port]

}

when SERVER_CONNECTED {

set client "[IP::client_addr]:[TCP::client_port]"

set node "[IP::server_addr]:[TCP::server_port]"

}

when CLIENT_CLOSED {

# log connection info

log local0.info "Client $client -> VIP: $vip -> Node: $node"

}


Security Headers Irule


when HTTP_RESPONSE_RELEASE {

if {!([HTTP::header exists "X-Frame-Options" ])} {

HTTP::header insert "X-Frame-Options" "DENY"

}

if {!([HTTP::header exists "X-XSS-Protection"])} {

HTTP::header insert "X-XSS-Protection" "1; mode=block"

}

if {!([HTTP::header exists "X-Content-Type-Options"])} {

HTTP::header insert "X-Content-Type-Options" "nosniff"

}

if {!([HTTP::header exists "Strict-Transport-Security"])} {

HTTP::header insert "Strict-Transport-Security" "max-age=16070400; includeSubDomains"

}

}

IPI rule


when HTTP_REQUEST {

set ip_reputation_categories [IP::reputation [IP::client_addr]]

set is_reject 0

if {($ip_reputation_categories contains "Windows Exploits")} {

set is_reject 1

}

if {($ip_reputation_categories contains "Web Attacks")} {

set is_reject 1

}

if {($is_reject)} {

log local0. "Attempted access from malicious IP address [IP::client_addr]

($ip_reputation_categories), request was rejected"

HTTP::respond 200 content"<HTML><HEAD><TITLE>Rejected Request</TITLE></HEAD><BODY>The request was rejected. <BR>Attempted access from malicious IP address</BODY></HTML>"

}

}


HTTP test page

when HTTP_REQUEST {

set http_reply "You have reached [HTTP::host],

This site is a test virtual server."

HTTP::respond 200 content $http_reply

}

Log all info

when HTTP_REQUEST {

# set the URL here, log it on the response

set url [HTTP::header Host][HTTP::uri]

set vip [IP::local_addr]:[TCP::local_port]

}


when HTTP_RESPONSE {

set client [IP::client_addr]:[TCP::client_port]

set node [IP::server_addr]:[TCP::server_port]

set nodeResp [HTTP::status]


# log connection info

log local0.info "Client: $client -> VIP:$vip$url -> Node: $node with response $nodeResp"

}


Log all headers

when HTTP_REQUEST {

set LogString "Client [IP::client_addr]:[TCP::client_port] -> [HTTP::host][HTTP::uri]"

log local0. "============================================="

log local0. "============================================="

log local0. "============================================="

log local0. "$LogString (request)"

foreach aHeader [HTTP::header names] {

log local0. "$aHeader: [HTTP::header value $aHeader]"

}

log local0. "============================================="

log local0. "============================================="

log local0. "============================================="

}

when HTTP_RESPONSE {

log local0. "============================================="

log local0. "============================================="

log local0. "============================================="

log local0. "$LogString (response) - status: [HTTP::status]"

foreach aHeader [HTTP::header names] {

log local0. "$aHeader: [HTTP::header value $aHeader]"

}

log local0. "============================================="

log local0. "============================================="

log local0. "============================================="

}


when CLIENTSSL_CLIENTHELLO {

log local0. [IP::client_addr]

log local0. [SSL::cipher name]

log local0. [SSL::cipher version]

}


HSTS

#FP DR

when HTTP_RESPONSE {

HTTP::cookie secure "JSESSIONID" enable

set ck [HTTP::header values "Set-Cookie"]

HTTP::header remove "Set-Cookie"

foreach acookie $ck {

if {$acookie starts_with "JSESSIONID"} {

HTTP::header insert "Set-Cookie" "${acookie}; HttpOnly; SameSite=none "

} else {

HTTP::header insert "Set-Cookie" "${acookie}; HttpOnly; SameSite=none "

}

}

}

Rewrite Host Header

# This iRule rewrites the host header

# in requests and responses based

# on the contents of a datagroup.


# Mapping external fqdns to internal hostnames

# and vice versa


# Datagroups: host_header_rewrite_request_dg

# host_header_rewrite_response_dg


#v1 20161123 - first cut

#v2 20161206 - added response dg and updated logic


when HTTP_REQUEST {

set requestedHost [HTTP::host]


#log local0. "HOST is $requestedHost"

#log local0. "URI is [HTTP::uri]"


if { [class match $requestedHost equals host_header_rewrite_request_dg] } {

HTTP::header replace Host [class lookup $requestedHost host_header_rewrite_request_dg]


#log local0. "Rewriting Host header from $requestedHost -> [class lookup $requestedHost host_header_rewrite_request_dg]"

}

}


when HTTP_RESPONSE {

if { [HTTP::is_redirect] } {


#log local0. "Location before [HTTP::header Location]"


# This assumes absolute urls in Location header. May need to cater for relative in future.

set locationFQDN [getfield [HTTP::header Location] "/" 3]


#log local0. "locationFQDN is $locationFQDN"


if { [class match $locationFQDN equals host_header_rewrite_response_dg] } {

HTTP::header replace Location [string map -nocase "$locationFQDN [class lookup $locationFQDN host_header_rewrite_response_dg]" [HTTP::header Location]]

}

#log local0. "Location after [HTTP::header Location]"

}

}


IP Block

iRule to allow defined list of Test Engineers to connect to the VIP as normal but block everyone not in the datagroup and return a 503


when CLIENT_ACCEPTED {

set disallowed 0

if { [class match [IP::client_addr] eq "client_allow_dg" ] }{

log local0. "Client [IP::client_addr]:[TCP::client_port] traffic is allowed. Client IP match found in client_allow_dg"

# return #return means to exit thecurrent event i.e. CLIENT_ACCEPTED EVENT

} else {

log local0. "Client [IP::client_addr]:[TCP::client_port] traffic is not allowed. Client IP match not found in client_allow_dg"

set disallowed 1

}

}



when HTTP_REQUEST {

if {$disallowed == 1}{

set vip [IP::local_addr]

set uri [HTTP::uri]


log local0. "Client [IP::client_addr]:[TCP::client_port] connected to VS ($vip)for URI $uri"

HTTP::respond 503 noserver

log local0. "A 503 was Returned"

}

}


TLS Decrypt

when CLIENTSSL_HANDSHAKE {

if {[IP::addr [IP::client_addr] equals 192.168.0.16] }

{

log local0. "TCP source port: [TCP::remote_port]"

log local0. "RSA Session-ID:[SSL::sessionid] Master-Key:[SSL::sessionsecret]"

}

}

when SERVERSSL_HANDSHAKE {

log local0. "TCP Source port: [TCP::local_port]"

log local0. "RSA Session-ID:[SSL::sessionid] Master-Key:[SSL::sessionsecret]"

}


MAC address check


when ACCESS_POLICY_AGENT_EVENT {

if { [ACCESS::policy agent_id] eq "chkmac" } {

set mac [ACCESS::session data get "session.client.mac_address" ]

log local0.info "$mac***"

if { [class match $mac equals macDG] } {

ACCESS::session data set "session.logon.custom.chkmac" 1

} else {

ACCESS::session data set "session.logon.custom.chkmac" 0

}

}

}


Sorry / Maintenance page


when HTTP_REQUEST {

if { [active_members [LB::server pool]] == 0 }

{ set http_reply "You have reached [HTTP::host],

This site is down

."

HTTP::respond 200 content $http_reply

}

}

---------------------**********------------

when HTTP_REQUEST {

if {[active_members [LB::server pool]] < 1} {


switch [HTTP::uri] {

"/mylogo.png" {HTTP::respond 200 content [ifile get "mylogo.png"] }


default {HTTP::respond 200 content [ifile get "mypage.html"] }

}

}

}

Limit connections Queuing systems


https://devcentral.f5.com/s/articles/Mitigate-Unplanned-Scale-Issues-with-an-iRule-Waiting-Room


when HTTP_REQUEST {

## Check if host name match otherwise exit.

## This is needed if you have multiple websites running on same Virtual Server

if {[HTTP::host] eq "test.test.local"} {

# waiting room js file, only necessary if you want a canvas animation

if { [HTTP::uri] eq "/bb.js"} {

HTTP::respond 200 content [ifile get bb.js]

TCP::close

return

}

## Set variable

#Your website (unique)shortcode, needed to divide multiple online waiting room iRules on same Virtual Server.

#In this example the shortcode is SITE1

set OWR SITE1

# Max visitor count

# How many concurrent visitors can you serve

set max_visitors 2

# Timeout in seconds

# IdleTimeout value is based your cart ideltimeout value. Must at least be equal to your cart IdleTimeout value.

set IdleTimeout 60

set WaitingRoomTimeout 60

# Decide vistors IP address. Visitors behind a proxy are seen for one visitor.

if { ([HTTP::header exists "True-Client-IP"]) and ([HTTP::header "True-Client-IP"] != "") } {

set Client_IP [HTTP::header "True-Client-IP"]

} else {

set Client_IP [IP::client_addr]

}

# Defining Tables

set VisitorsTable VisitorsTable-$OWR-$max_visitors

set WaitingRoomTable WaitingRoom-$OWR-$max_visitors

# Generic

set unique_id [format "%08d" [expr { int(100000000000 * rand()) }]]

set request_uri [HTTP::host][HTTP::uri]

set BYPASS $OWR-bypass-url-list

# Counters

set VisitorCount [table keys -subtable $VisitorsTable -count]

set WaitingRoomCount [table keys -subtable $WaitingRoomTable -count]

set TotalVisitors [expr {$VisitorCount + $WaitingRoomCount}]

## End Variable

## Monitoring

# Allow monitoring from internal IP's or subnets.

if { ($Client_IP starts_with "192.168.102") && ([HTTP::uri] equals "/getcount") } {

HTTP::respond 200 content "Total Visitors: \[$TotalVisitors\]

Max Visitors: \[$max_visitors\]

Waiting Room Count: \[$WaitingRoomCount\]"

TCP::close

return

}

## Start WaitingRoom

# Check if the visitor session still exists

set VisitorSession [table lookup -subtable $VisitorsTable $Client_IP]

if { $VisitorSession != "" } {

# We have a valid session... The lookup has reset the timer on it so just finish processing

} else {

# No valid session...

# Check if BYPASS URL

set bypass_url [class match -value [HTTP::uri] contains $BYPASS]

if { not ($bypass_url == "") } {

# BYPASS, do nothing

} else {

# NOT BYPASS, Check connection count for displaying WR Page

# So do we have a free 'slot'?

if {$VisitorCount < $max_visitors} {

# Yes we have a free slot... Allocate it..

# Register visitor

table add -subtable $VisitorsTable $Client_IP $unique_id $IdleTimeout

} else {

# Max visitors limit reached, show WaitingRoom

# Insert visitor into WaitingRoomTable

table add -subtable $WaitingRoomTable $Client_IP $unique_id $WaitingRoomTimeout

# Show waiting Room HTML

HTTP::respond 503 content [ifile get waitingroom.html]

}

TCP::close

}

}

}

}

From <https://devcentral.f5.com/s/articles/Mitigate-Unplanned-Scale-Issues-with-an-iRule-Waiting-Room>

1. <html>

2. <head>

3. <meta http-equiv="refresh" content="60">

4. <title>Online Waiting Room</title>

5. <style>

6. </style>

7. </head>

8. <script>

9. var surface;

10. var happy;

11. var x = 25;

12. var y = 25;

13. var dirX = 1;

14. var dirY = 1;

15.  

16. function drawCanvas() {

17. // Get our Canvas element

18. surface = document.getElementById("myCanvas");

19. if (surface.getContext) {

20. // If Canvas is supported, load the image

21. dcjp = new Image();

22. dcjp.onload = loadingComplete;

23. dcjp.src = "dcjp_50px.png";

24. }

25. }

26.  

27. function loadingComplete(e) {

28. // When the image has loaded begin the loop

29. setInterval(loop, 5);

30. }

31.  

32. function loop() {

33. // Each loop we move the image by altering its x/y position

34. // Grab the context

35. var surfaceContext = surface.getContext('2d');

36. // Draw the image

37. surfaceContext.drawImage(dcjp, x, y);

38.  

39. x += dirX;

40. y += dirY;

41.  

42. if (x <= 0 || x > 700 - 25) {

43. dirX = -dirX;

44. }

45. if (y <= 0 || y > 350 - 40) {

46. dirY = -dirY;

47. }

48. }

49. </script>

50.  

51. <body onload="drawCanvas();">

52. <center>

53. <h2>Online Waiting Room</h2>

54. <h3>Hey there...sorry about the wait!</h3>

55. <p>We currently have an exceptionally large number of visitors on the site and you are in the queue.</p>

56. <p>Please hold tight, it should only be a few minutes. Make sure you stay on this page. Be mesmerized below, and you will be automatically redirected shortly.</p>

57. <div>

58. <canvas id="myCanvas" width="700" height="350">

59. <p>Your browser doesn't support canvas.</p>

60. </canvas>

61. </div><br>

62. </center>

63. </body>

64. </html>

From <https://devcentral.f5.com/s/articles/Mitigate-Unplanned-Scale-Issues-with-an-iRule-Waiting-Room>

Set HTTP only and samesite

#FP DR

when HTTP_RESPONSE {

HTTP::cookie secure "JSESSIONID" enable

set ck [HTTP::header values "Set-Cookie"]

HTTP::header remove "Set-Cookie"

foreach acookie $ck {

if {$acookie starts_with "JSESSIONID"} {

HTTP::header insert "Set-Cookie" "${acookie}; HttpOnly; SameSite=none "

} else {

HTTP::header insert "Set-Cookie" "${acookie}; HttpOnly; SameSite=none "

}

}


IPI iRule

when HTTP_REQUEST {

set ip_reputation_categories [IP::reputation [IP::client_addr]]

set is_reject 0

if {($ip_reputation_categories contains "Windows Exploits")} {

set is_reject 1

}

if {($ip_reputation_categories contains "Web Attacks")} {

set is_reject 1

}

if {($ip_reputation_categories contains "Infected Sources")} {

set is_reject 1

}

if {($ip_reputation_categories contains "Tor Proxies")} {

set is_reject 1

}

if {($ip_reputation_categories contains "Denial-of-Service")} {

set is_reject 1

}

if {($ip_reputation_categories contains "Scanners")} {

set is_reject 1

}

if {($ip_reputation_categories contains "BotNets")} {

set is_reject 1

}

if {($is_reject)} {

log local0. "Attempted access from malicious IP address [IP::client_addr]

($ip_reputation_categories), request was rejected"

HTTP::respond 200 content "<HTML><HEAD><TITLE>Rejected Request</TITLE></HEAD><BODY>The request was rejected. <BR>Attempted access from malicious IP address</BODY></HTML>"

}

}


*****

when CLIENT_ACCEPTED {

log local0. "IP Intelligence for IP address [IP::client_addr]:

[IP::reputation [IP::client_addr]]"

}

Client IP- RSA key

when CLIENTSSL_HANDSHAKE {


if {[IP::addr [IP::client_addr] equals 10.10.10.10] } {

log local0. "========CLIENT SIDE==================="

log local0. "TCP source port: [TCP::remote_port]"

log local0. "Master-Key:[SSL::sessionsecret]"


log local0. "[TCP::client_port] :: RSA Session-ID:[SSL::sessionid] Master-Key:[SSL::sessionsecret]"

log local0. "======================================"

log local0. " "

}

}



rewrites the host header

# This iRule rewrites the host header

# in requests and responses based

# on the contents of a datagroup.

# Mapping external fqdns to internal hostnames

# and vice versa

# Datagroups: host_header_rewrite_request_dg

# host_header_rewrite_response_dg


when HTTP_REQUEST {

set requestedHost [HTTP::host]

#log local0. "HOST is $requestedHost"

#log local0. "URI is [HTTP::uri]"

if { [class match $requestedHost equals host_header_rewrite_request_dg] } {

HTTP::header replace Host [class lookup $requestedHost host_header_rewrite_request_dg]

#log local0. "Rewriting Host header from $requestedHost -> [class lookup $requestedHost host_header_rewrite_request_dg]"

}

}

when HTTP_RESPONSE {

if { [HTTP::is_redirect] } {

#log local0. "Location before [HTTP::header Location]"

# This assumes absolute urls in Location header. May need to cater for relative in future.

set locationFQDN [getfield [HTTP::header Location] "/" 3]

#log local0. "locationFQDN is $locationFQDN"

if { [class match $locationFQDN equals host_header_rewrite_response_dg] } {

HTTP::header replace Location [string map -nocase "$locationFQDN [class lookup $locationFQDN host_header_rewrite_response_dg]" [HTTP::header Location]]

}

#log local0. "Location after [HTTP::header Location]"

}

}


Waiting Room


when HTTP_REQUEST {

## Check if host name match otherwise exit.

## This is needed if you have multiple websites running on same Virtual Server

if {[HTTP::host] eq "192.168.51.132"} {

# waiting room js file, only necessary if you want a canvas animation

if { [HTTP::uri] eq "/nhs.png"} {

HTTP::respond 200 content [ifile get nhs.png]

TCP::close

return

}

## Set variable

#Your website (unique)shortcode, needed to divide multiple online waiting room iRules on same Virtual Server.

#In this example the shortcode is SITE1

set OWR SITE1

# Max visitor count

# How many concurrent visitors can you serve

set max_visitors 1

# Timeout in seconds

# IdleTimeout value is based your cart ideltimeout value. Must at least be equal to your cart IdleTimeout value.

set IdleTimeout 60

set WaitingRoomTimeout 60

# Decide vistors IP address. Visitors behind a proxy are seen for one visitor.

if { ([HTTP::header exists "True-Client-IP"]) and ([HTTP::header "True-Client-IP"] != "") } {

set Client_IP [HTTP::header "True-Client-IP"]

} else {

set Client_IP [IP::client_addr]

}

# Defining Tables

set VisitorsTable VisitorsTable-$OWR-$max_visitors

set WaitingRoomTable WaitingRoom-$OWR-$max_visitors

# Generic

set unique_id [format "%08d" [expr { int(100000000000 * rand()) }]]

set request_uri [HTTP::host][HTTP::uri]

set BYPASS $OWR-bypass-url-list

# Counters

set VisitorCount [table keys -subtable $VisitorsTable -count]

set WaitingRoomCount [table keys -subtable $WaitingRoomTable -count]

set TotalVisitors [expr {$VisitorCount + $WaitingRoomCount}]

## End Variable

## Monitoring

# Allow monitoring from internal IP's or subnets.

if { ($Client_IP starts_with "192.168.") && ([HTTP::uri] equals "/getcount") } {

HTTP::respond 200 content "Total Visitors: \[$TotalVisitors\]

Max Visitors: \[$max_visitors\]

Waiting Room Count: \[$WaitingRoomCount\]"

TCP::close

return

}

## Start WaitingRoom

# Check if the visitor session still exists

set VisitorSession [table lookup -subtable $VisitorsTable $Client_IP]

if { $VisitorSession != "" } {

# We have a valid session... The lookup has reset the timer on it so just finish processing

} else {

# No valid session...

# Check if BYPASS URL

set bypass_url [class match -value [HTTP::uri] contains $BYPASS]

if { not ($bypass_url == "") } {

# BYPASS, do nothing

} else {

# NOT BYPASS, Check connection count for displaying WR Page

# So do we have a free 'slot'?

if {$VisitorCount < $max_visitors} {

# Yes we have a free slot... Allocate it..

# Register visitor

table add -subtable $VisitorsTable $Client_IP $unique_id $IdleTimeout

} else {

# Max visitors limit reached, show WaitingRoom

# Insert visitor into WaitingRoomTable

table add -subtable $WaitingRoomTable $Client_IP $unique_id $WaitingRoomTimeout

# Show waiting Room HTML

HTTP::respond 503 content [ifile get waitingroom.html]

}

TCP::close

}

}

}

}


Upload iFiles

png file - example nhs.png

and

waiting-room.html

when HTTP_REQUEST {

## Check if host name match otherwise exit.

## This is needed if you have multiple websites running on same Virtual Server

if {[HTTP::host] eq "192.168.51.132"} {

# waiting room js file, only necessary if you want a canvas animation

if { [HTTP::uri] eq "/nhs.png"} {

HTTP::respond 200 content [ifile get nhs.png]

TCP::close

return

}

## Set variable

#Your website (unique)shortcode, needed to divide multiple online waiting room iRules on same Virtual Server.

#In this example the shortcode is SITE1

set OWR SITE1

# Max visitor count

# How many concurrent visitors can you serve

set max_visitors 1

# Timeout in seconds

# IdleTimeout value is based your cart ideltimeout value. Must at least be equal to your cart IdleTimeout value.

set IdleTimeout 60

set WaitingRoomTimeout 60

# Decide vistors IP address. Visitors behind a proxy are seen for one visitor.

if { ([HTTP::header exists "True-Client-IP"]) and ([HTTP::header "True-Client-IP"] != "") } {

set Client_IP [HTTP::header "True-Client-IP"]

} else {

set Client_IP [IP::client_addr]

}

# Defining Tables

set VisitorsTable VisitorsTable-$OWR-$max_visitors

set WaitingRoomTable WaitingRoom-$OWR-$max_visitors

# Generic

set unique_id [format "%08d" [expr { int(100000000000 * rand()) }]]

set request_uri [HTTP::host][HTTP::uri]

set BYPASS $OWR-bypass-url-list

# Counters

set VisitorCount [table keys -subtable $VisitorsTable -count]

set WaitingRoomCount [table keys -subtable $WaitingRoomTable -count]

set TotalVisitors [expr {$VisitorCount + $WaitingRoomCount}]

## End Variable

## Monitoring

# Allow monitoring from internal IP's or subnets.

if { ($Client_IP starts_with "192.168.") && ([HTTP::uri] equals "/getcount") } {

HTTP::respond 200 content "Total Visitors: \[$TotalVisitors\]

Max Visitors: \[$max_visitors\]

Waiting Room Count: \[$WaitingRoomCount\]"

TCP::close

return

}

## Start WaitingRoom

# Check if the visitor session still exists

set VisitorSession [table lookup -subtable $VisitorsTable $Client_IP]

if { $VisitorSession != "" } {

# We have a valid session... The lookup has reset the timer on it so just finish processing

} else {

# No valid session...

# Check if BYPASS URL

set bypass_url [class match -value [HTTP::uri] contains $BYPASS]

if { not ($bypass_url == "") } {

# BYPASS, do nothing

} else {

# NOT BYPASS, Check connection count for displaying WR Page

# So do we have a free 'slot'?

if {$VisitorCount < $max_visitors} {

# Yes we have a free slot... Allocate it..

# Register visitor

table add -subtable $VisitorsTable $Client_IP $unique_id $IdleTimeout

} else {

# Max visitors limit reached, show WaitingRoom

# Insert visitor into WaitingRoomTable

table add -subtable $WaitingRoomTable $Client_IP $unique_id $WaitingRoomTimeout

# Show waiting Room HTML

HTTP::respond 503 content [ifile get waitingroom.html]

}

TCP::close

}

}

}

}



Tcpdump TLS/SSL Traffic


ref: https://www.notion.so/TMSH-Commands-5e5158192243480391cbabb4701ee99c


First you will have to attach an iRule that captures key material when the handshake is taking place. To make sure you see the handshake on the capture, ALWAYS use a fresh incognito/private browser window on the testing client.


It's recommended that you duplicate your virtual-server and configure everything the same way (use the same pool, policies, profiles) but add the source address, so you can better direct your testing. You can just use the virtual-server you're having trouble but you will need to attach an iRule to it. In some environments this could be not possible, so creating a duplicate VS with the source-address of the testing client takes advantage of the order of precedence of a virtual-server: https://support.f5.com/csp/article/K14800


Create an iRule as detailed on the next block of code. This iRule captures client-ssl and server-ssl. If your virtual-server uses just one or the other, use the specific block on the iRule


when CLIENTSSL_HANDSHAKE {

if { [IP::addr [getfield [IP::client_addr] "%" 1] equals <client_IP_addr>] } {

log local0. "[TCP::client_port] :: RSA Session-ID:[SSL::sessionid] Master-Key:[SSL::sessionsecret]"

}

}


when SERVERSSL_HANDSHAKE {

if { [IP::addr [getfield [IP::client_addr] "%" 1] equals <client_IP_addr>] } {

log local0. "[TCP::client_port] :: RSA Session-ID:[SSL::sessionid] Master-Key:[SSL::sessionsecret]"

}

}


After the iRule is attached to your virtual-server, capture the traffic as follows, specifing the client's IP, for example 192.168.70.25.


tcpdump -nni 0.0:nnnp host 192.168.70.25 -s0 -w /var/tmp/app_tls.pcap -v -c 5000


The iRule will log to your /var/log/ltm some lines with the TLS/SSL. You will need those lines to open the capture inside Wireshark. You can use the following command to output just that information to a file to make your life easier.

tcpdump -nni 0.0:nnnp host 192.168.70.25 -s0 -w /var/tmp/app_tls.pcap -v -c 5000


Finally, grab the "sessionsecrets.pms" file using WinSCP or SCP and open the capture inside Wireshark and point to the sessionsecrets.pms.


**-nni** —> Do not resolve names and MACs and use the following interface

**0.0:nnnp** —> 0.0 is BIG-IP's internal alias to any interface. :nnnp injects internal TMM information with all details and p flag marks the flow so BIG-IP can capture the server-side

**host 192.168.70.25** —> Observe packet with IP address 192.168.70.25

**-s0** —> Unlimited snap lenght. Prevent packets getting truncated. Usefull for HTTP traffic with lot's of headers. As a rule of thumb, always use when capturing to a file

**-w /var/tmp/mother.pcap** —> Output to a file on the specified directory. WinSCP or SCP to the specified directory to download the file and open it on Wireshark

**-v** —> Tcpdump will let you know how many packet it capture right there on the terminal

**-c 5000** —> Capture the first 5000 packets an then stop. It's recommend to use a stop if you are capturing a heavy application. You can change this number to anything you like


The following article explains the :p modifier in further details: [https://support.f5.com/csp/article/K20233108](https://support.f5.com/csp/article/K20233108)


There is great DevCentral article by Rodrigo Albuquerque that explains the entire process: [https://devcentral.f5.com/s/articles/Decrypting-TLS-traffic-on-BIG-IP](https://devcentral.f5.com/s/articles/Decrypting-TLS-traffic-on-BIG-IP)


Some users reported issues while using the ":p" modifier. There's a F5 Article about it: [https://support.f5.com/csp/article/K13637](https://support.f5.com/csp/article/K13637)

Spamhouse IP list

cd /shared/

mkdir scripts