Table of Contents
Overview
This post will give you informations and the needed configuration for publishing a Microsoft ADFS (As it’s still not dead in the wild!) with NetScaler and using AAA for Authentication with SSO to the Backend of ADFS. It’s containing both configurations when using ADFS with SAML Relying Party Trusts and OAuth Application Groups.
As there are some authentication limits (and for some you have to use Plugins, too) when working with ADFS by native, using NetScaler AAA in front of ADFS giving you the flexibility of using your preferred logon-method and nFactor.
Also, I don’t like to push ADFS landing pages to public, browsing to https://adfs.customer.com/adfs/ls/idpinitiatedsignon gives you anonymous informations about all Relying Party Trusts the customer created, so you’re able to gain insights about which Apps (and possible corresponding security issues) are in use.
As soon as you’re authenticated to one AAA session, the cookie gives you SSO to all matching ADFS Apps.
Configuration
We have to differentiate between the used protocols by ADFS. Here’s the separate configuration for SAML and OAuth.
SAML
For SAML Relying Party Trusts, we will create two ADFS LB-vServer. All things AAA we would like to secure will use and check for /adfs/ls for the Logonpage. The second vServer will have NoAAA, so the FederationMetadata.xml (URL https://adfs.customer.com/FederationMetadata/2007-06/FederationMetadata.xml) will be available anonymous furthermore for all your connected SP’s.
#ADFS SAML Relying Party Trusts NetScaler AAA PreAuth: #replace 192.168.10.10 with ADFS-Server01 #replace 192.168.10.11 with ADFS-Server02 #replace adfs.customer.com with your ADFS-URL #replace 192.168.10.15 with your CS-vServer #replace aaa.customer.com with your AAA-URL #replace AAA_vServer_NA with your configured AAA vServer enable ns feature LB CS SSL SSLVPN REWRITE add server SRV-ADFS01 192.168.10.10 add server SRV-ADFS02 192.168.10.11 add servicegroup ServiceGroup_LB_ADFS SSL -maxClient 0 -maxReq 0 -cip ENABLED X-MS-Forwarded-Client-IP -usip NO -useproxyport YES -sp ON -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP YES bind servicegroup ServiceGroup_LB_ADFS SRV-ADFS01 443 bind servicegroup ServiceGroup_LB_ADFS SRV-ADFS02 443 add lb monitor LB_Monitor_ADFS HTTP-ECV -customHeaders "host: adfs.customer.com\r\n" -send "GET /federationmetadata/2007-06/federationmetadata.xml" -recv "adfs.customer.com/adfs/services/trust" -LRTM ENABLED -secure YES bind servicegroup ServiceGroup_LB_ADFS -monitorName LB_Monitor_ADFS add lb vserver LB_vServer_ADFS_NA SSL 0.0.0.0 0 -persistenceType NONE -cltTimeout 180 -AuthenticationHost aaa.customer.com -Authentication ON -authnVsName AAA_vServer_NA add lb vserver LB_vServer_ADFS_NoAAA_NA SSL 0.0.0.0 0 -persistenceType NONE -cltTimeout 180 add cs action CS_Action_ADFS -targetLBVserver LB_vServer_ADFS_NA add cs action CS_Action_ADFS_NoAAA -targetLBVserver LB_vServer_ADFS_NoAAA_NA add cs policy CS_Policy_ADFS -rule "HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ(\"adfs.customer.com\") && HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(\"/adfs\")" -action CS_Action_ADFS add cs policy CS_Policy_ADFS_NoAAA -rule "HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ(\"adfs.customer.com\") && HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(\"/FederationMetadata\")" -action CS_Action_ADFS_NoAAA add rewrite action rewrite_adfs_ProxyHeader insert_http_header X-MS-Proxy "\"NETSCALER\"" add rewrite action rewrite_adfs_Mex 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 rw_pol_adfs_ProxyHeader "http.REQ.URL.TO_LOWER.STARTSWITH(\"/adfs\")" rewrite_adfs_ProxyHeader add rewrite policy rw_pol_adfs_Mex "http.REQ.URL.TO_LOWER.STARTSWITH(\"/adfs/services/trust/mex\")" rewrite_adfs_Mex bind lb vserver LB_vServer_ADFS_NA ServiceGroup_LB_ADFS bind lb vserver LB_vServer_ADFS_NA -policyName rw_pol_adfs_ProxyHeader -priority 100 -gotoPriorityExpression NEXT -type REQUEST bind lb vserver LB_vServer_ADFS_NA -policyName rw_pol_adfs_Mex -priority 110 -gotoPriorityExpression END -type REQUEST bind lb vserver LB_vServer_ADFS_NoAAA_NA ServiceGroup_LB_ADFS bind lb vserver LB_vServer_ADFS_NoAAA_NA -policyName rw_pol_adfs_ProxyHeader -priority 100 -gotoPriorityExpression NEXT -type REQUEST bind lb vserver LB_vServer_ADFS_NoAAA_NA -policyName rw_pol_adfs_Mex -priority 110 -gotoPriorityExpression END -type REQUEST add cs vserver CS_vServer_ADFS SSL 192.168.10.15 443 bind cs vserver CS_vServer_SSL -policyName CS_Policy_ADFS_NoAAA -priority 10 bind cs vserver CS_vServer_SSL -policyName CS_Policy_ADFS -priority 20 add tm formSSOAction AAA-FormSSO_ADFS-SAML -actionURL "/adfs/ls" -userField UserName -passwdField Password -ssoSuccessRule true -nameValuePair "AuthMethod=FormsAuthentication" -responsesize 15000 -submitMethod POST add tm trafficAction AAA-TraffAct_ADFS-SAML-Logout -appTimeout 1 -persistentCookie OFF -InitiateLogout ON -kcdAccount NONE add tm trafficAction AAA-TraffAct_ADFS-SAML-Login -appTimeout 1 -SSO ON -formSSOAction AAA-FormSSO_ADFS-SAML -persistentCookie ON -InitiateLogout OFF -kcdAccount NONE -userExpression AAA.USER.ATTRIBUTE(2) add policy expression PolExpr_AAA_ADFS-SAML-Login "HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).STARTSWITH(\"/adfs/ls\") && ((HTTP.REQ.METHOD.EQ(GET) && (HTTP.REQ.URL.QUERY.VALUE(\"wa\").EQ(\"wsignin1.0\") || HTTP.REQ.URL.QUERY.VALUE(\"SAMLRequest\").LENGTH.GE(1))) || HTTP.REQ.METHOD.EQ(POST))" add policy expression PolExpr_AAA_ADFS-SAML-Logout "HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).STARTSWITH(\"/adfs/ls\") && HTTP.REQ.URL.QUERY.VALUE(\"wa\").EQ(\"wsignout1.0\")" add tm trafficPolicy AAA-TraffPol_ADFS-SAML-Logout PolExpr_AAA_ADFS-SAML-Logout AAA-TraffAct_ADFS-SAML-Logout add tm trafficPolicy AAA-TraffPol_ADFS-SAML-Login PolExpr_AAA_ADFS-SAML-Login AAA-TraffAct_ADFS-SAML-Login bind lb vserver LB_vServer_ADFS_NA -policyName AAA-TraffPol_ADFS-SAML-Login -priority 100 -gotoPriorityExpression END -type REQUEST bind lb vserver LB_vServer_ADFS_NA -policyName AAA-TraffPol_ADFS-SAML-Logout -priority 110 -gotoPriorityExpression END -type REQUEST
OAuth
For OAuth Application Groups, we will create two ADFS LB-vServer. All things AAA we would like to secure will use and check for /adfs/oauth2 for the Logonpage. The second vServer will have NoAAA, so the OpenID-Configuration (URL’s https://adfs.customer.com/adfs/.well-known/openid-configuration & https://adfs.customer.com/adfs/discovery/keys ) will be available anonymous furthermore for all your connected SP’s.
#ADFS OAuth Application Groups NetScaler AAA PreAuth: #replace 192.168.10.10 with ADFS-Server01 #replace 192.168.10.11 with ADFS-Server02 #replace adfs.customer.com with your ADFS-URL #replace 192.168.10.15 with your CS-vServer #replace aaa.customer.com with your AAA-URL #replace AAA_vServer_NA with your configured AAA vServer enable ns feature LB CS SSL SSLVPN REWRITE add server SRV-ADFS01 192.168.10.10 add server SRV-ADFS02 192.168.10.11 add servicegroup ServiceGroup_LB_ADFS SSL -maxClient 0 -maxReq 0 -cip ENABLED X-MS-Forwarded-Client-IP -usip NO -useproxyport YES -sp ON -cltTimeout 180 -svrTimeout 360 -CKA NO -TCPB NO -CMP YES bind servicegroup ServiceGroup_LB_ADFS SRV-ADFS01 443 bind servicegroup ServiceGroup_LB_ADFS SRV-ADFS02 443 add lb monitor LB_Monitor_ADFS HTTP-ECV -customHeaders "host: adfs.customer.com\r\n" -send "GET /federationmetadata/2007-06/federationmetadata.xml" -recv "adfs.customer.com/adfs/services/trust" -LRTM ENABLED -secure YES bind servicegroup ServiceGroup_LB_ADFS -monitorName LB_Monitor_ADFS add lb vserver LB_vServer_ADFS_NA SSL 0.0.0.0 0 -persistenceType NONE -cltTimeout 180 -AuthenticationHost aaa.customer.com -Authentication ON -authnVsName AAA_vServer_NA add lb vserver LB_vServer_ADFS_NoAAA_NA SSL 0.0.0.0 0 -persistenceType NONE -cltTimeout 180 add cs action CS_Action_ADFS -targetLBVserver LB_vServer_ADFS_NA add cs action CS_Action_ADFS_NoAAA -targetLBVserver LB_vServer_ADFS_NoAAA_NA add cs policy cs_pol_adfs -rule "HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ(\"adfs.customer.com\") && HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(\"/adfs/oauth2\")" -action CS_Action_ADFS add cs policy cs_pol_adfs_NoAAA -rule "HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ(\"adfs.customer.com\") && (HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(\"/adfs/.well-known/openid-configuration\")||HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(\"/adfs/discovery/keys\")" -action CS_Action_ADFS_NoAAA add rewrite action rewrite_adfs_Mex 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 rw_pol_adfs_Mex "http.REQ.URL.TO_LOWER.STARTSWITH(\"/adfs/services/trust/mex\")" rewrite_adfs_Mex bind lb vserver LB_vServer_ADFS_NA ServiceGroup_LB_ADFS bind lb vserver LB_vServer_ADFS_NA -policyName rw_pol_adfs_Mex -priority 110 -gotoPriorityExpression END -type REQUEST bind lb vserver LB_vServer_ADFS_NoAAA_NA ServiceGroup_LB_ADFS bind lb vserver LB_vServer_ADFS_NoAAA_NA -policyName rw_pol_adfs_Mex -priority 110 -gotoPriorityExpression END -type REQUEST add cs vserver CS_vServer_ADFS SSL 192.168.10.15 443 bind cs vserver CS_vServer_SSL -policyName CS_Policy_ADFS_NoAAA -priority 10 bind cs vserver CS_vServer_SSL -policyName CS_Policy_ADFS -priority 20 add tm trafficAction AAA-TraffAct_ADFS-OAuth-Login -appTimeout 1 -SSO ON -persistentCookie ON -InitiateLogout OFF -kcdAccount NONE add tm trafficAction AAA-TraffAct_ADFS-OAuth-Logout -appTimeout 1 -persistentCookie OFF -InitiateLogout ON -kcdAccount NONE add policy expression PolExpr_AAA_ADFS-OAuth-Login "HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).STARTSWITH(\"/adfs/oauth2\")" add policy expression PolExpr_AAA_ADFS-OAuth-Logout "HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(\"/adfs/oauth2/logout\") || HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(\"/vpn/logout\") || HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(\"/cgi/tmlogout\") || HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(\"/cgi/logout\")" add tm trafficPolicy AAA-TraffPol_ADFS-OAuth-Login PolExpr_AAA_ADFS-OAuth-Login AAA-TraffAct_ADFS-OAuth-Login add tm trafficPolicy AAA-TraffPol_ADFS-OAuth-Logout PolExpr_AAA_ADFS-OAuth-Logout AAA-TraffAct_ADFS-OAuth-Logout bind lb vserver LB_vServer_ADFS_NA -policyName AAA-TraffPol_ADFS-OAuth-Login -priority 100 -gotoPriorityExpression END -type REQUEST bind lb vserver LB_vServer_ADFS_NA -policyName AAA-TraffPol_ADFS-OAuth-Logout -priority 110 -gotoPriorityExpression END -type REQUEST
ADFS Settings for OAuth
You might noticed, for OAuth I didn’t bind the ProxyHeader rewrite policy and I didn’t used a form-based authentication action. That’s because we have to use WIA (Windows Integraded Authentication) and so ADFS should think our NetScaler is a trusted intranet source – which the NetScaler is at least mostly 😉
To get SSO to work with WIA to ADFS, you have to extend the WIA supported User-Agents and disable the extended protection feature via PowerShell.
Set-ADFSProperties -WIASupportedUserAgents @("MSIE 6.0", "MSIE 7.0", "MSIE 8.0", "MSIE 9.0", "MSIE 10.0", "Trident/7.0", "MSIPC", "Windows Rights Management Client", "Firefox/25.0", "Firefox/47.0", "Mozilla/4.0", "Mozilla/5.0") Set-ADFSProperties -ExtendedProtectionTokenCheck None
Also check if WIA is an enabled authentication method for Intranet.
Summary
I hope this post gave you some useful configuration insights about NetScaler and Microsoft’s ADFS when doing AAA PreAuth for SAML and OAuth.