NetScaler – ADFS with AAA Authentication and SSO

Reading Time: 4 minutes

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.

ADFS enable WIA 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.

Leave a Reply

Your email address will not be published. Required fields are marked *