APM Sharepoint authentication v2
Problem this snippet solves:
This new version of irule supports NTLM auth (mandatory for Onedrive Apps)
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
- connect to One Drive on premise from PC and mobiles
- 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 (NTLM and Basic)
- Form based authentication (browser and Microsoft office) is compatible (validated for one customer) with SAML authentication
- NTLM auth for Onedrive mobile applications
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.
In the first HTTP_REQUEST event, configure authentication mode list by setting the AUTHENTICATION_MODE variable
Set authentication mode list supported. possible values are :
- form :default Form based authentication
- msofba : Microsoft Office Form Based Authentication for Office and Onedrive apps
-
persist : Add persistent cookie to recover closed session. this function is only supported by form and msofba authentications.
- --> persist word must be set after authentication mode : ex : {form persist} or {msofba persist}
-
basic : Basic Authentication
-
ntlm : NTLM Authentication
-
negotiate : Kerberos / SPNEGO authentication : Not supported yet by this irule
- --> basic, ntm and negotiate can be set together. ex: {negotiate ntlm basic} {ntlm basic}
-
deny : send a 403 response code to deny the request
-
disable : disable APM authentication
Code :
when RULE_INIT { #If NTLM Auth is defined below, define the ECA_METADATA_ARG with your NTLM profile and enable eca profile in virtual server configuration with tmsh command # modify ltm virtual [virtual name] profile add {eca} set static::ECA_METADATA_ARG "select_ntlm:/Common/NTLM-Auth" set static::session_restore_aes_key "AES 256 affeaffeaffeaffeaffeaffeaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe" ;# AES Key to protect and validate recovery data # Define required APM variables stored in the restore cookie to create new session with same security level. set static::session_restore_variables { session.ui.lang session.logon.last.username session.logon.last.logonname session.logon.last.krbdomain session.logon.last.domain session.krbsso.last.domain session.krbsso.last.username session.assigned.acls session.logon.last.domain session.sso.token.last.username session.user.sessiontype } # Cookie expire in 2 hours for the test... set static::session_restore_timeout 172800 ;### 7200 / 172800 } when CLIENT_ACCEPTED { set last_ua_agent "init" # Set addtional HTTP headers in HTTP authentication responses set ADDITIONAL_AUTH_HEADERS "MicrosoftSharePointTeamServices 15.0.0.4763" } when HTTP_REQUEST { #################################### Authentication method selection ##################################################### # Set authentication mode list supported. possible values are # form :default Form based authentication # msofba : Microsoft Office Form Based Authentication for Office and Onedrive apps # persist : Add persistent cookie to recover closed session. this function is only supported by form and msofba authentications. # --> persist word must be set after authentication mode : ex : {form persist} or {msofba persist} # basic : Basic Authentication # ntlm : NTLM Authentication # negotiate : Kerberos / SPNEGO authentication : Not supported yet by this irule # --> basic, ntm and negotiate can be set together. ex: {negotiate ntlm basic} {ntlm basic} # deny : send a 403 response code to deny the request # disable : disable APM authentication # Disable Authentication for Internal networks #if { [IP::addr [IP::client_addr]/25 equals 1.1.1.0] or [IP::addr [IP::client_addr]/25 equals 2.2.2.0] } { # set AUTHENTICATION_MODE {disable} # ASM::disable # return #} #log local0. [HTTP::header "User-Agent"] if { $last_ua_agent equals [set last_ua_agent [HTTP::header value "User-Agent"]] } { # Do nothing, keep previous request authschema value } elseif {[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 AUTHENTICATION_MODE {msofba} } else { switch -glob -- [string tolower [HTTP::header "User-Agent"]] { "*microsoft office *ios*" - "*onedriveiosapp*" - "onedrive/*darwin*" { # NTLM is the only one supported for onedrive mobile set AUTHENTICATION_MODE {ntlm basic} #set AUTHENTICATION_MODE {deny} } "*microsoft office onedrive*" - "*microsoft onedrive*" - "*microsoft office skydrive*" - "*microsoft office syncproc*" - "*microsoft office upload center*" - "*office protocol discovery*" - "*microsoft office*" - "*microsoft data access internet publishing provider*" - "*non-browser*" - "msoffice 12*" - "*microsoft-webdav-miniredir*" - "*ms frontpage 1[23456789]*" { # Implicit MSOFBA support detected. set AUTHENTICATION_MODE {msofba} #set AUTHENTICATION_MODE {ntlm basic} } "*ms frontpage*" { # Legacy client detected set AUTHENTICATION_MODE {ntlm basic} #set AUTHENTICATION_MODE {deny} } "*mozilla*" - "*opera*" { # Regular web browser detected. set AUTHENTICATION_MODE {form} } default { set AUTHENTICATION_MODE {ntlm basic} #set AUTHENTICATION_MODE {msofba} } } #log local0. "[IP::remote_addr] : [string tolower [HTTP::header "User-Agent"]] : $AUTHENTICATION_MODE : [HTTP::cookie value MRHSession] : [HTTP::cookie value MRHSession_R]" } #################################### end of Authentication method selection ############################################## } priority 900 when RULE_INIT { set static::AUTH_POLICY_FAILED "policy_failed" set static::AUTH_POLICY_SUCCEED "policy_succeed" set static::AUTH_POLICY_DONE_WAIT_SEC 5 set static::AUTH_FIRST_BIG_POST_CONTENT_LEN 640000 set static::AUTH_POLICY_RESULT_POLL_INTERVAL 100 set static::AUTH_POLICY_RESULT_POLL_MAXRETRYCYCLE 100 set static::AUTH_ACCESS_USERKEY_TBLNAME "auth_access_userkey" set static::AUTH_ACCESS_LOG_PREFIX "01490000:7:" set static::AUTH_ACCESS_DEL_COOKIE_HDR_VAL "MRHSession=deleted; \ expires=Thu, 01-Jan-1970 00:00:01 GMT;\ path=/" } when CLIENT_ACCEPTED { set clientless_mode 0 set inject_session_cookie "" set inject_recover_cookie 0 if { ! [ info exists ADDITIONAL_AUTH_HEADERS ] } { set ADDITIONAL_AUTH_HEADERS "" } } when HTTP_REQUEST { set inject_session_cookie "" if { ! [ info exists f_ntlm_auth_succeed ] } { set f_ntlm_auth_succeed 0 } if { ! [ info exists sid_cache ] } { set sid_cache "" } if { ! [ info exists PROFILE_POLICY_TIMEOUT ] } { set PROFILE_POLICY_TIMEOUT [PROFILE::access access_policy_timeout] } if { ! [ info exists PROFILE_MAX_SESS_TIMEOUT ] } { set PROFILE_MAX_SESS_TIMEOUT [PROFILE::access max_session_timeout] } if { ! [ info exists src_ip ] } { set src_ip [IP::remote_addr] } if { ! [ info exists PROFILE_RESTRICT_SINGLE_IP ] } { set PROFILE_RESTRICT_SINGLE_IP [PROFILE::access restrict_to_single_client_ip] } set persisted_session_timeout [PROFILE::access inactivity_timeout] set http_method [HTTP::method] set http_uri [HTTP::uri] set http_content_len [HTTP::header Content-Length] set MRHSession_cookie [HTTP::cookie value MRHSession] if { ! [ info exists AUTHENTICATION_MODE ] } { set AUTHENTICATION_MODE {form} } switch -- [lindex $AUTHENTICATION_MODE 0] { "disable" { #################################### APM Disable ######################################################################### # disable APM and leave irule if mode is set to disable ACCESS::disable return } "form" { #################################### Form based authentication ########################################################### # Leave irule if authentication mode is set to default form base authentication if {[lindex $AUTHENTICATION_MODE 1] equals "persist"} { set inject_recover_cookie 1} else {set inject_recover_cookie 0} set apm_sessionid [HTTP::cookie value "MRHSession"] if {![HTTP::cookie exists MRHSession] || !($inject_recover_cookie && [HTTP::cookie exists MRHSession_R])} {return} } "deny" { #################################### Deny Request ######################################################################## # Respond with 403 response code and "Access Denied" content if mode is set to deny HTTP::respond 403 -version "1.1" \ content {Access Denied.} \ noserver \ "Content-Type" "text/html" \ "Set-Cookie" "MRHSession=deleted;path=/;secure" \ "Set-Cookie" "LastMRH_Session=deleted;path=/;secure" \ "Set-Cookie" "MRHSession=deleted; expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;secure" \ "Set-Cookie" "LastMRH_Session=deleted; expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;secure" return } default { #################################### All other authentication methods ##################################################### # provision response Headers for specific authentication set httpheaders "$ADDITIONAL_AUTH_HEADERS Set-Cookie \"MRHSession=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/\" Connection close" set httpcontent "ForbidenAuthentication\\ Required" set httpcode 401 foreach authvalue $AUTHENTICATION_MODE { switch $authvalue { basic { append httpheaders " WWW-Authenticate \"Basic realm=\\\"[HTTP::host]\\\"\"" } ntlm { append httpheaders " WWW-Authenticate NTLM" } negotiate { # Authentication method negociate not managed by this irule yet append httpheaders " WWW-Authenticate Negotiate" } msofba { #log local0. [HTTP::cookie value MRHSession_SP] if {[lindex $AUTHENTICATION_MODE 1] equals "persist" && [HTTP::cookie exists MRHSession_R]} { set persisted_session_timeout 300 } elseif {[HTTP::path] ne "/vdesk/ms-ofba-form"} { append httpheaders " \"Content-Type\"\ \"text/html\"" append httpheaders " X-FORMS_BASED_AUTH_REQUIRED"\ "https://[getfield [HTTP::host] ":" 1]/vdesk/ms-ofba-form?mode=[lindex $AUTHENTICATION_MODE 1]" append httpheaders " X-FORMS_BASED_AUTH_RETURN_URL"\ "https://[getfield [HTTP::host] ":" 1]/vdesk/ms-ofba-completed" append httpheaders " X-FORMS_BASED_AUTH_DIALOG_SIZE"\ "800x600" set httpcontent "ForbidenAccess\\ Denied.\\ Make\\ sure\\ that\\ your\\ client\\ is\\ correctly\\ configured.\\ See\\ https://support.microsoft.com/en-us/kb/932118\\ for\\ further\\ information." set httpcode 403 } } } } } } #################################### NTLM already authenticated connection ############################################### if {$f_ntlm_auth_succeed} { # enable ECA profile for already NTLM authenticated connections and ignore this irule event ECA::enable ECA::select $static::ECA_METADATA_ARG return } #################################### Valid session request : MHRSession Cookie ########################################### if { ( [set apm_sessionid [HTTP::cookie value "MRHSession"]] ne "" ) and ( [ACCESS::session exists -state_allow $apm_sessionid] ) } then { # Allow the successfully pre authenticated request to pass return #################################### Valid session request : MHRSession_R Cookie ######################################## ### This cokie is inserted to allow a user to log with recorded data stored on a encrypted cookie } elseif {([lindex $AUTHENTICATION_MODE 0] ne "form") and ( [set apm_sessionid [HTTP::cookie value "MRHSession_SP"]] ne "" ) and ( [ACCESS::session exists -state_allow $apm_sessionid] ) } then { #################################### Valid session request : MHRSession_SP Cookie ######################################## # Check if persistent APM session cookie is present and valid # Restore APM session cookie value HTTP::cookie insert name "MRHSession" value $apm_sessionid set inject_session_cookie $apm_sessionid # Allow the successfully pre authenticated request to pass return } elseif { [lindex $AUTHENTICATION_MODE 1] equals "persist" && [HTTP::cookie exists MRHSession_R] and ( [set session_restore_data [AES::decrypt $static::session_restore_aes_key [b64decode [HTTP::cookie value MRHSession_R]]]] ne "" )} { array set restore_data $session_restore_data if {$restore_data(timeout) > [clock seconds] } { set user_key "persist.[PROFILE::access name].$restore_data(session.logon.last.username)" if {[set apm_sessionid [table lookup -subtable APMSessionRestore $user_key]] != "" && ( [ACCESS::session exists -state_allow $apm_sessionid] ) } { #log local0. "session recover by table" } elseif { ([ llength [set cookie_list [ ACCESS::user getsid $user_key ] ] ] != 0) and [set apm_sessionid [ ACCESS::user getkey [ lindex $cookie_list 0 ] ] ] ne ""} { #log local0. "session recover by cookie list" } else { #log local0. "new session" if { [HTTP::header value "Accept-Language"] eq "" } then { # A "Accept-language" header is not present. Injecting language code = none HTTP::header insert "Accept-Language" "none" } set apm_sessionid [ACCESS::session create -timeout $persisted_session_timeout] ACCESS::session data set -sid $apm_sessionid "session.policy.result" "allow" foreach session_variable [lsearch -all -inline -not -exact [array names restore_data] timeout] { ACCESS::session data set -sid $apm_sessionid $session_variable $restore_data($session_variable) #log local0.debug "Adding $session_variable = $restore_data($session_variable)" } ACCESS::session data set -sid $apm_sessionid ".session.assigned.uuid" "tmm.uuid.$user_key" ACCESS::session data set -sid $apm_sessionid "session.user.uuid" $user_key } HTTP::cookie insert name "MRHSession" value $apm_sessionid set inject_session_cookie $apm_sessionid table set -subtable "APMSessionRestore" $user_key $apm_sessionid $persisted_session_timeout indef unset restore_data user_key return } } #################################### Request with valid Authorization Header ############################################ ### convert authentication header to be managed by APM if { [ llength [set auth_data [split [HTTP::header Authorization] " "]] ] == 2 } { if {[lsearch -exact $AUTHENTICATION_MODE [set authvalue [string tolower [ lindex $auth_data 0]]]] ne -1} { switch $authvalue { "ntlm" { ECA::enable ECA::select $static::ECA_METADATA_ARG } "basic" { set clientless(insert_mode) 1 set clientless(src_ip) [IP::remote_addr] set clientless(username) [ string tolower [HTTP::username] ] set clientless(password) [HTTP::password] if { $PROFILE_RESTRICT_SINGLE_IP == 0 } { binary scan [md5 "$clientless(password)"] H* clientless(hash) } else { binary scan [md5 "$clientless(password)$clientless(src_ip)"] 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) set clientless_mode 1 } unset clientless } "negotiate" { #log local0. [getfield [HTTP::header Authorization] " " 2] #set clientless(insert_mode) 1 #set clientless(authparam) [getfield [HTTP::header Authorization] " " 2] #log local0. $clientless(authparam) #log local0. [string length $clientless(authparam)] #set clientless(decode) [b64decode $clientless(authparam)] ##log local0. [sha256 "$clientless(decode)"] #binary scan [sha256 "$clientless(decode)"] H* clientless(hash) #log local0. $clientless(hash) #set user_key "$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 # set clientless_mode 1 # } # unset clientless } default { #other authentication methodes are not managed by this irule yet } } } #################################### Redirect unmanaged requests with supported authentication method ################## } else { eval HTTP::respond $httpcode -version 1.1 content $httpcontent noserver $httpheaders return } } when HTTP_RESPONSE { # Insert persistent cookie for html content type and private session if { [HTTP::header "Content-Type" ] contains "text/html" && [info exists "apm_sessionid"]} { HTTP::cookie remove MRHSession_SP HTTP::cookie insert name MRHSession_SP value $apm_sessionid path "/" HTTP::cookie expires MRHSession_SP 300 relative HTTP::cookie secure MRHSession_SP enable } # Insert session cookie if session was recovered from persistent cookie if { ([info exists "inject_session_cookie"]) && ($inject_session_cookie ne "") } { HTTP::cookie insert name MRHSession value $inject_session_cookie path "/" HTTP::cookie secure MRHSession enable #log local0. "injected $inject_session_cookie" } if { $inject_recover_cookie } then { # Insert APM recover session cookie into HTTP response. set session_restore_data "[clock seconds] [ACCESS::session data get "session.logon.last.username"]" foreach session_variable $static::session_restore_variables { if { [set session_variable_value [ACCESS::session data get $session_variable]] ne "" } then { lappend session_restore_data "$session_variable=$session_variable_value" } } HTTP::header insert "Set-Cookie" "MRHSession_R=[b64encode [AES::encrypt $static::session_restore_aes_key $session_restore_data]];Path=/;Secure;HttpOnly" set inject_recover_cookie 0 } if {[HTTP::header exists "Transfer-Encoding"]} { HTTP::payload rechunk } } when ACCESS_SESSION_STARTED { if {[set landinguri [ACCESS::session data get session.server.landinguri]] equals "/vdesk/ms-ofba-form?mode=persist" } { ACCESS::session data set session.server.landinguri "/vdesk/AddPersistentCookie?url=[b64encode "/vdesk/ms-ofba-completed"]" } elseif {([info exists "inject_recover_cookie"])} { ACCESS::session data set session.server.landinguri "/vdesk/AddPersistentCookie?url=[b64encode [ACCESS::session data get session.server.landinguri]]" ACCESS::session data set session.inactivity_timeout 300 } if {([info exists "clientless_mode"])} { ACCESS::session data set session.clientless $clientless_mode if {$clientless_mode} { ACCESS::session data set session.inactivity_timeout 300 if {[HTTP::header Authorization] starts_with "Negotiate"}{ ACCESS::session data set session.logon.last.authtype "Negotiate" ACCESS::session data set session.logon.last.authparam [getfield [HTTP::header Authorization] " " 2] } } } if { [ info exists user_key ] } { ACCESS::session data set "session.user.uuid" $user_key } } when ACCESS_POLICY_COMPLETED { if { ! [ info exists user_key ] } { return } set user_key_value "" set f_delete_session 0 set policy_result [ACCESS::policy result] set sid [ ACCESS::session sid ] switch $policy_result { "allow" { set user_key_value $sid set sid_cache $user_key_value log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX Result: Allow: $user_key" log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX sid = $sid" } "deny" { eval ACCESS::respond 401 -version 1.1 content {ForbidenAuthentication\ Required} noserver $httpheaders set f_delete_session 1 } default { ACCESS::respond 503 content $static::actsync_503_http_body Connection Close log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX Got unsupported policy result for $user_key ($sid)" set f_delete_session 1 } } if { $f_ntlm_auth_succeed == 1 } { if { $user_key_value != "" } { log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX Setting $user_key => $static::AUTH_POLICY_SUCCEED" table set -subtable $static::AUTH_ACCESS_USERKEY_TBLNAME $user_key $static::AUTH_POLICY_SUCCEED } else { log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX Setting $user_key => $static::AUTH_POLICY_FAILED $static::AUTH_POLICY_DONE_WAIT_SEC $static::AUTH_POLICY_DONE_WAIT_SEC in table $static::AUTH_ACCESS_USERKEY_TBLNAME" table set -subtable $static::AUTH_ACCESS_USERKEY_TBLNAME $user_key $static::AUTH_POLICY_FAILED $static::AUTH_POLICY_DONE_WAIT_SEC $static::AUTH_POLICY_DONE_WAIT_SEC } } if { $f_delete_session == 1 } { ACCESS::session remove set f_delete_session 0 log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX Removing the session for $user_key." } } when ACCESS_ACL_ALLOWED { switch -glob -- [string tolower [HTTP::path]] { "/vdesk/addpersistentcookie" { set cookie_expire_absolute [expr {[clock seconds] + $static::session_restore_timeout}] set cookie_expire_date [clock format $cookie_expire_absolute -format "%a, %d-%b-%Y %H:%M:%S GMT" -gmt true] set session_restore_data "timeout $cookie_expire_absolute" foreach session_variable $static::session_restore_variables { if { [set session_variable_value [ACCESS::session data get $session_variable]] ne "" } then { lappend session_restore_data $session_variable $session_variable_value } } set cookie [format "MRHSession_R=%s; path=/; expires=%s;Secure; HttpOnly" [b64encode [AES::encrypt $static::session_restore_aes_key $session_restore_data]] $cookie_expire_date] ACCESS::respond 302 noserver Location [b64decode [URI::query [HTTP::uri] url]] "Set-Cookie" $cookie } "/vdesk/ms-ofba-form" { ACCESS::respond 302 noserver Location "/vdesk/ms-ofba-completed" } "/vdesk/ms-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" "Set-Cookie" "MRHSession_R=deleted;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;secure" event disable TCP::close 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 "/" "Set-Cookie" "MRHSession_R=deleted;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;secure" event disable TCP::close return } } default { # No Actions } } } when ECA_REQUEST_ALLOWED { set f_ntlm_auth_succeed 1 if { $MRHSession_cookie == "" } { # Retrieve from SID cache set MRHSession_cookie $sid_cache HTTP::cookie insert name MRHSession value $sid_cache } if { $MRHSession_cookie != "" } { # Destroy session ID cache. This client should not need session ID cache if { ($sid_cache != "") && ($sid_cache != $MRHSession_cookie) } { set sid_cache "" } if { [ ACCESS::session exists -state_allow $MRHSession_cookie ] } { log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX HTTP *VALID* MRHSession cookie: $MRHSession_cookie" # Default profile access setting is false if { $PROFILE_RESTRICT_SINGLE_IP == 0 } { log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX Release the request" return } elseif { [ IP::addr $src_ip equals [ ACCESS::session data get -sid $MRHSession_cookie "session.user.clientip" ] ] } { log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX source IP matched. Release the request" return } else { log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX source IP does not matched" } } else { log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX HTTP *INVALID* MRHSession cookie: $MRHSession_cookie" } } set MRHSession "" set sid_cache "" HTTP::cookie remove MRHSession # Build user_key set user_key {} append user_key [string tolower [ECA::username]] "@" [ string tolower [ECA::domainname] ] if { $PROFILE_RESTRICT_SINGLE_IP == 0 } { append user_key ":" $src_ip } append user_key ":" [ECA::client_machine_name] set apm_cookie_list [ ACCESS::user getsid $user_key ] if { [ llength $apm_cookie_list ] != 0 } { set MRHSession_cookie [ ACCESS::user getkey [ lindex $apm_cookie_list 0 ] ] if { $MRHSession_cookie != "" } { set sid_cache $MRHSession_cookie HTTP::cookie insert name MRHSession value $MRHSession_cookie log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX APM Cookie found: $sid_cache" return } } unset apm_cookie_list set try 1 set start_policy_str $src_ip append start_policy_str [TCP::client_port] while { $try <= $static::AUTH_POLICY_RESULT_POLL_MAXRETRYCYCLE } { log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX NO APM Cookie found" log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX Trying #$try for $http_method $http_uri $http_content_len" if { $http_content_len > $static::AUTH_FIRST_BIG_POST_CONTENT_LEN } { # Wait at below } else { log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX EXEC: table set -notouch -subtable $static::AUTH_ACCESS_USERKEY_TBLNAME -excl $user_key $start_policy_str $PROFILE_POLICY_TIMEOUT $PROFILE_MAX_SESS_TIMEOUT" set policy_status [table set -notouch -subtable $static::AUTH_ACCESS_USERKEY_TBLNAME -excl $user_key $start_policy_str $PROFILE_POLICY_TIMEOUT $PROFILE_MAX_SESS_TIMEOUT] log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX DONE: table set -notouch -subtable $static::AUTH_ACCESS_USERKEY_TBLNAME -excl $user_key $start_policy_str $PROFILE_POLICY_TIMEOUT $PROFILE_MAX_SESS_TIMEOUT" if { $policy_status == $start_policy_str } { # ACCESS Policy has not started. Start one HTTP::header insert "clientless-mode" 1 set clientless_mode 1 break } elseif { $policy_status == $static::AUTH_POLICY_SUCCEED } { log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX table is out-of-sync retry" table delete -subtable $static::AUTH_ACCESS_USERKEY_TBLNAME $user_key continue } elseif { $policy_status == $static::AUTH_POLICY_FAILED } { ACCESS::disable TCP::close return } # Wait at below } log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX Waiting $static::AUTH_POLICY_RESULT_POLL_INTERVAL ms for $http_method $http_uri" # Touch the entry table table lookup -subtable $static::AUTH_ACCESS_USERKEY_TBLNAME $user_key after $static::AUTH_POLICY_RESULT_POLL_INTERVAL set apm_cookie_list [ ACCESS::user getsid $user_key ] if { [ llength $apm_cookie_list ] != 0 } { set MRHSession_cookie [ ACCESS::user getkey [ lindex $apm_cookie_list 0 ] ] if { $MRHSession_cookie != "" } { set sid_cache $MRHSession_cookie HTTP::cookie insert name MRHSession value $MRHSession_cookie log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX APM Cookie found: $sid_cache" return } } incr try } if { $try > $static::AUTH_POLICY_RESULT_POLL_MAXRETRYCYCLE } { log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX Policy did not finish in [ expr { $static::AUTH_POLICY_RESULT_POLL_MAXRETRYCYCLE * $static::AUTH_POLICY_RESULT_POLL_INTERVAL } ] ms. Close connection for $http_method $http_uri" table delete -subtable $static::AUTH_ACCESS_USERKEY_TBLNAME $user_key ACCESS::disable TCP::close return } log -noname accesscontrol.local1.debug "$static::AUTH_ACCESS_LOG_PREFIX Releasing request $http_method $http_uri" unset try unset start_policy_str } when ECA_REQUEST_DENIED { log local0. "User [ECA::username]@[ECA::domainname], Client Machine [ECA::client_machine_name], Auth Status [ECA::status]" set f_ntlm_auth_succeed 0 }
Tested this on version:
11.5- IT_TAB_F5Nimbostratus
Hello Stanislas,
We have tried this V2 code with default settings, but we received a script error on web_host.js on Windows devices when opening an Office document in Office 2016. On Mac OS the Office client hangs when we tried to edit a document. If we change the default authentication for MSOffice clients to Basic we receive an error that the document cannot be opened. Note: We are using APM Domain Mode with Multiple Autentication Domains. If we switch to APM Single Domain mode, then Basic authentication works.
How can we get MSOFBA or Basic working on Windows and Mac OS with APM Domain Mode with Multiple Autentication Domains?
Thanks for your support
Kind regards
- kevin_flynn_180Nimbostratus
Stanislas, We are having the same issue with a new deployment we are standing up. Were you able to resolve this?
- ANNimbostratus
I am still having issue with launching office app even using above iRULE. Following APM Policy I have:
START -> IP SUBNET MATCH -> Internal IP -> Don't do anything -> ALLOW Extenral IP -> Logon Page -> AD Auth -> SSO Crendetial Mapping -> Variable Assigne-> Allow Fall balck ---------------------------------------------> Denytext
- Stanislas_Piro2Cumulonimbus
Commented lines 44-47 are there to bypass APM for some client ips!
What issues do you have?
- ANNimbostratus
Thanks Stanislas Piron for your response. My issue is I don't want to use APM policy if client is coming from internal. But if client is external then give F5 login page and do NTLM SSO to backend servers.
 
I want to use same VSERVER for both internal and external. I tried disabling APM based on client IP with no success.
 
when HTTP_REQUEST { if { [IP::addr [IP::client_addr] equals 10.10.0.0/255.0.0.0] } { ACCESS::disable HTTP::cookie remove "MRHSession" HTTP::cookie remove "LastMRH_Session" } }
From below link I found other users have same issue. https://devcentral.f5.com/s/feed/0D51T00006i7aINSAY
 
Issue I am having with using your iRULE I still same error when opening any office documents. If I remove APM policy works fine.
 
- Stanislas_Piro2Cumulonimbus
Did you look at lines 44-47?
- ANNimbostratus
@ Stanislas Piron
I have uncomment following and replaced IP with Office servers IPs
`if { [IP::addr [IP::client_addr]/32 equals 10.24.104.146] or [IP::addr [IP::client_addr]/32 equals 10.24.104.147] } { set AUTHENTICATION_MODE {disable} ASM::disable return'
But no luck..
- JoeTheFifthAltostratus
I have two issues after a quick test.
I had to add this to make it work with office web apps (sharepoint 2013): if {[HTTP::header "User-Agent"] equals "MSWAC"} { ACCESS::disable ECA::disable return }
I have the same issue with the irule I wrote for my setup. And another similar error which I've always seen but never dealt with as I'm still testing my setup. Java script error when opening office docs with office client right before the logonpage. the error comes from apm script having issues with the mini ie browser. https://webapp1.com/public/include/js/web_host.js error line 35 => return (window.external && typeof window.external === 'object');
DefaultExternalWebHostImpl.prototype.isAvailable = function() { return (window.external && typeof window.external === 'object'); }
I spent part of the day working with this iRule and it seems that it may be broken in regards to using Office clients on OSX. Adding some debugging shows that they continue to attempt to authenticate after the first success, in rapid succession. The client then crashes and needs to be force closed. I'm trying to see what the difference, if any is between OSX and Windows.
Hi Stanislas,
you may want to double check your lines 211, 212 and 343. They allow an attacker to perform a TCL-injection attack by sending handcrafted HOST header values.
Remote Code Execution with TMM crash:
Host: www.[while { 1 } { set x 1 }].de
Disclosure of your AES Recovery Key:
Host: www.[b64encode [subst [b64decode JHN0YXRpYzo6c2Vzc2lvbl9yZXN0b3JlX2Flc19rZXk=]]].de
Cheers, Kai