APM 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.5- Stanislas_Piro2Cumulonimbus
Hi Kai,
the use of
is from F5 exchange irule. this irule is also used when enabling exchange profile.[ACCESS::user getsid $user_key]
this is based on a user key from username and password. if you think it is insecure, we can set a the variable session.max_session_timeout to 1 hour. it will force a new authentication every hour if the user changed his password or if the account is locked. this can be seamless as the authentication header is inserted in every request.
The main reason I filter on MRHSession_SP cookie only for non browser is to secure persistent cookie usage.
When managing persistent cookie for non browser, it will allow only non-browser to recover session cookie from sharepoint cookie. when user close the browser without logout, next user on a shared computer can access previous user session if persistent cookie is not expired.
if you manage the MRHSession_SP cookie for all user agents, the user will be allowed to close the browser, open again and access to sharepoint.
another reason I put ACCESS::session command only for clientless and OFBA clients is to solve performance issues.
ACCESS::session may be used carefully because it generate a pause in execution of irule waiting all other TMM answer about this session. executing this for every requests may cause latency. that's why I filtered first on user-agent, then on session status.
I understand that in my irule, there are static objects I can remove and fix OFBA urls and persist cookie name.
I agree your switch command to filter user-agents is more optimized (with frontage filter version), but I will keep irule architecture to stay generic for every client type.
I will update my irule soon with good points I found in yours.
Hi Stanislas,
good point to allow the MRHSession_SP persistent cookie logins only to non-browsers. I've already changed my code to include this additional security mechanism (see below). The performance impact off this change shouldn't be that much, since the
event will inject a MRHSession cookie for the very next request.HTTP_RESPONSE
I don't have any information that ACCESS::session will park the connection till every other TMM has been contacted. I thought its more like a
call where just a specific TMM (data owner) will be contacted if needed. SOL12962 does also not explain this behavior...[table]
"Note: When you run the ACCESS::session command, iRule execution on the connection will be suspended until the operation completes only if the session database record is held by another TMM; this situation allows the current TMM to retrieve the data from the other TMM before processing the remainder of the iRule. The ACCESS:: commands are available only if your BIG-IP system is licensed for the BIG-IP APM system."
Do you have additional information on the ACCESS:: connection parking behavior?
when CLIENT_ACCEPTED { set inject_session_cookie 0 } when HTTP_REQUEST { Check if APM session cookie is present and valid if { ( [set sessionid [HTTP::cookie value "MRHSession"]] ne "" ) and ( [ACCESS::session exists -state_allow $sessionid] ) } then { Allow the successfully pre authenticated request to pass } else { Enumerate explicit MS-OFBA authentication capabilities Background: https://msdn.microsoft.com/en-us/library/office/cc313069(v=office.12).aspx if { ( [HTTP::header "X-FORMS_BASED_AUTH_ACCEPTED"] equals "t" ) or ( [HTTP::header "X-FORMS_BASED_AUTH_ACCEPTED"] equals "f" ) } then { Explicit MSOFBA support detected. set authschema "ms-ofba" } else { Enumerate implicit MS-OFBA authentication capabilities switch -glob -- [string tolower [HTTP::header "User-Agent"]] "*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 authschema "ms-ofba" } "*ms frontpage*" { Legacy client detected set authschema "legacy" } "*mozilla*" - \ "*opera*" { Regular web browser detected. set authschema "browser" } default { set authschema "legacy" } } if { not ( $authschema eq "browser" ) and ( [set sessionid [HTTP::cookie value "MRHSession_SP"]] ne "" ) and ( [ACCESS::session exists -state_allow $sessionid] ) } then { Restore APM session cookie value HTTP::cookie insert name "MRHSession" value $sessionid set inject_session_cookie 1 Allow the successfully pre authenticated request to pass } else { if { $authschema eq "ms-ofba" } then { Send a MSOFBA compatible Access Denied response if { [HTTP::path] ne "/sp-msofba-form" } then { HTTP::respond 403 -version "1.1" \ content "Access Denied. Make sure that your client is correctly configured. See https://support.microsoft.com/en-us/kb/932118 for further information." \ noserver \ "Content-Type" "text/html" \ "X-FORMS_BASED_AUTH_REQUIRED" "https://[getfield [HTTP::host] ":" 1]/sp-msofba-form" \ "X-FORMS_BASED_AUTH_RETURN_URL" "https://[getfield [HTTP::host] ":" 1]/sp-msofba-completed" \ "X-FORMS_BASED_AUTH_DIALOG_SIZE" "800x600" \ "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" } } elseif { $authschema eq "legacy" } then { Send a regular Access Denied response HTTP::respond 403 content "Access denied. An unsupported client access has been detected." } else { Let the regular web browser request pass to the APM policy } } } } when ACCESS_ACL_ALLOWED { switch -glob -- [string tolower [HTTP::path]] "/sp-msofba-form" { Successfully APM authenticated request MS-OFBA request detected. Redirect to MS-OFBA return URL ACCESS::respond 302 noserver Location "/sp-msofba-completed" } "/sp-msofba-completed" { Successfully APM authenticated request MS-OFBA request detected. Sending MS-OFBA return response ACCESS::respond 200 content "AuthenticatedGood Work, you are Authenticated" noserver } "*/signout.aspx" { SharePoint SignOut signature detected. Disconnect session and redirect to APM logout Page ACCESS::respond 302 noserver Location "/vdesk/hangup.php3" } "/_layouts/accessdenied.aspx" { SharePoint AccessDenied signature detected. if { [string tolower [URI::query [HTTP::uri] loginasanotheruser]] equals "true" } then { SharePoint LoginAsAnotherUser request detected. Killing the APM session an sending redirect to www-root. ACCESS::session remove ACCESS::respond 302 noserver Location "/" return } } default { Let the authenticated request pass } } when HTTP_RESPONSE { if { [HTTP::header "Content-Type" ] contains "text/html" } then { Insert persistent APM session cookie into HTTP response. HTTP::header insert "Set-Cookie" "MRHSession_SP=$sessionid;Path=/;Secure;HttpOnly" HTTP::cookie expires "MRHSession_SP" 120 relative } if { $inject_session_cookie } then { Insert APM session cookie into HTTP response. HTTP::header insert "Set-Cookie" "MRHSession=$sessionid;Path=/;Secure;HttpOnly" set inject_session_cookie 0 } }
Note: If the F5 exchange irule uses
to offload authentication request, then its a even a bigger issue... 😞 The problem is, that you can easily bruteforce AD accounts if the offloading cache is not protected by an account lockout mechanism. You can guess passwords even if the account is already locked out and if the correct password is found you are allowed to enter or at least get a slightly different error message. A max session timeout period of 1 hour wouldn't make it better if SmartPhones polls the mailbox 24/7, isn't it?[ACCESS::user getsid $user_key]
Cheers, Kai
- Stanislas_Piro2Cumulonimbus
the use of
is not a big issue.[ACCESS::user getsid $user_key]
in ACCESS_SESSION_STARTED, the session uuid is changed from default tmm.policyname.logonname to username."md5 of user password"
if { [ info exists user_key ] } { ACCESS::session data set "session.user.uuid" $user_key }
so, when a new request comes with same username and password and without session cookie, APM will not create a new session if a previous one exists with the same username / password.
This is first created for active sync to prevent multiple access session for one user.
in the active sync, there is a option to insert in the hash the client ip address to prevent a user with multiple devices to share the same access session (this is the default behavior for active sync). this can be added in the irule.
- Stanislas_Piro2Cumulonimbus
and I confirm I had some issues about ACCESS::session with more than 30K access session on BIGIP 10250.
BIGIP 10250 use 12 TMM process, I had some request dropped when access::session did not receive session status from all tmm.
In the first version, I used ACCESS::session to get session status, get session variable (for one customer, MRHSession_SP cookie is only sent if the user check "private computer" in the logon page).
I changed the irule to limit ACCESS::session command to optimize the code.
- Stanislas_Piro2Cumulonimbus
Kai,
instead of creating a variable with previous user agent, you can check if the variable authschema exists
if { [info exists authschema] } then { Do nothing, keep previous request authschema value } elseif { ( [HTTP::header value "X-FORMS_BASED_AUTH_ACCEPTED"] equals "t" ) or ( [HTTP::header value "X-FORMS_BASED_AUTH_ACCEPTED"] equals "f" ) } then { Enumerate explicit MS-OFBA authentication capabilities Background: https://msdn.microsoft.com/en-us/library/office/cc313069(v=office.12).aspx Explicit MSOFBA support detected. set authschema "ms-ofba" } else { Enumerate implicit MS-OFBA authentication capabilities Background: https://msdn.microsoft.com/en-us/library/office/cc313069(v=office.12).aspx switch -glob -- [string tolower $last_ua_agent] "*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 authschema "ms-ofba" } "*ms frontpage*" { Legacy client detected set authschema "legacy" } "*mozilla*" - \ "*opera*" { Regular web browser detected. set authschema "browser" } default { Unknown user-agent detected. set authschema "legacy" } }
Hi Stanislas,
has a rather poor performance on TCL8.4. With TCL8.5 the performance will become much better. In addition I also tried to cover the scenario, that the UA may change on a single TCP connection. But a good catch that I could combine the two[info exists]
's into a single one. This will save some additional cycles... Thanks! 😉[if]
Cheers, Kai
- Stanislas_Piro2Cumulonimbus
Hi Kai,
I tried in version tcl 8.4 et tcl 8.5, and I confirm
is optimized in version 8.5. but[info exists]
has also poor performance in tcl 8.4.$last_ua_agent ne [set last_ua_agent XXX]
$ tclsh8.5 % set ua "Mozilla" Mozilla % set authschema "browser" browser % time { expr { [info exists authschema] } } 100000 0.32418845 microseconds per iteration % time { expr { $ua != [ set ua "Mozilla"] } } 100000 0.56738861 microseconds per iteration $ tclsh8.4 % set ua "Mozilla" Mozilla % set authschema "browser" browser % time { expr { [info exists authschema] } } 100000 0.98919 microseconds per iteration % time { expr { $ua != [ set ua "Mozilla"] } } 100000 0.73574 microseconds per iteration
I understand the user agent can change on a single TCP connection, but the client type may not change (As I know, proxy servers does not reuse TCP connection for different client connections).
thank you very much for pointing me which part of this irule I can optimize, and share your experience about irule and APM interoperability.
I hope this feature will be included in future sharepoint iapp and deployment guide.
Next step will be to add Microsoft ADAL support with future version if F5 oauth implementation will be compatible.
Hi Stanislas,
No please don't, I have to thank YOU for pointing me in the right direction. Your iRule simply rocks! 🙂
I'm rather unsure if certain SSL-Inspection Proxy may share upstream connections between different internal clients unless "Session-Based-Authentication" support is explicitly requested. At least certain HTTP Proxy will recycle connections. But since HTTP is per RFC a stateless protocol, its always better to prepare for the worst and accept the fact that certain Proxy's may recycle connections, isn't it?
Question: Rumers (e.g. sol36322151) are going around that TMOS v12+ supports TCL 8.5 for iRules. Do you know how to enable TCL8.5? If you have some sparetime left I would be glad if you could open a support call to find out. I don't have a valid support contract in my pocket... 😞
In addition I was able to repro my concerns regarding the mentioned account lockout bypass.
Behavior with APM Profile "Restrict to Single Client IP" Option =
DISABLED
1.) Access the site using a legacy client (aka. Basic Auth)
HTTP/1.0 401 Unauthorized WWW-Authenticate: Basic realm="HelloWorld - HTTP_REQUEST" Set-Cookie: MRHSession=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/ Server: BigIP Connection: close Content-Length: 0
2.) Login as valid user using credential set: test:password1+
HTTP/1.1 302 Redirect Location: /pages/default.aspx Server: Microsoft-IIS/8.0 ... Set-Cookie: MRHSession=100f33e0307120fd8f2d0e886cf2b7d1; expires=Wed, 14 Sep 2016 15:48:19 GMT;path=/;secure;HttpOnly Set-Cookie: LastMRH_Session=100f33e0307120fd8f2d0e886cf2b7d1; expires=Wed, 14 Sep 2016 15:48:19 GMT;path=/;secure;HttpOnly
3.) Login from a different host using credential set: test:password1-
HTTP/1.0 401 Unauthorized WWW-Authenticate: Basic realm="HelloWorld - ACCESS_POLICY_COMPLETED" Set-Cookie: MRHSession=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/ Server: BigIP Connection: Close Set-Cookie: LastMRH_Session=e443275f;path=/;secure;HttpOnly Set-Cookie: MRHSession=7884faddb7f22b02f86b38f4e443275f;path=/;secure;HttpOnly Content-Length: 0
4.) Repeat step 3.) multiple times to lock the user account in your repository
HTTP/1.0 401 Unauthorized WWW-Authenticate: Basic realm="HelloWorld - ACCESS_POLICY_COMPLETED" Set-Cookie: MRHSession=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/ Server: BigIP Connection: Close Set-Cookie: LastMRH_Session=3c9093b9;path=/;secure;HttpOnly Set-Cookie: MRHSession=26212f5e9bec540efa77ad963c9093b9;path=/;secure;HttpOnly Content-Length: 0
5.) Login from a different host using credential set: test:password1+ (aka. you have guessed the correct password!)
HTTP/1.1 302 Redirect Location: /pages/default.aspx Server: Microsoft-IIS/8.0 ... Set-Cookie: MRHSession=100f33e0307120fd8f2d0e886cf2b7d1; expires=Wed, 14 Sep 2016 15:48:19 GMT;path=/;secure;HttpOnly Set-Cookie: LastMRH_Session=100f33e0307120fd8f2d0e886cf2b7d1; expires=Wed, 14 Sep 2016 15:48:19 GMT;path=/;secure;HttpOnly
Behavior with APM Profile "Restrict to Single Client IP" Option =
ENABLED
1.) Access the site using a legacy client (aka. Basic Auth)
HTTP/1.0 401 Unauthorized WWW-Authenticate: Basic realm="HelloWorld - HTTP_REQUEST" Set-Cookie: MRHSession=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/ Server: BigIP Connection: close Content-Length: 0
2.) Login as valid user using credential set: test:password1+
HTTP/1.1 302 Redirect Location: /pages/default.aspx Server: Microsoft-IIS/8.0 ... Set-Cookie: MRHSession=100f33e0307120fd8f2d0e886cf2b7d1; expires=Wed, 14 Sep 2016 15:48:19 GMT;path=/;secure;HttpOnly Set-Cookie: LastMRH_Session=100f33e0307120fd8f2d0e886cf2b7d1; expires=Wed, 14 Sep 2016 15:48:19 GMT;path=/;secure;HttpOnly
3.) Login from a different host using credential set: test:password1-
HTTP/1.0 401 Unauthorized WWW-Authenticate: Basic realm="HelloWorld - ACCESS_POLICY_COMPLETED" Set-Cookie: MRHSession=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/ Server: BigIP Connection: Close Set-Cookie: LastMRH_Session=e443275f;path=/;secure;HttpOnly Set-Cookie: MRHSession=7884faddb7f22b02f86b38f4e443275f;path=/;secure;HttpOnly Content-Length: 0
4.) Repeat step 3. multiple times to lock the user account in your repository
HTTP/1.0 401 Unauthorized WWW-Authenticate: Basic realm="HelloWorld - ACCESS_POLICY_COMPLETED" Set-Cookie: MRHSession=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/ Server: BigIP Connection: Close Set-Cookie: LastMRH_Session=3c9093b9;path=/;secure;HttpOnly Set-Cookie: MRHSession=26212f5e9bec540efa77ad963c9093b9;path=/;secure;HttpOnly Content-Length: 0
5.) Login from a different host using credential set: test:password1+ (aka. you have guessed the correct password!)
HTTP/1.0 302 Found Server: BigIP Cache-Control: no-cache, no-store Connection: Close Content-Length: 0 Location: /vdesk/hangup.php3 Set-Cookie: LastMRH_Session=e443275f;path=/;secure;HttpOnly Set-Cookie: MRHSession=7884faddb7f22b02f86b38f4e443275f;path=/;secure;HttpOnly
Note: As you've already pointed out, the Exchange iRule uses an optional
for per-src_ip uuie keying. But even then, it can still be used to by-pass repository account lockouts, if you sit behind the same NAT device (e.g. same conference center, cafe, etc.). The better approach would be to implement a tight account lookout before any credential caches are used.[md5 "$apm_password$src_ip"]
Cheers, Kai
- Stanislas_Piro2Cumulonimbus
Hi kai,
¬â€
the exchange irule does not use the APM Profile "Restrict to Single Client IP" value.
¬â€
I already created a little irule to enable the use of this value with APM exchange profile:
¬â€
https://devcentral.f5.com/s/feed/0D51T00006j1zdWSAQ
¬â€
I will update the irule with the ability to limit session reuse for a single client ip like in exchange irule.
¬â€
With sharepoint, it is interesting to limit session reuse per IP. with active sync, mobile phone often change client ip as they are natted with an address pool. in this case, limiting session per client IP can create lots of session for the same device.
¬â€
Hi Stanislas,
tell me what you thing about this rather simple change. Its much more secure than any IP binding... 🙂
when RULE_INIT { set static::account_failed_auth_limit 5 set static::account_failed_auth_window 300 set static::account_lockout_duration 600 } when HTTP_REQUEST { .... if { !([HTTP::header Authorization] == "") } { set clientless(insert_mode) 1 set username [ string tolower [HTTP::username] ] if { [table lookup "$username\_lock"] ne 1 } then { set clientless(password) [HTTP::password] binary scan [md5 "$clientless(password)"] H* clientless(hash) set user_key "$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" $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 unset clientless return } } 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 } ... } when ACCESS_POLICY_COMPLETED { if { ([info exists "clientless_mode"]) && ($clientless_mode) && ([ACCESS::policy result] equals "deny") } { ACCESS::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 ACCESS::session remove if { $static::account_failed_auth_limit > 0 } then { if { [expr { [table keys -subtable "$username\_count" -count] + 1 }] >= $static::account_failed_auth_limit } { table set -notouch "$username\_lock" 1 indef $static::account_lockout_duration table delete -subtable "$username\_count" -all } else { table set -subtable "$username\_count" [clock clicks] 1 indef $static::account_failed_auth_window } } } }
Note: The provided code is recycled from one of my iRule based authentication module(s). The code is not tested in combination with your code and may contain some coding glitches...
Cheers, Kai