Suppress MFA for a period of time
Problem this snippet solves:
This code snippet can be used if you want to suppress MFA for a period of time. This solution uses an encrypted persistent cookie, that will be set at a successful MFA logon. Upon subsequent logons the browser will send the persistent cookie (when not expired) and the cookie will be validated. When the cookie is valid, the 2nd authentication factor will be skipped.
How to use this snippet:
Create an Access Policy which uses MFA. For example see the following picture.
Note the following VPE agents in the example above: 'iRule Event - Check Cookie', 'Suppress MFA' and 'Variable Assign - Set Cookie'. These events need to be in place to cooperate with the iRule in this code snippet.
iRule Event - Check Cookie
Empty Event - SuppressMFA
Add a new Branch Rule named 'Yes' and add the following expression:
expr { [mcget {session.custom.suppressmfa.skip}] == 1 }
Variable Assign - Set Cookie
Code :
when RULE_INIT { # change passphrase below before any publishing # set seconds after which the peristent cookie expires array set static::suppress_mfa { passphrase "hEuoYjmFUpB4PcpO3bUdQtLP4ic7jjm" cookie "SuppressMFA" seconds 86400 } } when ACCESS_SESSION_STARTED { # store hash from cookie in APM variable if { [HTTP::cookie exists $static::suppress_mfa(cookie)] } { set hash [HTTP::cookie decrypt $static::suppress_mfa(cookie) $static::suppress_mfa(passphrase)] ACCESS::session data set session.custom.suppressmfa.hash $hash } } when ACCESS_POLICY_COMPLETED { # if cookie should be set, create hash and store it into a APM variable if { [ACCESS::session data get session.custom.suppressmfa.setcookie] == 1 } { set username [ACCESS::session data get session.logon.last.username] set UA [ACCESS::session data get session.user.agent] set hash [b64encode [md5 "c:$username:$UA"]] ACCESS::session data set session.custom.suppressmfa.hash $hash } } when ACCESS_POLICY_AGENT_EVENT { # check if hash from cookie matches current session hash (username and user-agent) switch [ACCESS::policy agent_id] { "checkcookie" { set username [ACCESS::session data get session.logon.last.username] set UA [ACCESS::session data get session.user.agent] set hash [b64encode [md5 "c:$username:$UA"]] if { $hash equals [ACCESS::session data get session.custom.suppressmfa.hash] } { ACCESS::session data set session.custom.suppressmfa.skip 1 } } } } when HTTP_RESPONSE { # if cookie should be set, insert an encrypted cookie containing the hash (username and user-agent) if { [ACCESS::session data get session.custom.suppressmfa.setcookie] == 1 } { HTTP::cookie insert name $static::suppress_mfa(cookie) value [ACCESS::session data get session.custom.suppressmfa.hash] HTTP::cookie expires $static::suppress_mfa(cookie) $static::suppress_mfa(seconds) relative HTTP::cookie encrypt $static::suppress_mfa(cookie) $static::suppress_mfa(passphrase) HTTP::cookie path $static::suppress_mfa(cookie) "/" HTTP::cookie secure $static::suppress_mfa(cookie) enable ACCESS::session data set session.custom.suppressmfa.setcookie 0 } }
Tested this on version:
13.0I just tested the original iRule as shared with the community and it seems to be working fine in 14.1.4. One thing I notice in your version is that you removed the conditional 'set cookie' from the HTTP_RESPONSE part. The original version only sets a cookie when an user has successfully performed the MFA. Your version sets the cookie every time the HTTP_RESPONSE is being triggered. This doesn't seem right to me.
- wbrowneAltostratus
Niels thank you for your response.
I have put my entire iRule here. I just entered the HTTP_RESPONSE originally because that is what I am having problems with. I am not sure what you mean by saying I removed the conditional 'set cookie'. I have the
if { [ACCESS::session data get session.custom.suppressmfa.setauthtable] == 1 } . I also have the line in there that sets it to ACCESS::session data set session.custom.suppressmfa.setauthtable 0 after the first response. Really the only thing that I have added was that I am creating a table called tab_amia [IP::client_addr] and added a value "Authed" and added that to the cookie check in the when ACCESS_POLICY_AGENT_EVENT. All this seems to be working accept for the actual cookie creation
when RULE_INIT { # set the cookie encryption passphrase # set the cookie name # set the encrypted value in the cookie # set seconds after which the peristent cookie expires # To see debug logs set to 1, turn off with 0. Logs can be viewed in /var/log/apm or in the TMUI under System -> Logs -> Local Traffic set static::AMIADEV_Cookie_debug 1 array set static::suppress_mfa { passphrase "pw for decryption" cookie "AMIA_MFA" value "amia" seconds "300" } } when ACCESS_SESSION_STARTED { # store hash from cookie in APM variable if { [HTTP::cookie exists $static::suppress_mfa(cookie)] } { log local0. "amia cookie exists for [IP::client_addr]" set hash [HTTP::cookie decrypt $static::suppress_mfa(cookie) $static::suppress_mfa(passphrase)] if {$static::AMIADEV_Cookie_debug } {log local0. "cookie decrypted $hash"} ACCESS::session data set session.custom.suppressmfa.hash $hash } else { table delete tab_amia:[IP::client_addr] if {$static::AMIADEV_Cookie_debug } {log local0. "no cookie found"} if {$static::AMIADEV_Cookie_debug } {log local0. "cookie name expected $static::suppress_mfa(cookie)"} } } when ACCESS_POLICY_AGENT_EVENT { # check if hash from cookie matches encrypted value switch [ACCESS::policy agent_id] { "checkauthed" { set checked [table lookup tab_amia:[IP::client_addr]] if { [ACCESS::session data get session.custom.suppressmfa.hash] equals $static::suppress_mfa(value) and $checked contains "Authed" } { ACCESS::session data set session.custom.suppressmfa.skip 1 } } } } when HTTP_RESPONSE { # if table shoud be set then take record of the ClientIP and set encrytped cookie if { [ACCESS::session data get session.custom.suppressmfa.setauthtable] == 1 } { table set tab_amia:[IP::client_addr] Authed $static::suppress_mfa(seconds) set taba [table lookup tab_amia:[IP::client_addr]] if {$static::AMIADEV_Cookie_debug } {log local0. "$taba"} HTTP::cookie insert name $static::suppress_mfa(cookie) value $static::suppress_mfa(value) path "/" HTTP::cookie expires $static::suppress_mfa(cookie) $static::suppress_mfa(seconds) relative HTTP::cookie secure $static::suppress_mfa(cookie) enable HTTP::cookie httponly $static::suppress_mfa(cookie) enable HTTP::cookie encrypt $static::suppress_mfa(cookie) $static::suppress_mfa(passphrase) HTTP::header "Cache-Control" "max-age=$static::suppress_mfa(seconds)" HTTP::close ACCESS::session data set session.custom.suppressmfa.setauthtable 0 } }
- wbrowneAltostratus
I was able to get this fixed with the help of a colleague. No matter what we tried in the response it always worked for the first person but no one after. We took the when HTTP_RESPONSE out completely and added a when ACCESS_POLICY_COMPLETES.
when ACCESS_POLICY_COMPLETED { if { [ACCESS::session data get session.custom.suppressmfa.setauthtable] == 1 }{ set sessionauth [ACCESS::session data get session.custom.suppressmfa.setauthtable] if {$static::AMIADEV_Cookie_debug } {log local0. "AMIA set auth table is $sessionauth"} table set tab_amia:[IP::client_addr] Authed $static::suppress_mfa(seconds) set taba [table lookup tab_amia:[IP::client_addr]] if {$static::AMIADEV_Cookie_debug } {log local0. "$taba"} HTTP::cookie insert name $static::suppress_mfa(cookie) value $static::suppress_mfa(value) path "/" if {$static::AMIADEV_Cookie_debug } {log local0. "cookie $static::suppress_mfa(cookie) set for $static::suppress_mfa(seconds)"} HTTP::cookie expires $static::suppress_mfa(cookie) $static::suppress_mfa(seconds) relative if {$static::AMIADEV_Cookie_debug } {log local0. "cookie expires in $static::suppress_mfa(seconds)"} HTTP::cookie secure $static::suppress_mfa(cookie) enable HTTP::cookie httponly $static::suppress_mfa(cookie) enable HTTP::cookie encrypt $static::suppress_mfa(cookie) $static::suppress_mfa(passphrase) ACCESS::respond 302 noserver "Location" [ACCESS::session data get session.policy.result.start_uri] "Cache-Control" "no-cache, must-revalidate" Set-Cookie "$static::suppress_mfa(cookie)=[HTTP::cookie $static::suppress_mfa(cookie)];path=/;secure;httponly;Max-age=$static::suppress_mfa(seconds)" if {$static::AMIADEV_Cookie_debug } {log local0. "policy completed"} foreach aHeader [HTTP::header names] { if {$static::AMIADEV_Cookie_debug } {log local0. "$aHeader: [HTTP::header value $aHeader]"}} unset sessionauth unset taba } }
- HarriNimbostratus
Totally overkill to play with cookies. Just use session table with the table-command to add username to table when mfa is first successful and inspect the table in the beginning of the policy in irule agent to set up a suppress variable.