Category: NetScaler

OpenID Connect token validation in Citrix ADC

I’ve previously written about how to use OpenID Connect in NetScaler and a way to use callouts to validate tokens. You can also use the function JWT_VERIFY_CERTKEY() but that requires that you (for now) keep the issuing certificate updated locally.

Another way is to setup an OpenID Connect client (OAuth Action) on Citrix ADC and enable 401 authentication in the load balancing vserver. Below is an example where the NetScaler will validate that the token sent is valid and issued by the correct provider. (I’ve used Azure AD in my example)

The only thing you have to do is send traffic with tokens (HTTP.REQ.HEADER(”Authorization”).SET_TEXT_MODE(IGNORECASE).CONTAINS(”Bearer ”)) to this LB and a session will be created in NetScaler. In my case, I’m also verifying that the user exists using a second factor to LDAP.

Try it out and all feedback is welcome!



Citrix ADC and ADM automation using Ansible

I’ve been working with Ansible more and more and been learning a lot. It’s so much fun but I also think it can help others out there with their projects. I’ve published a few blog posts regarding a few different parts of how I automate Citrix ADC (NetScaler) and Citrix ADM (NetScaler MAS), and will be holding a presentation about it at Citrix User Group Norway (CUGTech Autumn 2018) – I hope to see you there!

The blog posts I’ve published regarding this (so far) are:

I’ve learnt so much creating these playbooks and will continue to work on and perfect them. Most likely will be undergoing continuous improvement from now on! It will be great to talk about all of this next week, something I’m really looking forward to!

I hope to see at least a few of you out there test these playbooks and maybe even contribute to them or collaborate with me making them even better.



Configure Stylebook configpacks using Ansible and Citrix ADM

I’ve created an Ansible playbook to deploy configpacks to Citrix ADC (previously Citrix NetScaler) using Ansible and Citrix ADM (previously Citrix NetScaler MAS). You add the configuration to the parameters and the playbook will add configpacks using the settings you’ve defined.

Still a lot to do with this one, for example updating the configpack when the parameters has changed in the playbook.

The playbook has been published to Azure DevOps and can be found here. The readme contains the latest information.

The playbook configures the following (as of this blog post):

  • Identifies the current primary/active Citrix ADC (NetScaler)
  • Locates the active nodes instanceId
  • Identifies all Stylebooks on Citrix ADM
  • Identifies what Stylebooks will be used
  • Creates configpack if it isn’t already created
  • Verifies that the configpack is deployed without any failures

Feel free to try it out and all feedback is welcome!



Deploy Citrix ADM Stylebooks using Ansible

I’ve created an Ansible playbook to deploy Citrix ADM (previously Citrix NetScaler MAS) Stylebooks. It will upload the latest version of the stylebook, migrate existing configpacks that are using the older version and then remove the old version from MAS.

There are still a lot to do with this playbook, for example handle parameters being added to a new version and delete Stylebooks if they’ve been removed from the playbook.

The playbook has been published to Azure DevOps and can be found here. The readme contains the latest information.

The playbook configures the following (as of this blog post):

  • Logs on to MAS
  • Locates all stylebooks in files/stylebooks
  • Identifies stylbook versions
  • Uploads stylebook if it that version doesn’t exist
  • Migrates configpacks to the new version
  • Removes the old version(s) of the stylebooks

Right now, there are four stylebooks:

  • xenit-srvobject.yml – Adds one or more server objects
  • xenit-svcgroup.yml – Adds a service groups with one or more server objects
  • xenit-csvserver.yml – Adds a cs vserver
  • xenit-lbvserver.yml – Adds an lb vserver using service group and binds it to a cs vserver

Feel free to try it out and any feedback is welcome! Or maybe even do a pull request?



Citrix ADC base configuration with Ansible and Citrix ADM

I’ve created an Ansible playbook to configure a base line on Citrix ADC (previously Citrix NetScaler) using Ansible and Citrix ADM (previously Citrix NetScaler MAS). The only thing you will have to do is change the parameters in the playbook and run it.

The playbook has been published to Azure DevOps and can be found here. The readme contains the latest information.

The playbook configures the following (as of this blog post):

  • NSIP parameters
  • HA Node parameters
  • SNIP parameters
  • VLANs
  • Policy Based Routing
  • Access Lists
  • SSL profiles
  • TCP Settings
  • HTTP Profile
  • NS Parameters
  • LB Parameters
  • SNMP Parameters
  • Cache parameters
  • Compression parameters
  • NetScaler modes
  • NetScaler features
  • NTP Configuration

I hope this can be of some help and feel free to give feedback or contribute to the playbook!



Configure Citrix ADC HA pair using Ansible and Citrix ADM

I’ve create an Ansible playbook to configure two Citrix ADCs (previously Citrix NetScaler) into an HA pair using Citrix ADM (previously NetScaler MAS). The only thing you will have to do is change the parameters in the playbook and run it with the credentials and IP-addresses as parameters and you’ll have an HA pair.

The playbook has been published to Azure DevOps and can be found here. The readme contains the latest information.

The playbook configures the following (as of this blog post):

  • Creates or updates a device profile
  • Creates or updates a datacenter (mps_datacenter)
  • Adds Citrix ADC instances to Citrix ADM
  • Creates an HA pair of the Citrix ADC instances using the ns_hapair_template maintenance job
  • Configures a new rpcNode password

I hope this can be of some help and feel free to reach out if you have any feedback or questions!



Configure Citrix ADM using Ansible

I’ve created an Ansible Playbook to configure Citrix ADM (previously Citrix NetScaler MAS). Instead of configuring all the different parts using the GUI, you can now change the parameters in a configuration file and the playbook will apply and update your configuration for you – making giving you Infrastructure as Code and documentation in one place!

The playbook has been published to Azure DevOps and can be found here. Read the readme for the latest information.

What the playbook configures (or at least at the time of writing this blogpost):

  • nsroot password
  • DNS servers
  • time zone
  • system settings
  • prune policy
  • syslog purge settings
  • backup policy
  • device backup policy
  • NTP sync and servers (reboots server if required)
  • LDAP servers and enables them as external authentication servers
  • Adds groups

Feel free to try it out and get back to me with any feedback! It’s a work in progress and I’ll try to keep the information up to date in the readme.



Deploy Citrix ADC to Azure using ARM Templates and Ansible

I’ve created an Ansible playbook to deploy Citrix ADC (previously Citrix NetScaler) to Azure using ARM Templates. You can find the playbook here in Azure DevOps, see the readme for the latest information about the playbook, how to use it and the per-requisites.

The main points for creating a new one instead of using the Citrix provided ones are (or at least were):

  • Deploy HA using Availability Zone
  • Using HA Ports for internal LB
  • Generates the external LB rules based on the number of Public IPs and Ports automatically
  • Naming convention that matches all other resources

Feel free to try it out and get back to me if you have any questions!



Choosing ”HTML5 Receiver” vs ”Native Receiver” dynamically through Netscaler Rewrite Policies

After a user has authenticated on a NSGW vServer, the user will either be prompted to select which Receiver Type (HTML5 vs Native) he/she wants to use, or a choice will be made automatically depending on how well the user’s web browser manages to detect a local Citrix Receiver install. See below picture for an example of the prompt I’m referring to.

You can however get rid of below prompt, and at the same also have a mechanism that selects which Receiver Type that should be for a particular user or scenario. This is achieved through Netscaler Rewrite policies.

How does it work?

In a normal scenario, after the Receiver Type has been selected (either automatically or by user), then the cookie ‘CtxsClientDetectionDone=true’ will be created in the user’s web browser. If Native Receiver has been chosen, then the cookie ‘CtxsUserPreferredClient=Native’ will also be created. By using Rewrite Policies we can create these two cookies by ourselves for the user, and therefore suppress the prompt for the user and automatically choose which Receiver Type to use.

If HTML5 should be used, then we only want to apply the Rerwite policy ”RWP-RES-DISABLE-RECEIVER-CHECK” to suppress the prompt. When Netscaler sees that the cookie ‘CtxsUserPreferredClient’ Cookie is missing, it will default to HTML5 Receiver (this is dependent on your Storefront configuration – see further down). If we want to force the Native Receiver, we also apply the rewrite policy RWP-RES-SET-NATIVE-RECEIVER” to create the cookie ‘CtxsUserPreferredClient=Native’.

In below scenario, I have defined an Expression for my Rewrite Policy ‘RWP-RES-SET-NATIVE-RECEIVER’ to only apply if the user is connecting from IP subnet 10.240.5.0/24. You can also use ”HTTP.REQ.HEADER(\”User-Agent\”).CONTAINS(\”Chrome\”)” to only apply it to Chrome Users, or use most other type of Expressions. I tried to use HTTP.REQ.USER.ATTRIBUTE(1) and HTTP.REQ.USER.IS_MEMBEROF(\”GroupName\”) expressions, but it seems that these expressions will always evaluate to false for a Rewrite Policy bound to a VPN vServer, so they don’t work, which is a shame.

 

 

For the choice between Native Receiver and HTML5 Receiver to work, you will need to configure your Storefront so that both HTML5 and Native Receivers are possible, like below picture. If you configure ”Always use Receiver for HTML5” instead of ”Use Receiver for HTML5 if local Receiver is unavailable”, then it doesn’t matter that the cookie ‘CtxsUserPreferredClient=Native’ exists. Similarly, if you configure ”Install locally” instead of ”Use Receiver for HTML5 if local Receiver is unavailable”, then Native Receiver will always be used.

If you want want the dynamic choice between HTML5 and Native Receiver, then don’t use ”Use Receiver for HTML5 if local Receiver is unavailable” and only create the ‘CtxsClientDetectionDone’ cookie to suppress the unnecessary prompt for the user.

Feel free to email me at rasmus.kindberg@xenit.se if you have any suggestions or questions related to this blog post.



Netscaler: ADFS protected by AAA – How to handle SAML POST requests

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: