programmability contest
12 TopicsAPM Sharepoint authentication
Problem this snippet solves: Updated version to support Webdav with windows explorer after Nicolas's comment. APM is a great authentication service but it does it only with forms. The default behavior is to redirect user to /my.policy to process VPE. this redirect is only supported for GET method. Sharepoint provide 3 different access types: browsing web site with a browser Editing documents with Office browser folder with webdav client (or editing documents with libreoffice through webdav protocol) This irule display best authentication method for each of these access types: browsers authenticate with default authentication method (form based authentication) Microsoft office authenticate with Form based authentication (with support of MS-OFBA protocol) Libreoffice and webdav clients authenticate with 401 basic authentication Form based authentication (browser and Microsoft office) is compatible (validated for one customer) with SAML authentication Editing documents is managed with a persistent cookie expiring after 5 minutes. to be shared between IE and Office, it requires : cookie is persistent (expiration date instead of deleted at the end of session) web site defined as "trusted sites" in IE. How to use this snippet: install this irule and enable it on the VS. Code : when RULE_INIT { array set static::MSOFBA { ReqHeader "X-FORMS_BASED_AUTH_REQUIRED" ReqVal "/sp-ofba-form" ReturnHeader "X-FORMS_BASED_AUTH_RETURN_URL" ReturnVal "/sp-ofba-completed" SizeHeader "X-FORMS_BASED_AUTH_DIALOG_SIZE" SizeVal "800x600" } set static::ckname "MRHSession_SP" set static::Basic_Realm_Text "SharePoint Authentication" } when HTTP_REQUEST { set apmsessionid [HTTP::cookie value MRHSession] set persist_cookie [HTTP::cookie value $static::ckname] set clientless_mode 0 set form_mode 0 # Identify User-Agents type if {[HTTP::header exists "X-FORMS_BASED_AUTH_ACCEPTED"] && (([HTTP::header "X-FORMS_BASED_AUTH_ACCEPTED"] equals "t") || ([HTTP::header "X-FORMS_BASED_AUTH_ACCEPTED"] equals "f"))} { set clientless_mode 0; set form_mode 1 } else { switch -glob [string tolower [HTTP::header "User-Agent"]] { "*microsoft-webdav-miniredir*" { set clientless_mode 1 } "*microsoft data access internet publishing provider*" - "*office protocol discovery*" - "*microsoft office*" - "*non-browser*" - "msoffice 12*" { set form_mode 1 } "*mozilla/4.0 (compatible; ms frontpage*" { if { [ string range [getfield [string tolower [HTTP::header "User-Agent"]] "MS FrontPage " 2] 0 1] > 12 } { set form_mode 1 } else { set clientless_mode 1 } } "*mozilla*" - "*opera*" { set clientless_mode 0 } default { set clientless_mode 1 } } } if { $clientless_mode || $form_mode } { if { [HTTP::cookie exists "MRHSession"] } {set apmstatus [ACCESS::session exists -state_allow $apmsessionid]} else {set apmstatus 0} if { !($apmstatus) && [HTTP::cookie exists $static::ckname] } {set apmpersiststatus [ACCESS::session exists -state_allow $persist_cookie]} else {set apmpersiststatus 0} if { ($apmpersiststatus) && !($apmstatus) } { # Add MRHSession cookie for non browser user-agent first request and persistent cookie present if { [catch {HTTP::cookie insert name "MRHSession" value $persist_cookie} ] } {log local0. "[IP::client_addr]:[TCP::client_port] : TCL error on HTTP cookie insert MRHSession : URL : [HTTP::host][HTTP::path] - Headers : [HTTP::request]"} else {return} } } else { return } if { $clientless_mode && !($apmstatus)} { if { !([HTTP::header Authorization] == "") } { set clientless(insert_mode) 1 set clientless(username) [ string tolower [HTTP::username] ] set clientless(password) [HTTP::password] binary scan [md5 "$clientless(password)"] H* clientless(hash) set user_key "$clientless(username).$clientless(hash)" set clientless(cookie_list) [ ACCESS::user getsid $user_key ] if { [ llength $clientless(cookie_list) ] != 0 } { set clientless(cookie) [ ACCESS::user getkey [ lindex $clientless(cookie_list) 0 ] ] if { $clientless(cookie) != "" } { HTTP::cookie insert name MRHSession value $clientless(cookie) set clientless(insert_mode) 0 } } if { $clientless(insert_mode) } { HTTP::header insert "clientless-mode" 1 HTTP::header insert "username" $clientless(username) HTTP::header insert "password" $clientless(password) } unset clientless } else { HTTP::respond 401 WWW-Authenticate "Basic realm=\"$static::Basic_Realm_Text\"" Set-Cookie "MRHSession=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/" Connection close return } } elseif {$form_mode && !($apmstatus) && !([HTTP::path] equals $static::MSOFBA(ReqVal))}{ HTTP::respond 403 -version "1.1" noserver \ $static::MSOFBA(ReqHeader) "https://[HTTP::host]$static::MSOFBA(ReqVal)" \ $static::MSOFBA(ReturnHeader) "https://[HTTP::host]$static::MSOFBA(ReturnVal)" \ $static::MSOFBA(SizeHeader) $static::MSOFBA(SizeVal) \ "Connection" "Close" return } } when HTTP_RESPONSE { # Insert persistent cookie for html content type and private session if { [HTTP::header "Content-Type" ] contains "text/html" } { HTTP::cookie remove $static::ckname HTTP::cookie insert name $static::ckname value $apmsessionid path "/" HTTP::cookie expires $static::ckname 120 relative HTTP::cookie secure $static::ckname enable } # Insert session cookie if session was recovered from persistent cookie if { ([info exists "apmpersiststatus"]) && ($apmpersiststatus) } { HTTP::cookie insert name MRHSession value $persist_cookie path "/" HTTP::cookie secure MRHSession enable } } when ACCESS_SESSION_STARTED { if {([info exists "clientless_mode"])} { ACCESS::session data set session.clientless $clientless_mode } if { [ info exists user_key ] } { ACCESS::session data set "session.user.uuid" $user_key } } when ACCESS_POLICY_COMPLETED { if { ([info exists "clientless_mode"]) && ($clientless_mode) && ([ACCESS::policy result] equals "deny") } { ACCESS::respond 401 noserver WWW-Authenticate "Basic realm=$static::Basic_Realm_Text" Connection close ACCESS::session remove } } when ACCESS_ACL_ALLOWED { switch -glob [string tolower [HTTP::path]] { "/sp-ofba-form" { ACCESS::respond 302 noserver Location "https://[HTTP::host]$static::MSOFBA(ReturnVal)" } "/sp-ofba-completed" { ACCESS::respond 200 content { Authenticated Good Work, you are Authenticated } noserver } "*/signout.aspx" { # Disconnect session and redirect to APM logout Page ACCESS::respond 302 noserver Location "/vdesk/hangup.php3" return } "/_layouts/accessdenied.aspx" { # Disconnect session and redirect to APM Logon Page if {[string tolower [URI::query [HTTP::uri] loginasanotheruser]] equals "true" } { ACCESS::session remove ACCESS::respond 302 noserver Location "/" return } } default { # No Actions } } } Tested this on version: 11.54.8KViews1like57CommentsF5 Automation - TCL & Bash
Problem this snippet solves: This is a really simple way to automate CLI command execution on multiple F5 devices using Bash & TCL scripting. How to use this snippet: On a linux machine that is utilized to connect to the F5 device: Create a directory mkdir F5_Check Within the "F5_Check" directory, create the following 3 files: F5_Host.txt (This file contains F5's IP address) F5_Bash_v1 (This is the bash script used to collect username/password for F5) F5_Out_v1.exp (This is the TCL script executes the relevant commands on F5) Explanation of the 3 files: File Content: F5_Out_v1.exp is provided as code share. This is the main TCL script that is utiliezd to execute CLI commands on multiple F5 devices. File Content: F5_Bash_v1 #!/bin/bash # Collect the username and password for F5 access echo -n "Enter the username " read -s -e user echo -ne '\n' echo -n "Enter the password " read -s -e password echo -ne '\n' # Feed the expect script a device list & the collected username & passwords for device in `cat ~/F5_Check/F5_Host.txt`; do ./F5_Out_v1.exp $device $password $user ; done File Contents: F5_Host.txt This contains the management IP of the F5 devices. Example: cat F5_Host.txt 10.12.12.200 10.12.12.201 10.12.12.202 10.12.12.203 Code : #!/usr/bin/expect -f # Set variables set hostname [lindex $argv 0] set password [lindex $argv 1] set username [lindex $argv 2] # Log results log_file -a ~/F5_Check/F5LOG.log # Announce which device we are working on and the time send_user "\n" send_user ">>>>> Working on $hostname @ [exec date] <<<<<\n" send_user "\n" # SSH access to device spawn ssh $username@$hostname expect { "no)? " { send "yes\n" expect "*assword: " sleep 1 send "$password\r" } "*assword: " { sleep 1 send "$password\r" } } expect "(tmos)#" send "sys\n" expect "(tmos.sys)#" send "show software\n" expect "#" send "exit\n" expect "#" send "quit\n" expect ":~\$" exit Tested this on version: 11.51.8KViews0likes2CommentsAutomate F5 Initial Setup - iControl & Ansible
Problem this snippet solves: While everyone loves F5, we all know the initial system setup, networking components and device service cluster is a tedious process. This simple Ansible playbook will allow you to automate the entire F5 initial setup by reading a CSV file and leave you with a ready to go active/standby pair. This does include setting up - NTP, DNS, Hostname, LACP, dot1q, Self-IPs, device trust, configuration sync, etc How to use this snippet: How to Use Required Items Ansible (tested on version 2.1) Blank pair of F5s with management IP configured (version 12.0 & 12.1) Install Ansible if Needed Official Ansible Install Guide Great 3rd Party Install Guide Download and Run F5 Ansible Setup Playbook - f5_ansible_setup.yml Please run the following Ansible Playbook. This will download the required modules, playbook for F5 Initial Setup and example CSV file. Be sure to run this playbook from ~/ansible/playbooks/ F5 Ansible Setup Playbook Fill Out CSV File - f5_initial_setup.csv Use the example CSV file as an example to fit to your environment. Using the CSV file allows you to not have to edit the actual F5 Initial Setup Playbook. This was tested on a pair of 5200v's with so adjust interfaces as needed. The CSV file will be automatically downloaded from GitHub when you run the F5 Ansible Install Playbook. Run F5 Initial Setup Playbook - f5_initial_setup.yml Once you have edited the CSV file to your needs, run the F5 Initial Setup Playbook. This playbook will read the CSV file and configure the two F5 devices from scratch. When everything completes, you should be left with an active/standby pair of devices ready to go! If you want to manually install the Ansible Playbook & Modules, please check out - GitHub Code : https://github.com/mwallco/f5_ansible Tested this on version: 12.01.6KViews0likes15CommentsiRule Event Order
Problem this snippet solves: It's sometimes hard which event is triggered and in which order. This code provide visibility on the event order and time between each event in millisecond. Possible (but not limited to) use cases : Troubleshooting connection Identify event triggered for specific requests learn the order of irule events How to use this snippet: Installation irule You need to install the irule on the top of the list in your Virtual Server. Special considerations Irule Generator the displayed irule has been generated with a proprietary tool developed in golang. Zip file Content The downloadable zip file contains : a shell script that generate the irule code for the list of event specified in event_list.tcl file a file that contain a list of all irule event (excluded : CLIENT_ACCEPTED, CLIENT_CLOSED, RULE_INIT) an irule containing all possible events (generated by the script). Just for demo, can not be used in real life. Compatibility Supported in v12.1.0 ACCESS_PER_REQUEST_AGENT_EVENT BOTDEFENSE_ACTION BOTDEFENSE_REQUEST WS_CLIENT_DATA WS_CLIENT_FRAME WS_CLIENT_FRAME_DONE WS_REQUEST WS_RESPONSE WS_SERVER_DATA WS_SERVER_FRAME WS_SERVER_FRAME_DONE Irule Events The displayed irule currently support events from CLIENT_*, HTTP_*, LB_* and SSL_*. This irule should fit for most of the standard ltm use cases on http and https. Outputs syslog output : virtual=/Common/vs_test_irule_order_event, id=d5f693399, time=0, event_order=0, event_type=CLIENT_ACCEPTED virtual=/Common/vs_test_irule_order_event, id=d5f693399, time=0, event_order=1, event_type=CLIENTSSL_CLIENTHELLO virtual=/Common/vs_test_irule_order_event, id=d5f693399, time=6, event_order=2, event_type=CLIENTSSL_HANDSHAKE virtual=/Common/vs_test_irule_order_event, id=d5f693399, time=6, event_order=3, event_type=HTTP_REQUEST virtual=/Common/vs_test_irule_order_event, id=d5f693399, time=6, event_order=4, event_type=LB_FAILED virtual=/Common/vs_test_irule_order_event, id=d5f693399, time=6, event_order=5, event_type=CLIENT_CLOSED ` JSON output : { "d5f693399": [ { "virtual": "/Common/vs_test_irule_order_event", "id": "d5f693399", "time": "0", "event_order": "0", "event_type": "CLIENT_ACCEPTED" }, { "virtual": "/Common/vs_test_irule_order_event", "id": "d5f693399", "time": "0", "event_order": "1", "event_type": "CLIENTSSL_CLIENTHELLO" }, { "virtual": "/Common/vs_test_irule_order_event", "id": "d5f693399", "time": "6", "event_order": "2", "event_type": "CLIENTSSL_HANDSHAKE" }, { "virtual": "/Common/vs_test_irule_order_event", "id": "d5f693399", "time": "6", "event_order": "3", "event_type": "HTTP_REQUEST" }, { "virtual": "/Common/vs_test_irule_order_event", "id": "d5f693399", "time": "6", "event_order": "4", "event_type": "LB_FAILED" }, { "virtual": "/Common/vs_test_irule_order_event", "id": "d5f693399", "time": "6", "event_order": "5", "event_type": "CLIENT_CLOSED" } ] } Features Version 1.0 List Event order Generate a sessionid to track the Client connection until it close Display time elapsed since client has been accepted Logging in JSON format (to use with third party libraries like d3.js) Backlog add logging using csv format use HSL instead of local logging add extra comments for specific events like CLIENT_CERT and LB_FAILED add more events (security related events) release a script to generate an irule that use events related to the other irules present in a Virtual Server External links Github : https://github.com/e-XpertSolutions/f5 iRule Sample when RULE_INIT { set static::client_ip "192.168.10.1" set static::json 1 } when CLIENT_ACCEPTED { set counter 0 set event_type "CLIENT_ACCEPTED" set sessionid "[IP::client_addr][TCP::client_port][IP::local_addr][TCP::local_port][expr { int(100000000 * rand()) }]" binary scan [md5 $sessionid] H* md5_string trash set md5_string [string range $md5_string 12 20] set start_time [clock clicks -milliseconds] log local0. "virtual=[virtual], id=$md5_string, time=0, event_order=$counter, event_type=$event_type" if { $static::json } { set json_log "\{ \"$md5_string\": \[\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"0\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when CLIENT_CLOSED { set counter [expr { $counter+1 }] set event_type "CLIENT_CLOSED" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}\]\}" log local0. "$json_log" } } when HTTP_REQUEST { set counter [expr { $counter+1 }] set event_type "HTTP_REQUEST" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when HTTP_REQUEST_RELEASE { set counter [expr { $counter+1 }] set event_type "HTTP_REQUEST_RELEASE" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when HTTP_RESPONSE { set counter [expr { $counter+1 }] set event_type "HTTP_RESPONSE" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when HTTP_RESPONSE_RELEASE { set counter [expr { $counter+1 }] set event_type "HTTP_RESPONSE_RELEASE" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when HTTP_RESPONSE_CONTINUE { set counter [expr { $counter+1 }] set event_type "HTTP_RESPONSE_CONTINUE" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when HTTP_REQUEST_SEND { set counter [expr { $counter+1 }] set event_type "HTTP_REQUEST_SEND" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when HTTP_REQUEST_DATA { set counter [expr { $counter+1 }] set event_type "HTTP_REQUEST_DATA" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when CLIENT_DATA { set counter [expr { $counter+1 }] set event_type "CLIENT_DATA" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when HTTP_PROXY_REQUEST { set counter [expr { $counter+1 }] set event_type "HTTP_PROXY_REQUEST" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when HTTP_DISABLED { set counter [expr { $counter+1 }] set event_type "HTTP_DISABLED" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when SERVER_CLOSED { set counter [expr { $counter+1 }] set event_type "SERVER_CLOSED" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when SERVER_DATA { set counter [expr { $counter+1 }] set event_type "SERVER_DATA" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when SERVERSSL_DATA { set counter [expr { $counter+1 }] set event_type "SERVERSSL_DATA" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when SERVER_CONNECTED { set counter [expr { $counter+1 }] set event_type "SERVER_CONNECTED" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when CLIENTSSL_DATA { set counter [expr { $counter+1 }] set event_type "CLIENTSSL_DATA" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when CLIENTSSL_CLIENTCERT { set counter [expr { $counter+1 }] set event_type "CLIENTSSL_CLIENTCERT" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when CLIENTSSL_CLIENTHELLO { set counter [expr { $counter+1 }] set event_type "CLIENTSSL_CLIENTHELLO" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when CLIENTSSL_HANDSHAKE { set counter [expr { $counter+1 }] set event_type "CLIENTSSL_HANDSHAKE" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when CLIENTSSL_SERVERHELLO_SEND { set counter [expr { $counter+1 }] set event_type "CLIENTSSL_SERVERHELLO_SEND" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when SERVERSSL_CLIENTHELLO_SEND { set counter [expr { $counter+1 }] set event_type "SERVERSSL_CLIENTHELLO_SEND" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when SERVERSSL_HANDSHAKE { set counter [expr { $counter+1 }] set event_type "SERVERSSL_HANDSHAKE" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when SERVERSSL_SERVERHELLO { set counter [expr { $counter+1 }] set event_type "SERVERSSL_SERVERHELLO" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when LB_QUEUED { set counter [expr { $counter+1 }] set event_type "LB_QUEUED" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when LB_FAILED { set counter [expr { $counter+1 }] set event_type "LB_FAILED" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } when LB_SELECTED { set counter [expr { $counter+1 }] set event_type "LB_SELECTED" set curtime [expr { [clock clicks -milliseconds] - $start_time }] log local0. "virtual=[virtual], id=$md5_string, time=$curtime, event_order=$counter, event_type=$event_type" if { $static::json } { append json_log "\{\"virtual\":\"[virtual]\", \"id\":\"$md5_string\", \"time\":\"$curtime\", \"event_order\":\"$counter\", \"event_type\":\"$event_type\"\}," } } Code : 68686 Tested this on version: 11.51.4KViews0likes3CommentsAPM Full Step Up Authentication
Problem this snippet solves: By default, APM is not able to handle several authentication during a session. Once you are logged in, it’s finished, you can’t ask for authentication again. Since v12.1.0, we can see a new feature in EA called “Step-up Authentication” and the introduction of subroutines that is currently limited to ldap authentication or a confirm box. The irule and configuration below allow the administrator to define 2 levels of authentication based on URIs. The concept can be extended to have multiple authentication levels. This concept can be extended to define several Level of authentication. You can also change the element that trigger the additionnal authentication process. How to use this snippet: Installation irule To make it works, you need to install the irule on the Virtual Server that publish your application with APM authentication. datagroup You need to create a datagroup of string type. This dg must contains http path that need an additional authentication step. The dg is named loa3_uri in the irule example. access profile If you already have an existing access profile, you will need to modify it and include some additionnal configuration in your VPE. If you have no access profile, you can starts building your own based on the description we provide below. Scenarios 1) User try to reach strong uri after first authentication process In this scenario, the user first authenticate using a standard authentication mecanism. Once authenticated, if the user request content that is behing strong uris, the user restart an authentication process in the "Strong Auth" and "Already Auth" branch of the VPE. 2) User try to reach strong uri during the first authentication process If the user try to access a strong uri on its first attempt, he will need to complete the full authentication process. Then, he can access every part of the web application without any additional prompt. Special considerations Client certificate Authentication You may need to use Client certificate authentication as a primary factor or second factor. We highly recommend to use "SSl on-demand authentication" if you need it as primary factor. Client Certificate is not supported as a second factor, you need to use SSl on-demand authentication. WebSSO When first authentication has already been allowed and the user try to access a protected uri, the system will invite the user to complete the new authentication (second factor). This process will restart a webSSO action on the backend. Basic, NTLM and Kerberos webSSO have been tested with success. Configuring the Visual Policy Editor The printscreen below is a minimal Visual Policy Editor used to make Step up Authentication works properly : Strong Auth The strong Auth block is an "Empty Action" with two branch. The branch named "Strong" contains the following condition : expr { [mcget {session.server.landinguri}] starts_with "/strong" || [mcget {session.custom.last.strong}] == 1 } We check that the uri starts with strong (used in scenario 1) or if a custom variable is set to 1 (second scenario) Already Auth This is an empty action with two branch. The branch named "yes" contains the following expression : expr { [mcget {session.custom.last.authresult}] contains "true" } 2-factor Ending session.custom.last.authtype variable must be set to 1 session.policy.result.redirect.url must be changed. The session.server.landinguri contains the true origin uri. To set this variable, you must use the tcl script below : proc urldecode str { variable map variable alphanumeric a-zA-Z0-9 for {set i 0} {$i <= 256} {incr i} { set c [format %c $i] if {![string match \[$alphanumeric\] $c]} { set map($c) %[format %.2x $i] } } array set map { " " + \n %0d%0a } set str [string map [list + { } "\\" "\\\\"] $str] regsub -all -- {%([A-Fa-f0-9][A-Fa-f0-9])} $str {\\u00\1} str return [subst -novar -nocommand $str] } set decoded_uri [urldecode [string range [mcget {session.server.landinguri}] [expr { [string last = [mcget {session.server.landinguri}]] + 1 }] end]] return $decoded_uri Full strong Ending session.custom.last.authtype variable must be set to 1 Standard Ending session.custom.last.authtype variable must be set to 0 Session variables The following variables can be used in the 2-factor section of the Visual Policy Editor : session.custom.last.username session.custom.last.password Features 2-step authentication Retrieve username and password from first authentication Encrypt Session1 cookie to avoid session Hijacking External links Github : https://github.com/e-XpertSolutions/f5 Code : when RULE_INIT { # to be changed prior to any publishing set passphrase "hEuoYjmFUpB4PcpO3bUdQtLP4ic7jjm" } when HTTP_REQUEST { if { [HTTP::cookie exists MRHSession] and [ACCESS::session exists -state_allow -sid [HTTP::cookie MRHSession]] } { set strong_auth [ACCESS::session data get session.custom.last.authtype] if { [class match [HTTP::path] starts_with loa3_uri] and $strong_auth == 0 } { HTTP::cookie encrypt "MRHSession" $passphrase HTTP::respond 302 noserver "Location" "/strong?return_url=[URI::encode [HTTP::uri]]" "Cache-Control" "no-cache, must-revalidate" Set-Cookie "MRHSession=deleted;expires=Thu, 01-Jan-1970 00:00:10 GMT;path=/" Set-Cookie "LastMRH_Session=deleted;expires=Thu, 01-Jan-1970 00:00:10 GMT;path=/" Set-Cookie "Session1=[HTTP::cookie MRHSession];path=/" } } } when ACCESS_SESSION_STARTED { # decrypt Session1 cookie value set decrypted [HTTP::cookie decrypt "Session1" $passphrase] if { [HTTP::cookie exists Session1] and [ACCESS::session exists -state_allow -sid $decrypted] } { ## section : retrieve session variables from the first session ACCESS::session data set session.custom.last.username [ACCESS::session data get session.logon.last.username -sid $decrypted] ACCESS::session data set session.custom.last.password [ACCESS::session data get session.logon.last.password -sid $decrypted] ## End section ACCESS::session data set session.custom.last.authresult "true" # remove the first created session during standard authentication to avoid multiple active sessions ACCESS::session remove -sid $decrypted } elseif { [class match [HTTP::path] starts_with loa3_uri] } { ACCESS::session data set session.custom.last.strong 1 } } Tested this on version: 11.51.3KViews0likes7CommentsGo library to manage BIG-IP iControl REST API
Problem this snippet solves: This library provides necessary structs and functions to manage the whole REST API. Some REST Calls may require BIG-IP v12.1.x to work properly. How to use this snippet: f5-rest-client implements a REST client to query the F5 BIG-IP iControl REST API. Installation go get -u github.com/e-XpertSolutions/f5-rest-client/f5 Available authentication methods Basic authentication f5Client, err := f5.NewBasicClient(base_url, username, password) Token based authentication f5Client, err := f5.NewTokenClient(base_url, username, password, login_provider_name, skip_ssl_verification) Usage // Copyright 2017 e-Xpert Solutions SA. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "encoding/json" "log" "github.com/e-XpertSolutions/f5-rest-client/f5" "github.com/e-XpertSolutions/f5-rest-client/f5/net" ) func sexyPrint(label string, a interface{}) { j, err := json.MarshalIndent(a, "", " ") if err != nil { log.Fatal(err) } log.Print("DEBUG ", label, ":\n", string(j)) } func main() { // 1) Basic Authentication f5Client, err := f5.NewBasicClient("https://127.0.0.1", "admin", "admin") // 2) Token Based Authentication // f5Client, err := f5.NewTokenClient("https://127.0.0.1", "admin", "admin", "tmos", true) if err != nil { log.Fatal(err) } f5Client.DisableCertCheck() netClient := net.New(f5Client) self, err := netClient.Self().ListAll() if err != nil { log.Fatal(err) } sexyPrint("SelfIP List:", self) } FEATURES Basic authentication Token based authentication Manage Virtual Server, pool, node, irules, monitors Manage Cluster Management Manage interfaces, vlan, trunk, self ip, route, route domains Manage virtualization features (/vcmp) Manage system related stuffs Add Helper functions to enable, disable or force a node offline Add Helper functions to enable or disable a Virtual Server List expiring certificates List expired certificates Transaction support [new] Manage DNS and global load balancing servers (/gtm) [new] Add support for Stats retrieval on node, pool, virtual and profiles ROADMAP Add support for authentication through external providers Manage access policies (/apm) Manage security (/security) Manage analytics configuration (/analytics) Add support for results pagination Add support for API versioning Add support for new API endpoints coming in v13 Examples Transactions - Create a simple HTTP service f5Client, err := f5.NewBasicClient("https://127.0.0.1", "admin", "admin") if err != nil { log.Fatal(err) } f5Client.DisableCertCheck() // Start new transaction. tx, err := f5Client.Begin() if err != nil { log.Fatal(err) } ltmClient := ltm.New(tx) // Create a HTTP monitor log.Print("Create a HTTP monitor") monitorConfig := ltm.MonitorHTTPConfig{ Name: "http_monitor_" + tx.TransactionID(), Send: "GET / HTTP/1.0\r\n\r\n", Recv: "Hello", } if err := ltmClient.MonitorHTTP().Create(monitorConfig); err != nil { log.Fatal(err) } // Create a Pool log.Print("Create a pool") poolConfig := ltm.PoolConfig{ Name: "pool_" + tx.TransactionID(), Monitor: "/Common/http_monitor_" + tx.TransactionID(), Members: []string{"10.1.10.10:80", "10.1.10.11:80"}, } if err := ltmClient.Pool().Create(poolConfig); err != nil { log.Fatal(err) } // Create a Virtual Server log.Print("Create a Virtual Server") vsConfig := ltm.VirtualServerConfig{ Name: "vs_http_" + tx.TransactionID(), Destination: "10.1.20.130:80", IPProtocol: "tcp", Pool: "pool_" + tx.TransactionID(), SourceAddressTranslation: ltm.SourceAddressTranslation{ Type: "automap", }, Profiles: []string{ "tcp-mobile-optimized", "http", }, } if err := ltmClient.Virtual().Create(vsConfig); err != nil { log.Fatal(err) } // Commit to make the changes persistent. if err := tx.Commit(); err != nil { log.Fatal(err) } List SSL Certificates sysClient := sys.New(f5Client) certs, err := sysClient.FileSSLCert().ListAll() if err != nil { log.Fatal(err) } sexyPrint("Certificates", certs) List expired SSL Certificates sysClient := sys.New(f5Client) certs, err := sysClient.FileSSLCert().ListExpired() if err != nil { log.Fatal(err) } sexyPrint("Expired Certificates", certs) List expiring SSL Certificates sysClient := sys.New(f5Client) // ListExpiring(number_of_seconds) certs, err := sysClient.FileSSLCert().ListExpiring(60 * 60 * 24 * 15) if err != nil { log.Fatal(err) } sexyPrint("Expiring Certificates", certs) Contributing We appreciate any form of contribution (feature request, bug report, pull request, ...). We have no special requirements for Pull Request, just follow the standard GitHub way. License The sources are release under a BSD 3-Clause License. The full terms of that license can be found in LICENSE file of this repository. Code : https://github.com/e-XpertSolutions/f5-rest-client Tested this on version: 11.51.3KViews1like3CommentsSliding session for "FedAuth" Persistent cookie delivered by ADFS
Problem this snippet solves: ADFS deliver persistent cookies that allow the user to connect to Sharepoint using WS-Fed. By default, the lifetime is about 12 hours and for security reason, should be changed to a lower value. This irule provide a 12 hours validity for the cookie but add a shorter timeout for inactivity. How to use this snippet: This irule change the default expiration time of the FedAuth cookie to a lower value. The expiration time is reset every time a user make a request until it reach the max expiration time. After the timeout or the max expiration time has been reached, the user is redirected back to the ADFS for authentication. External links Github : https://github.com/e-XpertSolutions/f5 Code : when RULE_INIT { set static::timeout 900 set static::httponly 1 set static::debug 0 set static::irule_name "irule-test-sliding-session" } when HTTP_REQUEST { if { $static::debug } { set event "HTTP_REQUEST" } set hostname [string tolower [HTTP::host]] switch -glob $hostname { "sharepoint1" - "sharepoint2" { set key "" set valid 1 if { [HTTP::cookie exists FedAuth] } { set key [sha1 "$hostname:[HTTP::cookie FedAuth]"] if { [table lookup $key] == "" } { if { $static::debug } { log local0. "$static::irule_name - [string map -nocase {"/common/" ""} [virtual name]]: no valid sliding session key found for [IP::client_addr] with session FedAuth:[string range [HTTP::cookie FedAuth] 0 7] on $hostname - Action: redirect user to logout uri" } HTTP::redirect "https://[HTTP::host]/_trust/default.aspx?wa=wsignoutcleanup1.0" } else { if { $static::debug } { log local0. "$static::irule_name - [string map -nocase {"/common/" ""} [virtual name]]: a valid key has been found for [IP::client_addr] with session FedAuth:[string range [HTTP::cookie FedAuth] 0 7] on $hostname" } } } } default { set valid 0 } } } when HTTP_RESPONSE { if { $static::debug } { set event "HTTP_RESPONSE" } if {[HTTP::cookie exists FedAuth] and $valid } { if { $static::debug } { log local0. "$static::irule_name - [string map -nocase {"/common/" ""} [virtual name]]: set-cookie header found with FedAuth cookie ([string range [HTTP::cookie FedAuth] 0 7]) for [IP::client_addr]" } set key [sha1 "$hostname:[HTTP::cookie FedAuth]"] if { [table lookup $key] != "" } { if { [table lifetime -remaining $key] >= $static::timeout } { table timeout $key $static::timeout HTTP::cookie expires FedAuth $static::timeout relative if { $static::debug } { log local0. "$static::irule_name - [string map -nocase {"/common/" ""} [virtual name]]: FedAuth cookie ([string range [HTTP::cookie FedAuth] 0 7]) valid for [table lifetime -remaining $key] seconds - Action : cookie expiration set to 300 seconds" } } else { HTTP::cookie expires FedAuth [table lifetime -remaining $key] relative if { $static::debug } { log local0. "$static::irule_name - [string map -nocase {"/common/" ""} [virtual name]]: FedAuth cookie ([string range [HTTP::cookie FedAuth] 0 7]) valid for [table lifetime -remaining $key] seconds - Action : cookie expiration set to remaining lifetime" } } } else { table add $key [HTTP::cookie FedAuth] $static::timeout [HTTP::cookie expires FedAuth] if { $static::debug } { log local0. "$static::irule_name - [string map -nocase {"/common/" ""} [virtual name]]: FedAuth cookie ([string range [HTTP::cookie FedAuth] 0 7]) valid for [table lifetime -remaining $key] seconds - Action : Add cookie to the sliding session table for [HTTP::cookie expires FedAuth] seconds" } #HTTP::cookie expires FedAuth $static::timeout relative if { $static::debug } { log local0. "$static::irule_name - [string map -nocase {"/common/" ""} [virtual name]]: FedAuth cookie ([string range [HTTP::cookie FedAuth] 0 7]) valid for [table lifetime -remaining $key] seconds - Action : set cookie to expires within 300 seconds" } } } elseif { $key != "" } { if { [table lookup $key] != "" } { if { [table lifetime -remaining $key] >= $static::timeout } { table timeout $key $static::timeout if { $static::debug } { log local0. "$static::irule_name - [string map -nocase {"/common/" ""} [virtual name]]: FedAuth cookie ([string range [HTTP::cookie FedAuth] 0 7]) valid for [table lifetime -remaining $key] seconds - Action : insert FedAuth session cookie with 300 seconds expiration time" } HTTP::cookie insert name FedAuth value [table lookup $key] path / HTTP::cookie expires FedAuth $static::timeout relative HTTP::cookie secure FedAuth enable if { $static::debug } { log local0. "$static::irule_name - [string map -nocase {"/common/" ""} [virtual name]]: FedAuth cookie ([string range [HTTP::cookie FedAuth] 0 7]) valid for [table lifetime -remaining $key] seconds - Action : insert FedAuth session cookie with 300 seconds expiration time" } } else { HTTP::cookie insert name FedAuth value [table lookup $key] path / HTTP::cookie expires FedAuth [table lifetime -remaining $key] relative HTTP::cookie secure FedAuth enable if { $static::debug } { log local0. "$static::irule_name - [string map -nocase {"/common/" ""} [virtual name]]: FedAuth cookie ([string range [HTTP::cookie FedAuth] 0 7]) valid for [table lifetime -remaining $key] seconds - Action : insert FedAuth session cookie" } } # # insert httponly flag to FedAuth Cookie # if { $static::httponly } { set value [HTTP::cookie value FedAuth] set testvalue [string tolower $value] set valuelen [string length $value] switch -glob $testvalue { "*;httponly*" - "*; httponly*" { } default { set value "$value; HttpOnly"; } } if { [string length $value] > $valuelen} { HTTP::cookie value FedAuth "${value}" } } } } } Tested this on version: 11.5999Views0likes1CommentF5 BIG-IP Phantom Cyber app
Problem this snippet solves: At World Wide Technology, we are engaging with customers in their evaluation of Phantom and this video clip provides a demonstration of the playbooks and apps developed to ingest data through the REST API and then implement a firewall rule on a F5 BIG-IP appliance to block the source IP address identified in the artifact. This video illustrates the app. https://youtu.be/1lktjQzVcQQ and this link provides additional background on the use case. https://blog.phantom.us/2016/03/31/community-magic/ The app imports (reuses) an Ansible module which uses the iControl REST interface. The Phantom app is available here. How to use this snippet: The app can be installed in Phantom and referenced in playbooks. This app supports containment actions like 'block ip' or 'unblock ip' on a F5 BIG-IP appliance. There must be a firewall policy (Security››Network Firewall:Policies) configured on the BIG-IP and the name of the policy must be specified in the Action Parameters. Code : https://github.com/joelwking/ansible-f5/blob/master/icontrol_install_config.py Tested this on version: 11.6880Views0likes7CommentsDoS and NTLM Brute force protection for HTTP(s) traffic
Problem this snippet solves: This snippet has been designed to mainly protect against NTLM's Denial of Service and brute force attacks against web application that use this authentication mecanism. This irule help companies to fight against brute force attacks at the HTTP layer. You can combine this irule with another one on non-http traffic to provide a brute force protection across multiple layers. For pure HTTP application, you should prefer the Brute force protection provided by the ASM module. How to use this snippet: This irule should be installed on each Virtual Server that require NTLM protection. In a Microsoft Skype for Business deployment, you may need to protect following services : Web Services Conf Autodiscover Exchange Web Services TO BE DONE : Provide a way to unlock blocked users External links Github : https://github.com/e-XpertSolutions/f5 Related Articles DoS and NTLM Brute force protection for SIP flow Credits This irule is based on NTLM logger Code : when RULE_INIT { array set NTLMFlags { unicode 0x00000001 oem 0x00000002 req_target 0x00000004 unknown1 0x00000008 sign 0x00000010 seal 0x00000020 datagram 0x00000040 lmkey 0x00000080 netware 0x00000100 ntlm 0x00000200 unknown2 0x00000400 unknown3 0x00000800 ntlm_domain 0x00001000 ntlm_server 0x00002000 ntlm_share 0x00004000 NTLM2 0x00008000 targetinfo 0x00800000 128bit 0x20000000 keyexch 0x40000000 56bit 0x80000000 } set static::irule_name "irule-ntlm-bruteforce" set static::email_domain "domain.com" set static::user_domain "DOMAIN" set static::log_server "" set static::log_pri "local0." set static::fail_tab "NTLMfails" set static::blacklist_tab "NTLMblackhole" set static::userfail_tab "NTLMUserfails" set static::userblacklist_tab "NTLMUserblackhole" set static::max_failures 5 set static::fail_memory 300 set static::block_duration 300 } when CLIENT_ACCEPTED { if {[table lookup -subtable $static::blacklist_tab [IP::client_addr]] == 1} { log $static::log_pri "[virtual] - BLACKHOLED IPADDR [IP::client_addr]:[TCP::client_port] (Reputation=[IP::reputation [IP::client_addr]])" reject return } } when HTTP_REQUEST { if {[table lookup -subtable $static::blacklist_tab [IP::client_addr]] == 1} { log $static::log_pri "[virtual] - BLACKHOLED IPADDR [IP::client_addr]:[TCP::client_port] (Reputation=[IP::reputation [IP::client_addr]])" reject return } if {[HTTP::header Authorization] starts_with "NTLM "} { set ntlm_msg [ b64decode [split [lindex [HTTP::header Authorization] 1] ] ] binary scan $ntlm_msg a7ci protocol zero type if { $type eq 3 } { binary scan $ntlm_msg @12ssissississississii \ lmlen lmlen2 lmoff \ ntlen ntlen2 ntoff \ dlen dlen2 doff \ ulen ulen2 uoff \ hlen hlen2 hoff \ slen slen2 soff \ flags set ntlm_domain {}; binary scan $ntlm_msg @${doff}a${dlen} ntlm_domain set ntlm_user {}; binary scan $ntlm_msg @${uoff}a${ulen} ntlm_user set ntlm_host {}; binary scan $ntlm_msg @${hoff}a${hlen} ntlm_host set unicode [expr {$flags & 0x00000001}] if {$unicode} { set ntlm_domain_convert "" foreach i [ split $ntlm_domain ""] { scan $i %c c if {$c>1} { append ntlm_domain_convert $i } elseif {$c<128} { set ntlm_domain_convert $ntlm_domain_convert } else { append ntlm_domain_convert \\u[format %04.4X $c] } } set ntlm_domain $ntlm_domain_convert set ntlm_user_convert "" foreach i [ split $ntlm_user ""] { scan $i %c c if {$c>1} { append ntlm_user_convert $i } elseif {$c<128} { set ntlm_user_convert $ntlm_user_convert } else { append ntlm_user_convert \\u[format %04.4X $c] } } set ntlm_user $ntlm_user_convert set ntlm_host_convert "" foreach i [ split $ntlm_host ""] { scan $i %c c if {$c>1} { append ntlm_host_convert $i } elseif {$c<128} { set ntlm_host_convert $ntlm_host_convert } else { append ntlm_host_convert \\u[format %04.4X $c] } } set ntlm_host $ntlm_host_convert } binary scan $ntlm_msg @${ntoff}a${ntlen} ntdata binary scan $ntlm_msg @${lmoff}a${lmlen} lmdata binary scan $ntdata H* ntdata_h binary scan $lmdata H* lmdata_h set interesting 1 if { ($ntlm_domain equals $static::user_domain or [string tolower $ntlm_user] ends_with $static::email_domain) } { set attack 1 if {[table lookup -subtable $static::userblacklist_tab $ntlm_user] == 1} { # Block ntlm_user exceeding the number of failed logons in the timeout period log $static::log_pri "[virtual] - BLACKHOLED $ntlm_domain\\$ntlm_user from $ntlm_host at [IP::client_addr]:[TCP::client_port] (Reputation=[IP::reputation [IP::client_addr]])" reject return } else { log $static::log_pri "[virtual] - Login attempt by $ntlm_domain\\$ntlm_user from $ntlm_host for [HTTP::uri]." } } else { set attack 0 log $static::log_pri "[virtual] - Not a valid user - Login attempt by $ntlm_domain\\$ntlm_user from $ntlm_host for [HTTP::uri]." } return [list type $type flags [format 0x%08x $flags] \ ntlm_domain $ntlm_domain ntlm_host $ntlm_host ntlm_user $ntlm_user \ lmhash $lmdata nthash $ntdata] } } } when HTTP_RESPONSE { if {[info exists interesting] && $interesting == 1} { set client [IP::client_addr]:[TCP::client_port] set node [IP::server_addr]:[TCP::server_port] set nodeResp [HTTP::status] if { $nodeResp == 401 and ([info exists attack] and $attack == 1)} { log $static::log_pri "[virtual] - invalid credentials detected for $ntlm_user" table set -subtable $static::fail_tab -notouch -excl [IP::client_addr] 0 indef $static::fail_memory table incr -subtable $static::fail_tab [IP::client_addr] if {[table lookup -subtable $static::fail_tab [IP::client_addr]] >= $static::max_failures} { set now [clock seconds] set now_date [split [clock format $now -format {%X %x}] " "] set later [expr {$now + $static::block_duration}] set later_date [split [clock format $later -format {%X %x}] " "] log $static::log_pri "[virtual] - BLACKHOLING IPADDR - [IP::client_addr] (Reputation=[IP::reputation [IP::client_addr]]) at $now_date until $later_date" table set -subtable $static::blacklist_tab -excl [IP::client_addr] 1 indef $static::block_duration } if {[info exists ntlm_user]} { table set -subtable $static::userfail_tab -notouch -excl $ntlm_user 0 indef $static::fail_memory table incr -subtable $static::userfail_tab $ntlm_user if {[table lookup -subtable $static::userfail_tab $ntlm_user] >= $static::max_failures} { set now [clock seconds] set later [expr {$now + $static::block_duration}] log $static::log_pri "[virtual] - BLACKHOLING USER - $ntlm_user at $now_date until $later_date" table set -subtable $static::userblacklist_tab -excl $ntlm_user 1 indef $static::block_duration } } } } } Tested this on version: 11.5857Views0likes3CommentsProvision IOS profile for Exchange ActiveSync with client certificate authentication
Problem this snippet solves: If you need to use client certificate authentication for ActiveSync services on IOS, you need to deploy custom profiles through a Mobile Device Management. MDM is maybe a little bit too much to achieve only this feature. The irule below provide necessary materials to provision a certificate and an exchange profile on IOS. Tested successfully on IOS 9. We use SCEP protocol for certificate enrollment. How to use this snippet: You need to define a Virtual Server and an access profile to publish ActiveSync. Then, you need to assign the irule on the Virtual Server. The certificate is retrieved using SCEP protocol on a Microsoft ADCS 2012 R2. The SCEP url should be changed in the Exchange payload. We configured APM to protect the access to this service and retrieve attributes from Active Directory but you can change the irule code to retrieve information and protect the service in a different manner. When a user reach /enroll uri with Safari browser, the provisioning process starts. /!\ I provide an IOS payload as example, but you need to modify it to fit your environment and save it as an ifile. Settings that need to be changed in the xml payload : <string>HOST.DOMAIN.COM</string> : Activesync FQDN <string>DOMAIN-Issuer-CA</string> : Issuing CA Name (if exists otherwise related code should be removed) <data>CERTIFICATE</data> : X.509 certificate in Base64 for Issuing CA <string>DOMAIN-Root-CA</string> : Root CA Name <data>CERTIFICATE</data> : X.509 certificate in Base64 for the Root CA <string>DOMAIN</string> : Organization name to be present in the user certificate <string>http://scep.domain.com/scep</string> : SCEP url External links Github : github.com/e-XpertSolutions/f5 Code : 68654 Tested this on version: 11.5799Views0likes1Comment