Posted in : Citrix, NetScaler, Other By Rasmus Kindberg Translate with Google ⟶

2 years ago

A limitation with Netscaler AAA is that it cannot handle FormData sent in a POST request to a Netscaler LB vServer that is protected by a AAA vServer. What happens is that the Form data in the POST will not be included when the user is redirected back to the LB vServer after AAA authentication. This becomes relevant in scenarios where you have a SAML ServiceProvider (SP) that is configured to do a login POST to an SAML IdentityProvider (IDP) and that IDP is protected by Netscaler AAA.
Below is the process flow:
1. User browses to the SAML SP address https://app1.somedomain.com/saml/login, which in this scenario is the URL that initiates the SAML logon process
2. The SP gives the user a SAML request and the user’s browser performs a POST against the IDP URL https://adfs.mycompany.com/adfs/ls/ with this SAML Request as the Form data.
3. The address https://adfs.mycompany.com points to a Netscaler LB vServer which is protected by AAA, so when Netscaler sees the incoming GET request above it will redirect the user to https://aaa.mycompany.com for AAA authentication (we assume the user has not authenticated against this AAA vServer this web session).
4. User performs AAA authentication, and is afterwards redirected back to the original URL https://adfs.mycompany.com/adfs/ls. HOWEVER, the SAML Request Form data is now missing.
5. User will land on https://adfs.mycompany.com/adfs/ls and receive an error message, because the ADFS server doesn’t know how to handle a request that doesn’t have any SAML form data.
 
Important notes:

  • Form Data passed along with a POST to a LB vServer, such as ADFS, that is protected by AAA will be ’dropped’ when the user is redirected back to the LB vServer after successful AAA authentication. This only applies if the user has not authenticated against the AAA in the current web session (ie the user does not have a NSC_TMAS cookie). We will make use of this later on.
  • Query values included in a POST are not ’dropped’, so this flaw is limited to Form data only.

 
Solution/work-around:
The easiest solution is to simply ask the SAML SP to use Redirect instead of POST for the SAML authentication process, but if that is not an option (the SAML SP’s backend code or configuration doesn’t support SAML Redirect) then below is a work-around I’ve been using. Basically what you do is that you store the original SP URL, https://app1.somedomain.com/saml/login, in a cookie in the user’s browser and in step 5 the user will be redirected back to this URL again.
Below is the process flow with a work-around implemented for POST:
1. User browses to https://app1.somedomain.com/saml/login, which in this scenario is the url that initiates the SAML logon process
2. The SP gives the user a SAML request and the user’s browser performs a POST against the IDP URL https://adfs.mycompany.com/adfs/ls/ with this SAML Request as the Form data.
3. The address adfs.mycompany.com points to a Netscaler LB vServer which is protected by AAA, so when Netscaler sees the incoming GET request above it will redirect the user to https://aaa.mycompany.com for AAA authentication.
3b. NEW: When the user is redirected to https://aaa.mycompany.com now, a Rewrite policy will trigger that will create a cookie ”ADFSPostCookieURL” for the user, and this cookie will contain the value ”https://app1.somedomain.com/saml/login”.
4. User performs AAA authentication, and is afterwards redirected back to the original URL https://adfs.mycompany.com/adfs/ls.
5. NEW: We have a Responder policy on our ADFS LB vServer that checks if the path is ”/adfs/ls” and if the cookie ”ADFSPostCookieURL” exists, and if both are true then we read the value in cookie ”ADFSPostCookieURL” and Redirects the user to that URL.
6. User is redirected back to https://app1.somedomain.com/saml/login, which will restart the SAML logon process
7. The SP gives the user a new SAML request and the user’s browser again performs a POST against the IDP URL https://adfs.mycompany.com/adfs/ls/ with this SAML Request as the Form data.
8. A key difference now is that the user already has done AAA authentication this web session and thus has a valid AAA cookie, and won’t be redirected to https://aaa.mycompany.com for authentication. The POST against https://adfs.mycompany.com/adfs/ls/ will therefore happen successfully and the ADFS backend server will see the SAML Form data since that has not been dropped by AAA redirect.
9. Assuming the SAML Request ticket is valid, the ADFS server will give the user a SAML Response ticket and redirect the user to https://app1.somedomain.com/myApp and the user is now logged on to this 3rd party site successfully.
 
Takeaways:

  • Our workaround revolves around storing the original url (https://app1.somedomain.com/saml/login) in some way so we can access it later, and requesting a SAML Request ticket twice from our SAML SP because in the second round we will not be bothered by AAA authentication.
  • Above solution is a bit hacky and involves requesting double SAML tickets from the SP, and there are a lot of Redirects involved, but it works well from an end-user perspective and it enables us to support SAML Post in conjunction with AAA.

 
If you have any questions regarding above solution, or ideas on how to handle above scenario in a better way, please contact me at rasmus.kindberg@xenit.se.
 
 
Below is the Netscaler configuration:

add policy patset PATSET_HOSTHEADER_ADFS
bind policy patset PATSET_HOSTHEADER_ADFS adfs.mycompany.com -index 1
add policy patset PATSET_HOSTHEADER_AAA
bind policy patset PATSET_HOSTHEADER_AAA aaa.mycompany.com -index 1
add policy patset PATSET_HOSTHEADER_ADFS_POST_REDIRECT_REFERERS
bind policy patset PATSET_HOSTHEADER_ADFS_POST_REDIRECT_REFERERS https://app1.somedomain.com -index 1
add policy expression EXP_ADV_HOSTHEADER_ADFS "HTTP.REQ.HEADER(\"Host\").SET_TEXT_MODE(IGNORECASE).EQUALS_ANY(\"PATSET_HOSTHEADER_ADFS\")"
add policy expression EXP_ADV_HOSTHEADER_AAA "HTTP.REQ.HEADER(\"Host\").SET_TEXT_MODE(IGNORECASE).EQUALS_ANY(\"PATSET_HOSTHEADER_AAA\")"
add policy expression EXP_ADV_HOSTHEADER_ADFS_POST_REDIRECT_REFERERS "HTTP.REQ.HEADER(\"Referer\").SET_TEXT_MODE(IGNORECASE).STARTSWITH_ANY(\"PATSET_HOSTHEADER_ADFS_POST_REDIRECT_REFERERS\")"
add responder action REA-ADFS_POST_REDIRECT redirect "HTTP.REQ.COOKIE.VALUE(\"ADFSPostCookieURL\")" -responseStatusCode 302
add responder policy REP-ADFS_POST_REDIRECT "EXP_ADV_HOSTHEADER_ADFS && HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).EQ(\"/adfs/ls/\") && HTTP.REQ.COOKIE.SET_TEXT_MODE(IGNORECASE).CONTAINS(\"ADFSPostCookieURL\") && HTTP.REQ.COOKIE.VALUE(\"ADFSPostCookieURL\").LENGTH > 0 && HTTP.REQ.HEADER(\"Origin\").EXISTS.NOT" REA-ADFS_POST_REDIRECT
add rewrite action RWA-RES-CREATE_ADFS_POST_COOKIE insert_http_header Set-Cookie "\"ADFSPostCookieURL=\" + HTTP.REQ.HEADER(\"Referer\") + \";Path=/;Domain=\" + HTTP.REQ.HOSTNAME.DOMAIN"
add rewrite policy RWP-RES-CREATE_ADFS_POST_COOKIE "EXP_ADV_HOSTHEADER_AAA && EXP_ADV_HOSTHEADER_ADFS_POST_REDIRECT_REFERERS && HTTP.REQ.COOKIE.CONTAINS(\"ADFSPostCookieURL\").NOT" RWA-RES-CREATE_ADFS_POST_COOKIE
bind rewrite global RWP-RES-CREATE_ADFS_POST_COOKIE 20 NEXT -type RES_OVERRIDE (rewrite policies cannot be bound to a AAA vServer, so we bind it globally  instead)
add rewrite action RWA-REQ-EXT_ADFS-XMSPROXY insert_http_header X-MS-Proxy "\"NETSCALER\""
add rewrite action RWA-REQ-EXT_ADFS-PROXYMEX replace HTTP.REQ.URL.PATH_AND_QUERY "\"/adfs/services/trust/proxymex\" + HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).PATH_AND_QUERY.STRIP_START_CHARS(\"/adfs/services/trust/mex\").HTTP_URL_SAFE"
add rewrite policy RWP-REQ-EXT_ADFS-NOOP EXP_ADV_HOSTHEADER_ADFS NOREWRITE
add rewrite policy RWP-REQ-EXT_ADFS-XMSPROXY "EXP_ADV_HOSTHEADER_ADFS && HTTP.REQ.URL.TO_LOWER.STARTSWITH(\"/adfs\")" RWA-REQ-EXT_ADFS-XMSPROXY
add rewrite policy RWP-REQ-EXT_ADFS-PROXYMEX "EXP_ADV_HOSTHEADER_ADFS && HTTP.REQ.URL.TO_LOWER.STARTSWITH(\"/adfs/services/trust/mex\")" RWA-REQ-EXT_ADFS-PROXYMEX
add rewrite policylabel RWPL-REQ-EXT_ADFS http_req
bind rewrite policylabel RWPL-REQ-EXT_ADFS RWP-REQ-EXT_ADFS-XMSPROXY 100 NEXT
bind rewrite policylabel RWPL-REQ-EXT_ADFS RWP-REQ-EXT_ADFS-PROXYMEX 110 END
add server adfs01.mycompany.local 10.101.50.51 -comment "ADFS"
add server adfs02.mycompany.local 10.101.50.52 -comment "ADFS"
add serviceGroup SG-ADFS SSL -maxClient 0 -maxReq 0 -cip ENABLED X-Forwarded-For -usip NO -useproxyport YES -cltTimeout 180 -svrTimeout 2005 -CKA NO -TCPB NO -CMP NO -appflowLog DISABLED
bind serviceGroup SG-ADFS adfs01.mycompany.local 443
bind serviceGroup SG-ADFS adfs02.mycompany.local 443
add authentication authnProfile AAA-AUPL-EXT_AAA -authnVsName AAA-EXTERNAL -AuthenticationHost aaa.mycompany.com -AuthenticationLevel 1
add lb vserver LB-EXT_ADFS_AUTH SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30 -cltTimeout 180 -Authentication ON -authnProfile AAA-AUPL-EXT_AAA
bind ssl vserver LB-EXT_ADFS_AUTH -certkeyName star.mycompany.com
bind lb vserver LB-EXT_ADFS_AUTH -policyName RWP-REQ-EXT_ADFS-NOOP -priority 100 -gotoPriorityExpression NEXT -type REQUEST -invoke policylabel RWPL-REQ-EXT_ADFS
bind lb vserver LB-EXT_ADFS_AUTH -policyName REP-ADFS_POST_REDIRECT -priority 100 -gotoPriorityExpression END -type REQUEST
bind lb vserver LB-EXT_ADFS_AUTH SG-ADFS

Tags : ADFS, NetScaler, POST, Redirect, Responder, rewrite, SAML

Personlig rådgivning

Vi erbjuder personlig rådgivning med författaren för 1400 SEK per timme. Anmäl ditt intresse i här så återkommer vi så snart vi kan.

Add comment

Your comment will be revised by the site if needed.