Sample Linux script to update CRL file from Certificate Authority
Problem this snippet solves: CRL files are signed lists of revoked serial numbers issued by a specific Certificate Authority (Verisign, Godaddy, GlobalSign, etc). There are several advanced methods of dealing with revoked certificates, the best of which is OCSP stapling. Other methods are OCSP responders or CRL Distribution Points. However, for small or internal projects, some administrators rely on simply using straight-up CRL files. After a time, the administrator will realize he or she needs to automate this process with a script. The administrator can automate this process on the BIG-IP itself with an iCall script. Here's a link to a great example of the iCall solution. However, some administrators many need to use a straight-up Linux device to pull and copy the CRL files around to many different devices, only one of which is the BIG-IP. How to use this snippet: This is a sample of a Linux script that pulls down a CRL file from GoDaddy, verifies it and then copies it to BIG-IP. Ensure that the Linux device can copy files directly to the BIG-IP via ssh-key authentication. Modify the 'f5' variable of the script to point to the BIG-IP. If not using GoDaddy, find the URL of the CRL file for the appropriate CA. If the 'ssl-crl' object hasn't been created on the BIG-IP yet, then it must be done manually the first time. Download the CRL, copy it to the BIG-IP's /var/tmp area. Then login to the BIG-IP and issue the following command: tmsh modify sys file ssl-crl gdcrl source-path file:/var/tmp/CRL After that, the script should work. Code : #!/bin/bash # # script to download a CRL from GoDaddy CA. # See this page for GoDaddy CRL information: # https://certs.godaddy.com/repository # Verify CRL # Convert CRL # Copy to BIG-IP # exit on error to prevent copying corrupt CRL to BIG-IP set -e f5=yourbigip.com f5port=22 crlurl=https://certs.godaddy.com/repository/mastergodaddy2issuing.crl gdcrturl=https://certs.godaddy.com/repository/gdig2.crt gdcrt=gdig2.crt echo "Automated CRL update Script" echo "Downloading from $crlurl" echo "Copying to ${f5}:${f5port}" echo "Last line should be SUCCESS" echo "---- GO ----" if [ ! -f $gdcrt ]; then echo "Fetching GoDaddy Certificate" curl $gdcrturl > $gdcrt fi cf=gdroot.crl last=last.crl if [ -f $cf ]; then if [ ! -s $cf ]; then echo "Found existing zero-length CRL file, deleting" rm -f $cf else echo "Found existing $cf - moving to backup" mv $cf $last fi fi echo "Downloading CRL file $cf" curl $crlurl > $cf echo "Testing $cf for readability" test -f $cf test -r $cf echo "Testing to see if $cf is zero length" test -s $cf if [ -f $last ]; then echo "Testing if $cf is newer than backup file $last" if [ ! $cf -nt $last ]; then echo "File not changed. SUCCESS" fi fi echo "Verifying CRL against certificate, converting to PEM format" openssl crl -inform DER -CAfile $gdcrt -in $cf -outform PEM -out ${cf}.pem echo "Testing PEM for zero length" test -s ${cf}.pem echo "Copying CRL file to bigip" scp -P ${f5port} ${cf}.pem root@${f5}:/var/tmp echo "Importing CRL into system" echo " this may fail if object was never created - replace 'modify' with 'create'" ssh root@${f5} -p ${f5port} "tmsh modify sys file ssl-crl gdcrl source-path file:/var/tmp/${cf}.pem" echo "SUCCESS"1.2KViews1like0CommentsRun tcpdump on event
Problem this snippet solves: This example shows how to trigger an iCall script using a log message as the trigger. In particular, it shows how to watch for messages about pool members failing their monitors, and then running tcpdump to capture some packets sent to that pool member in order to help troubleshoot later. How to use this snippet: Implementation Details This iCall script requires v11.4 or higher. Code : 1. create the following iCall script: modify script tcpdump { app-service none definition { set date [clock format [clock seconds] -format "%Y%m%d%H%M%S"] foreach var { ip port count vlan } { set $var $EVENT::context($var) } exec tcpdump -ni $vlan -s0 -w /var/tmp/${ip}_${port}-${date}.pcap -c $count host $ip and port $port } description none events none } 2. create an event handler for it that looks like the following: sys icall handler triggered tcpdump { script tcpdump subscriptions { tcpdump { event-name tcpdump } } } 3. modify /config/user_alert.conf to contain entries that looks like the following: alert local-http-10-2-80-1-80-DOWN "Pool /Common/my_pool member /Common/10.2.80.1:80 monitor status down" { exec command="tmsh generate sys icall event tcpdump context { { name ip value 10.2.80.1 } { name port value 80 } { name vlan value internal } { name count value 20 } }" } You will need one of these stanzas for each pool member that you want to monitor in this method - or to modify the example to act on different events and different objects.969Views1like0CommentsHealth Score iCall - Dynamic Modification of a Pool Member's Ratio
Problem this snippet solves: Code Use Case: This iApp can be used to control an iCall script that will dynamically modify a pool member's ratio based on the result of a header received from each individual pool member. The iApp exposes a list of typical questions that will in turn modify the iCall scripts behavior without having to dive into the iCall code itself. The most common example and what the original iCall script was written and tested against is SharePoint web servers will return a header called X-SharePointHealthScore with a value ranging from 0-10(0 being best) based on internal monitored metrics. The iApp managed iCall will then look at this value on an interval and apply a ratio to the pool member ranging from 10-1 as the inverse to the score above. This allows for a more in-tune approach to load balancing. Note: This will work against a pool managed by another iApp (tested with SharePoint 2010/2013/2016 iApp) as well as a manually configured pool (or a pool managed by an iApp with strict updates turned off). Link to Original iCall: https://devcentral.f5.com/codeshare/prioritize-sharepoint-nodes-on-reported-health How to use this snippet: Unzip the file, then upload the iApp template to Big-IP. Then create an application service using the template. Code : 67795 Tested this on version: 12.0900Views0likes7CommentsiCall CRL update with Route Domains and Auto-Sync
Problem this snippet solves: iCall script to update CRL file within F5 BIG-IP when the HTTP request must run from a specific Route Domain and also uses logger to write logs to the default LTM location. The original was to also update an iFile of the CRL file for use within an iRule however I have removed that due to it being a very special case (I may add another snippet later to detail that one). Important point here is we update the CRL file located within a folder (or partition) that was linked to a Sync-Only Device Group with auto-sync enabled e.g. CRL files are created and saved to /Common/ crl / This way the iCall script does not need to trigger any sort sync and the rest of the configuration can be left as manual sync. Code : sys icall handler periodic /Common/someCrl-CrlUpdate { arguments { { name rd value 2 } { name url value https://172.31.0.1/somepath/to/crlUpdateFile.crl } { name host value somecrl.CADomein.com } { name folder value tempCrlDirectory } { name sslCrl value /Common/crl/someCrlFile.crl } } interval 600 script /Common/iCallCrlUpdate } sys icall script /Common/iCallCrlUpdate { app-service none definition { set logTag "iCallCrlUpdate" set logLevel "notice" # Getting handler provided arguments foreach arg { rd url host folder sslCrl ifileCrl } { set $arg $EVENT::context($arg) } # Create a directory to save files to disk set crlDir /var/tmp/$folder exec mkdir -p $crlDir exec /bin/logger -i -t $logTag -p local0.$logLevel "Running, CRL URL=$url, Host=$host, SSL CRL=$sslCrl, iFile CRL=$ifileCrl, Directory=$crlDir, rd=$rd" # Download CRL file from provided route domain (rd) and url arguments and save to temporary directory set status [exec /usr/bin/rdexec $rd /usr/bin/curl-apd -s -o $crlDir/LatestCRL.crl -w %{http_code} -H Host:$host $url] if {$status == 200} { # Update F5 SSL CRL file tmsh::modify sys file ssl-crl $sslCrl source-path file:$crlDir/LatestCRL.crl exec /bin/logger -t $logTag -p local0.$logLevel "F5 CRL files update complete." } else { exec /bin/logger -i -t $logTag -p local0.error "Command /usr/bin/rdexec $rd /usr/bin/curl-apd -s -o $crlDir/LatestCRL.crl -w '%{http_code}' -H 'Host: onsitecrl.trustwise.com' $url, failed with status=$status" } } description none events none } Tested this on version: 12.1754Views2likes0CommentsNot working my iCall script...
Problem this snippet solves: Hello~ I created iCall and generate event for working iCall's script. But.... It is not working. list sys icall script change_iRule2 sys icall script change_iRule2 { app-service none definition { tmsh::modify ltm virtual v_198.100__443 rules { vip_snatpool } tmsh::modify ltm virtual v_198.101__443 rules { vip_snatpool } } description none events none } list sys icall handler triggered change_iRule2{ script change_iRule2 subscriptions { change_iRule2 { event-name change_iRule2 } } } I gernerated event generate sys icall event change_iRule2 And I checked bellow status...... Sys::iCall::Event Triggered Handler: change_iRule2 Events matching filters 11 Events causing handler to run 11 Creation time 01/24/17 16:38:35 Current status active Time since last status change 01/24/17 16:38:35 But, Configuration(virtual server's iRule) is not change.... Plz help me. T..T Code : sys icall script change_iRule2 { app-service none definition { tmsh::modify ltm virtual v_198.100__443 rules { vip_snatpool } tmsh::modify ltm virtual v_198.101__443 rules { vip_snatpool } } description none events none } create sys icall handler triggered change_iRule2 script change_iRule2 subscriptions add { change_iRule2 { event-name change_iRule2 } }725Views0likes0CommentsiCall Script that only runs on Active member
Problem this snippet solves: I had a request to run an iCall script only on the active member in a pair. How to use this snippet: This won't work if you're using active/active via traffic-groups. Code : # Only execute if local BIG-IP is active in failover if {[exec cat /var/prompt/ps1] == "Active"} { tmsh::log "I LIKE SOUP!" } Tested this on version: 12.1705Views0likes2CommentsDisable Interface if Pool Member Availability Drops Below Threshold
Problem this snippet solves: This iCall script can be used to disable an interface (int 1.3 in this case) if the member availability of a pool drops below a certain threshold (70% in this example.) How to use this snippet: Implementation Details This iCall script requires v11.4 or higher. The script is called by a periodic handler, but could be converted to a triggered handler with some custom work in /config/user_alert.conf on the pool members. Code : ## Script ## sys icall script poolCheck.v1.0.0 { app-service none definition { set pn "/Common/pool4" set total 0 set usable 0 foreach obj [tmsh::get_status /ltm pool $pn detail] { #puts $obj foreach member [tmsh::get_field_value $obj members] { #puts $member incr total if { [tmsh::get_field_value $member status.availability-state] == "available" && \ [tmsh::get_field_value $member status.enabled-state] == "enabled" } { incr usable } } } if { [expr $usable.0 / $total] < 0.7 } { tmsh::log "Not enough pool members in pool $pn, interface 1.3 disabled" tmsh::modify /net interface 1.3 disabled } else { tmsh::log "Enough pool members in pool $pn, interface 1.3 enabled" tmsh::modify /net interface 1.3 enabled } } description none events none } ## Handler ## sys icall handler periodic poolCheck.v1.0.0 { first-occurrence 2014-09-16:11:00:00 interval 60 script poolCheck.v1.0.0 }573Views0likes5Comments[icall] kill oldest sessions when reaching xx% of the APM license limit
Problem this snippet solves: When dealing with APM authentication, especially when the F5 device act as a SAML 2.0 IDP, the active sessions can increase considerably and easily reach the max access session limit of the license or the device. The following icall script allows an administrator to guarantee that new users can still authenticate through APM IDP under heavy load. The script will kill oldest active access sessions based on the access session consumption. This is a draft that need to be fine tuned. Warning: when using APM Guest on a vCMP host, we are not able to guarantee that the appliance limit is not reached as we just have knowledge of the active sessions within the guest context only. How to use this snippet: TMSH command to create an icall script create sys icall script apm_purge_sessions Then copy/paste the content of the icall script and save it. By default, the command create a script named "apm_purge_sessions". You can easily change the name of the script by modifying "apm_purge_sessions" in the command line. TMSH command to create the icall handler The following command trigger the script every 60 seconds. It can be changed to increase the frequency of the execution of the script. create sys icall handler periodic f5-apm-purge-session interval 60 script apm_purge_sessions Interesting tcl commands used in the script Retrieve the max_access_session variable in the license of the device: [string trim [lindex [split [exec /usr/bin/tmsh show /sys license detail | grep access] " "] 1] "\[\]"] retrieve the ordered list (oldest first) of active APM sessionIDs catch {set output [exec /usr/bin/sessiondump --allkeys | grep starttime | sort -k3 | cut -c1-8]} Use cases kill oldest sessions when reaching xx% of the APM license limit Evolution trigger the icall script based on a specific event (snmptrap, log, ...) sort APM sessions by Access Profile and kill sessions based on the criticity of each AP. Code : # retrieve the ordered list of active APM sessionIDs catch {set output [exec /usr/bin/sessiondump --allkeys | grep starttime | sort -k3 | cut -c1-8]} if {$output != ""} { # move the output to a list of sessionID set output [split $output "\n"] set count [llength $output] # determine the max_access_session allowed for the running platform set max_access [string trim [lindex [split [exec /usr/bin/tmsh show /sys license detail | grep access] " "] 1] "\[\]"] # determine acceptable threshold before triggering set access_threshold [expr round($max_access*0.85)] set diff [expr $count-$access_threshold] # kill oldest APM sessions until reaching 85% of active sessions in the APM device for {set i 0} {$i < $diff} {incr i} { catch { [exec /usr/bin/sessiondump --delete [lindex $output $i]] } } } Tested this on version: 11.6556Views0likes0CommentsFQDN pool members, Least Connections Member SLB with Cookie Persistence in an iCall
Problem this snippet solves: A lightweight SLB solution without LTM Code : sys icall script matt_dns { app-service none definition { # # Get the DNS records from the server and place them in a sorted list set dns_raw [exec bash -c {dig +short matt.f5demo.com @10.128.20.252 A}] #set dns_raw "10.128.20.51\n10.128.20.52\n10.128.20.53\n10.128.20.54" set dns [split $dns_raw "\n"] set dns_sorted [lsort $dns] # Read the prevous DNS result from file, sorted to ensure formating etc if { [catch { set dns_previous_file [open "/config/ifile/current_icall_dns" r] } err] } { set dns_old "" } else { set dns_old [read $dns_previous_file] close $dns_previous_file set dns_sorted_old [lsort $dns_old] } # Writing the current DNS to a file set dns_previous_file [open "/config/ifile/current_icall_dns" w+] puts $dns_previous_file $dns_raw close $dns_previous_file set count 0 foreach a $dns_sorted { set pn "pool_$a" if { [catch { tmsh::show /ltm pool $pn } err] } { # Create Pools if they don't exist tmsh::log "Creating new pool for $a" tmsh::create /ltm pool $pn \{ members add \{ $a:80 \}\} } else { # Enable the Pool member if it does exist (quicker to just enable it than to check if it is disabled) tmsh::modify /ltm pool $pn \{ members modify \{ all \{ session user-enabled \} \} \} } lappend pn_list $pn incr count } # Disable Pools no longer in use foreach b $dns_sorted_old { if {[lsearch -exact $dns_sorted $b]==-1} { tmsh::log "not in current dns list, disabling $b" set old_pn "pool_$b" tmsh::modify /ltm pool $old_pn \{ members modify \{ $b:http \{ session user-disabled \} \} \} # Not too keen to just staight delete these, my thoughts are to have another iCall running every say 20min looking for user-disabled pools (or could use something a particular monitor) and confirming no active sessions and then deleting #tmsh::delete /ltm pool $old_pn } } # If just one pool create a persistence cookie but no SLB (need a persistence record for the case when another member is dynamically added) if { $count eq 1 } { set persist [lindex $pn_list 0] set Ratio_SLB "set selected $persist" } else { # If more than one pool # Work out Total Connections set total 0 foreach a $pn_list { foreach obj [tmsh::get_status /ltm pool $a detail] { set cc [tmsh::get_field_value $obj cur-sessions] #set cc [expr double( { 100 * rand() })] set total [expr double( { $total + $cc })] lappend cc_list $cc } } # Work out cumulative ratios for each pool set percentage 0 incr count -1 set j 0 foreach a $pn_list { if { $total != 0.0 } { set percentage [expr double( { $percentage + ( $total - [lindex $cc_list $j] ) / ( $total * $count ) } ) ] } else { set percentage [expr double( {$percentage + ( 1.0 / ( $count + 1.0 ) ) } ) ] } tmsh::log "$percentage $total $count [lindex $cc_list $j]" # Construct the Dynamic Ratio SLB and persistence components of the iRule # First Pool if { $j eq 0 } { set Ratio_SLB "set Random_num \[expr \{ rand() \}\] if \{ \$Random_num < $percentage \} \{ set selected $a \}" set persist [concat $a " -" \n] # Middle Pools, if there are any } elseif { $j < $count } { set Ratio_tmp " elseif \{ \$Random_num < $percentage \} \{ set selected $a \}" set Ratio_SLB [concat $Ratio_SLB $Ratio_tmp] set persist [concat $persist $a " -" \n] # Last Pool } else { set Ratio_tmp " else \{ set selected $a \}" set Ratio_SLB [concat $Ratio_SLB $Ratio_tmp] set persist [concat $persist $a] } incr j } #tmsh::log "Test $Ratio_SLB" #tmsh::log "$persist" } # Construct the whole iRule set iRuleName "iCall_CookiePersist_RatioSLB" set iRuleCode " when HTTP_REQUEST timing on \{ switch \[HTTP::cookie pool_cookie\] \{ $persist \{ \#Persistence Record Exists pool \[HTTP::cookie pool_cookie\] set selected \"\" \} default \{ \#Load balancing decision required $Ratio_SLB pool \$selected \} \} \} when HTTP_RESPONSE timing on \{ if \{\$selected ne \"\"\} \{ \#set a persistence cookie HTTP::cookie insert name pool_cookie value \$selected path \"/\" \} \} " # Create the iRule # tmsh::log "$iRuleCode" if { [catch { tmsh::modify ltm rule $iRuleName $iRuleCode } err] } { tmsh::create ltm rule $iRuleName $iRuleCode } } description none events none }527Views0likes1CommentLogging pool member name (and not just address)
Problem this snippet solves: I wanted to see a pool member's text name, not just the IP address, in logs. We accomplished this by creating an iCall script that routinely created dynamic data-groups from existing pools, then using an iRule to log the pool member's name based on lookup. How to use this snippet: We had this script run periodically as new pool members were not created/added on a regular basis: createsysicallhandlerperiodiccreate_poolmember_datagroupsinterval86400scriptcreate_poolmember_datagroups The iRule would look in "dynpoolmbrdg-[pool_name]" by IP address and log the corresponding member text name. Code : icall script create_poolmember_datagroups { app-service none definition { # Define variable types set poollist [list] set memberlist [list] set datagrouplist [list] # get a list of current data-groups set dglistraw [tmsh::get_config /ltm data-group internal] foreach datagroup $dglistraw { lappend datagrouplist [tmsh::get_name $datagroup] } # process each pool member in the configuration set poollistraw [tmsh::get_config /ltm pool] foreach pool $poollistraw { # retrieve the pool name and current members set poolname [tmsh::get_name $pool] set memberlist [tmsh::get_field_value $pool members] # Create the pool's data group if it doesn't exist set dyndgname dynpoolmbrdg-$poolname if {[lsearch $datagrouplist $dyndgname] >= 0} { } else { tmsh::create ltm data-group internal $dyndgname type string } # Overwrite the data-group with a list of the current members foreach member $memberlist { set membername [tmsh::get_name $member] set memberaddr [tmsh::get_field_value $member "address"] append payload "$memberaddr { data $membername } " set records "{ $payload }" tmsh::modify ltm data-group internal $dyndgname records replace-all-with $records } } } description none events none } Tested this on version: No Version Found509Views0likes0Comments