1 year ago
SSO to Netscaler hosted web services for internal users:
A request we receive from time to time from our Netscaler customers is that they would prefer internal users (users connected to the company’s LAN/Wifi or through VPN) to automatically get SSO when they browse to a load balanced web system (https://sharepoint.mycompany.com). One way of doing this is to simply not configure any Authentication on the LB vServer (don’t specify any AAA vServer nor any Authentication Profile), and let the backend web server handle the SSO. If the user has added https://sharepoint.mycompany.com to Local Intranet Sites or Trused Sites through Internet Explorer (or through GPO) on their local computer, and the backend web server supports either NTLM or Kerberos, then that should work fine.
Another way to achieve SSO is to configure Kerberos authentication on Netscaler (on a Authentication vServer specifically) and then have the Netscaler handle the SSO to the backend through a Traffic Policy. In 9/10 cases you can go with the first method and let the backend server handle the SSO (through Local Intranet Sites configuration), but there is at least one scenario where is isn’t possible, and that’s when the Netscaler has a SAML federation to a 3rd party and the Netscaler is acting as the Identity Provider (IDP).
When the Netscaler acts as a SAML IDP, there is no real backend server that can handle the SSO part. Instead it is the Netscaler that requires the user’s credentials so it can pass them on to the 3rd party Service Provider (SP) in a SAML ticket, and one of the few authentication methods on Netscaler that doesn’t require active input (prompt) from the user is Kerberos authentication. But even with Kerberos authentication configured, the only thing the Netscaler can extract during the authentication process is sAMAccountName, and it could be that the 3rd party Service Provider requires that the SAML IDP sends the user’s Email Address or UPN, for example. So to fully cover that scenario you need to (in addition to Kerberos) also configure LDAP Attribute/Group Extraction on Netscaler to allow Netscaler to extract AD attributes of the user’s AD account (and store them in Netscaler temporarily for later use) during authentication.
Below I will go through the AD, DNS and Netscaler configuration needed to configure Kerberos Authentication + LDAP Group/Attribute Extraction, and hopefully this will help someone trying to achieve Kerberos auth. In the scenario below the Netscaler acts as IDP in a SAML federation with a 3rd party SP, and the SP expects the user’s email address (on the user’s AD account) to be sent as part of the SAML authentication.
Some info about the environment (example/mockup values, obviously):
SP logon URL: https://3rdpartySP.company.com
Authentication vServer FQDN: aaa.rakdev.com
Authentication vServer IP Address: 10.82.22.123
AD Domain Name: rakdev.se
Key Distribution Center server: RAK-DEV-DC01.rakdev.se (10.82.21.110)
User’s computer IP: 10.82.21.111
Traffic flow when everything is in place:
I’m putting the traffic flow here at the beginning, since it might help you understand why certain configurations are needed (and if something isn’t working, where it might be wrong).
Let’s say we have a user with the account name ’rakdev\testuser1’, and the user is sitting on his domain-joined corporate laptop, the laptop is connected to the corporate wifi and the user has logged to her laptop with her account ’rakdev\testuser1’. The process flow looks like the following:
- User starts a web browser and browses to https://3rdpartySP.company.com
- The SP redirects the user to the SAML IDP (AAA vServer on Netscaler), https://aaa.rakdev.com/saml/login
- The authentication policy on the AAA vServer is a Negotiate Policy (Kerberos), so Netscaler will respond with a ”401 Unauthorized” with the headers ”WWW-Authenticate: Negotiate” and ”WWW-Authenticate ”NTLM”, which tells the user’s web browser that it has to perform an authentication, and that Negotiate (Kerberos) is supported by the AAA vServer
- Since the DNS record ’aaa.rakdev.com” is a CNAME pointing to ’aaa.rakdev.se’, the client computer will check if it able to communicate with the Key Distribution Center (abbreviated as KCD, and KCD is the module that issues Kerberos tickets in an AD domain) server in the AD domain ’rakdev.se’. If we weren’t using a CNAME pointer here, the client computer would attempt to talk the KCD in the AD domain ’rakdev.com’, which it wouldn’t succeed with since that isn’t a reachable AD domain.
- The client now requests Kerberos ticket from the KCD in AD domain ’rakdev.se’ for the SPN value ’HTTP\aaa.rakdev.se’, and if this SPN record exists then the KCD will create a Kerberos ticket for the user and decrypt it using the password of the AD service account ’netscaler-krb’. Finally the KCD will reply back with the Kerberos ticket (TGS) to user’s computer/web browser.
- Wireshark capture from user’s computer during this stage:
- The client now re-sends the HTTP request to the AAA vServer with the Kerberos ticket attached
- The AAA vServer will decipher the Kerberos ticket using the password of the AD account ’netscaler-krb’ (since we specify this password in the NegotiateAction on Netscaler, the Netscaler knows the password), and if it succeeds then the Kerberos authentication is accepted.
- From the Kerberos ticket the AAA vServer can extract the username of the end-user, and it will use that to perform an LDAP lookup in AD domain ’rakdev.se’ for the user. Depending what is configured in the LdapAction on Netscaler, Netscaler will extract the values in specified AD attributes on the user’s AD account along with group membership.
- Now that the authentication part is complete, the AAA vServer will continue with the SAML authentication and respond to the user with a SAML ticket and a 301 Redirect back to https://3rdpartySP.company.com. This SAML ticket is populated with the user’s AD email account, sAMAccountName and UPN since this is inserted in the SAMLAction through the expressions ”HTTP.REQ.USER.ATTRIBUTE(1)”, ””HTTP.REQ.USER.ATTRIBUTE(2)”, etc where Attribute(1) corresponds to Attribute1 specified in the LdapAction.
- The user follows the 301 Redirect from Netscaler and sends a HTTP request to https://3rdpartySP.company.com with the SAML ticket attached, and the SAML SP will extract the email address from the SAML ticket and match it to a user account on their side. The SP then creates a web session cookie in their web system for the user.
- User is now logged on to the 3rd party web system. At no point during these steps is the user requested to input any info – in other words, we have achieved complete SSO for internal users (sitting on domain joined computers and logged on with a domain account to their computer).
Preparing AD for Kerberos authentication on Netscaler:
To allow Netscaler to offer working Kerberos authentication, you need to create a new user account (service account) in your AD domain and tie a SPN value to this service account. The sAMAccountName and UPN values of the service account do not matter, so you can name the account as ’netscaler-krb’ or something descriptive. Be sure to set ’Password never expires’, since we will specify the password to this account on Netscaler later on, and if the password is suddenly changed without updating on Netscaler, Kerberos authentication will stop working.
When you have created the AD service account, you need to run below command on a domain joined computer and you (almost) need Domain Admin privileges to run the command. This will create a Service Principal Name (SPN) record tied to this service account, and the SPN will be used during Kerberos authentication by the user’s computer and web browser.
setspn -A HTTP/aaa.rakdev.se rakdev\netscaler-krb
setspn -l rakdev\netscaler-krb
You can also see any SPN values tied to an account through dsa.msc:
Now we’re done with AD configuration. Let’s move on to DNS configuration.
Configuring DNS for Netscaler AAA and Kerberos:
Now it gets a little bit complicated. Most likely you have a DNS record for the FQDN of your AAA authentication vServer on Netscaler. In my case, the FQDN is ’aaa.rakdev.com’. If we were to create a normal DNS record (of type ’A’) in our environment for ’aaa.rakdev.com’ and pointing it to the IP of our Authentication vServer (10.82.22.123), then the user’s web browser would attempt to talk to the Key Distribution Center (KDC) in the AD domain ’rakdev.com’, which it wouldn’t be able to reach.
With a CNAME DNS pointing aaa.rakdev.com to aaa.rakdev.se however, the user’s web browser would instead attempt to talk to the KDC in domain ’rakdev.se’ (because CNAME is magical) and request a Kerberos ticket for ’aaa.rakdev.se’. And should succeed, since the user’s computer should be able reach the KDC server in ’rakdev.se’ over the internal corporate network.
’CNAME’ type DNS entry for aaa.rakdev.com:
’A’ type DNS entry for aaa.rakdev.se:
So let’s start with creating the AAA vServer. Nothing special here.
add authentication vserver AAA-INTERNAL SSL 10.82.22.123 443 -appflowLog DISABLED bind ssl vserver AAA-INTERNAL -certkeyName wildcard.rak.com
The Kerberos configuration on Netscaler is probably new to most Netscaler administrators, however. For normal Kerberos auth, such as now, the value for the parameter ’-domain’ in negotiateAction can be anything, because it isn’t used actually. The value is mandatory in Netscaler however, hence we put ’dummy’ here. The parameter ’-domainUser’ should be the SPN value (without the ’http\’ part) we created earlier and tied to the AD service account ’netscaler-krb’.
add authentication negotiateAction AS-NEGOTIATE -domain dummy -domainUser aaa.RAKDEV.SE -domainUserPasswd <password to AD service account 'netscaler-krb'> add authentication Policy AP-NEGOTIATE -rule true -action AS-NEGOTIATE
The LDAP configuration for extracting AD attributes and group membership of the end-user looks like below. The things to take note here are
- The ”-authentication DISABLED”, which makes this purely an LDAP lookup and Netscaler won’t try to authenticate as this user against LDAP (which it can’t anyway, since Netscaler doesn’t have the user’s password in our Kerberos setup)
- The ”-Attribute1 mail” which specifies that Netscaler should extract the AD attribute ’mail’ from the user’s AD account and store it in Attribute1 (which we will make use of later in the SAML configuration)
- The ”-nestedGroupExtraction ON -maxNestingLevel 3 -groupNameIdentifier samAccountName -groupSearchAttribute memberOf -groupSearchSubAttribute CN” which will make the Netscaler also extract the user’s group memberships (down to ’3’ levels of nested AD groups)
add authentication ldapAction AAA-AS-LDAP_NEGOTIATE_GROUP_EXTRACTION -serverIP 10.82.22.120 -serverPort 389 -authTimeout 5 -ldapBase "DC=rakdev,DC=se" -ldapBindDn firstname.lastname@example.org -ldapBindDnPassword <Password to netscaler-ldap account> -ldapLoginName sAMAccountName -groupAttrName memberOf -subAttributeName CN -secType TLS -ssoNameAttribute sAMAccountName -authentication DISABLED -nestedGroupExtraction ON -maxNestingLevel 3 -groupNameIdentifier samAccountName -groupSearchAttribute memberOf -groupSearchSubAttribute CN -Attribute1 mail -Attribute2 sAMAccountName -Attribute3 userPrincipalName add authentication Policy AAA-ADV-AP_LDAP_NEGOTIATE_GROUP_EXTRACTION -rule "true" -action AAA-AS-LDAP_NEGOTIATE_GROUP_EXTRACTION
The SAML configuration on Netscaler looks like below. I won’t expand on how to configure SAML on Netscaler since this post would become too extensive, but note the ”HTTP.REQ.USER.ATTRIBUTE(X)” expression to populate the SAML ticket with values that Netscaler extracted earlier during the LDAP lookup.
add authentication samlIdPProfile SAMLIDP-PR-NAMEOF3RDPARTYSERVICE -samlSPCertName 3rdPartySPcertificate -samlIdPCertName idp.rakdev.com -assertionConsumerServiceURL "https://3rdpartySP.company.com/auth/saml2 -samlIssuerName RAKDEVNetscaler -audience "https://3rdpartySP.company.com" -NameIDFormat Unspecified -Attribute1 userEmailAddress -Attribute1Expr "HTTP.REQ.USER.ATTRIBUTE(1)" -Attribute1FriendlyName userEmailAddress -Attribute2 samAccountName -Attribute2Expr "HTTP.REQ.USER.ATTRIBUTE(2)" -Attribute2FriendlyName samAccountName -Attribute3 UPN -Attribute3Expr "HTTP.REQ.USER.ATTRIBUTE(3)" -Attribute3FriendlyName UPN -skewTime 15 -serviceProviderID "https://3rdpartySP.company.com" -SPLogoutUrl "https://3rdpartySP.company.com/auth/saml2-logout" -logoutBinding REDIRECT add authentication samlIdPPolicy SAMLIDP-PO-NAMEOF3RDPARTYSERVICE -rule "HTTP.REQ.URL.PATH.SET_TEXT_MODE(IGNORECASE).EQ(\"/saml/login\")" -action SAMLIDP-PR-NAMEOF3RDPARTYSERVICE bind authentication vserver AAA-INTERNAL -policy SAMLIDP-PO-NAMEOF3RDPARTYSERVICE -priority 100 -gotoPriorityExpression NEXT
And now, the final part, which is nFactor. nFactor is a ’new’ authentication flow on Netscaler that Citrix introduced a few years ago. Basically it allows you to chain different types of authentications one after another. For example, for 2 factor authentication you could create an nFactor policy to first perform LDAP authentication, and then perform SMS (Radius) authentication in the next step. nFactor allows us Netscaler administrators to make it more granular, such as specifying that if a user is a member of AD group ”Skip-SMS-auth”, then only the LDAP authentication of the nFactor policy will be necessary. In our case, we will use a nFactor chain with Kerberos as 1st authentication policy and the LDAP lookup as 2nd ’authentication’.
Using nFactor is actually mandatory when configuring Kerberos + LDAP group extraction because of a technical aspect. Netscaler will extract the username specified in the Kerberos ticket delivered by the user’s web browser, and the username in the Kerberos ticket will be of format ’samAccountName@[domainTheUserBelongsTo]’. In our case, it would be ’email@example.com’. The Netscaler would then need to perform a lookup in AD for a user with a UPN value of ’firstname.lastname@example.org’, but this assumes that the UPN suffix of ’testuser1’ is ’@rakdev.se’. It could very well be that the user has a different UPN suffix, and hence the LDAP lookup would fail.
With nFactor, we can tell Netscaler to extract the string part before the ’@’ letter, which would be ’testuser1’, and perform an AD lookup for a user with the sAMAccountName of ’testuser1’ instead, and this should always work. This is what makes nFactor mandatory in our case.
I don’t have room to elaborate too much on how nFactor works, but below are the things to note from the configuration:
- We need to create a new loginSchema that specifies that Netscaler should extract the string part before ’@’ character, and pass that along as the user’s username to the LDAP lookup. This loginSchema needs to be used on the policyLabel that contains the LDAP authentication. For the Kerberos policyLabel we can use the default LSCHEMA_INT.
- We create an authentication policy called ’AAA-ADV-AP_TRUE-DUMMY’ which is simply used to invoke our 1st policyLabel (which is the Kerberos authentication) from the AAA vServer. This DUMMY-policy is bound to our AAA vServer, and this starts the authentication chain. Since the policy ’AAA-ADV-AP_NEGOTIATE’ and ’AAA-ADV-AP_LDAP_NEGOTIATE_GROUP_EXTRACTION’ have ’true’ as expressions they will always be executed.
add authentication loginSchema LSCHEMA_INT_NEGOTIATE_SSO -authenticationSchema noschema -userExpression "AAA.USER.NAME.BEFORE_STR(\"@\")" add authentication Policy AAA-ADV-AP_NEGOTIATE -rule "true" -action AAA-AS_NEGOTIATE add authentication Policy AAA-ADV-AP_TRUE-DUMMY -rule true -action NO_AUTHN add authentication policylabel AUPL-ADV-LDAP_NEGOTIATE_GROUP_EXTRACTION -loginSchema LSCHEMA_INT_NEGOTIATE_SSO bind authentication policylabel AUPL-ADV-LDAP_NEGOTIATE_GROUP_EXTRACTION -policyName AAA-ADV-AP_LDAP_NEGOTIATE_GROUP_EXTRACTION -priority 100 -gotoPriorityExpression NEXT add authentication policylabel AUPL-ADV-NEGOTIATE_SSO -loginSchema LSCHEMA_INT bind authentication policylabel AUPL-ADV-NEGOTIATE_SSO -policyName AAA-ADV-AP_NEGOTIATE -priority 100 -gotoPriorityExpression NEXT -nextFactor AUPL-ADV-LDAP_NEGOTIATE_GROUP_EXTRACTION bind authentication vserver AAA-INTERNAL -policy AAA-ADV-AP_TRUE-DUMMY -priority 100 -nextFactor AUPL-ADV-NEGOTIATE_SSO -gotoPriorityExpression NEXT
- You can run Wireshark on the user’s computer with a filter of ”tcp && tcp.port == 88” to see the Kerberos traffic between the user’s computer and the KDC (kerberos traffic is always on port 88)
- Ensure the FQDN for the AAA vServer (https://aaa.rakdev.se, in my case) is added under Local Intranet Site on the end-users computers (configured manually through Internet Explorer or through GPO). If the user receives Windows Authentication prompt (picture below) when getting redirected to https://aaa.rakdev.se, and you don’t see any Kerberos traffic on port 88 on the user’s computer, then lack of Local Intranet Site config is likely.
- Another reason for below auth prompt could be that the password for the AD service account ’netscaler-krb’ specified in Netscaler doesn’t match the AD account’s actual password, and Netscaler is failing to decrypt the Kerberos ticket.
- If the user is presented with the regular Netscaler logon form (picture below), then it means that the user’s computer couldn’t manage to either contact the KCD server to get a Kerberos Ticket (perform a Wireshark trace to see the failure reason) or something is misconfigured regarding nFactor flow on Netscaler (the Negotiate policy is not being invoked, and Netscaler falls back to default authentication)
- When testing the authentication flow as a user, always use Incognito Mode (’In Private Browsing’ in IE), otherwise the web browser might use earlier cached Kerberos tickets and the user’s computer won’t request new Kerberos Tickets.
- You can view Kerberos tickets stored on the user’s computer by running ’klist’ in cmd on the user’s computer. Run ’klist purge’ to remove all current Kerberos tickets. Useful when testing/configuring.
- Netscaler doesn’t seem to support ’AES-128’ and ’AES-256’ encrypted Kerberos Tickets, so ensure that the checkboxes below on the AD service account ’netscaler-krb’ are not checked. The default encryption, RC4, should be listed on the Kerberos ticket:
The username below is ’admin.rak’, but just ignore that. It would look the same for ’testuser1’.
If you have any further questions or comments on above, feel free to reach out to my at email@example.com