Sunday, March 19, 2017

Passing Parameters to XenDesktop by Rewriting the StoreFront ClientName


A recent client had the following business requirements:

1) To logon, enumerate and launch a XenDesktop from within their corporate intranet, and

2) To pass department/project information to the desktop. This was necessary so that the correct network drives were mapped for the session.

The first requirement was easily met by using the StoreFront Web API to embed StoreFront functionality into their intranet. The second requirement, however, was a bit more involved, as the same user could possibly be working on multiple projects simultaneously, and would therefore require different drive mappings for each.

Enter the StoreFront Store Customization SDK. The Store Customization SDK has predefined "customization points" which allow you step in and apply custom logic to modify enumerated resources, change launch parameters, and to modify device information. 

You can download the latest version of the Store Customization SDK here.

For an introduction to the SDK, see Citrix Principal Architect Simon Frost's blog post: Introducing the StoreFront Store Customization SDK. You can also check out his Synergy Developer Exchange Series video StoreFront Store Customization SDK.
One such field that can be modified via the Store Customization SDK is the session Clientname, which is easily accessed from within the XenDesktop session. Simon made modification of the Clientname quite easy with the release of his blog post and accompanying DLL Rewriting the Session ClientName from StoreFront. Alas, none of the many tokens defined in the DLL would support passing a dynamic parameter such as department or project information. However, since Simon was kind enough to also supply the source for his DLL, I was able to satisfy that business requirement by extending his DLL to incorporate HTTP headers into the ClientNameRewriteRule.

Simply use the token $H’header name’ (single quotes) within the ClientNameRewriteRule where you would like the value of the specified header placed (please refer to his blog post for a detailed explanation of the ClientNameRewriteRule).

For example, if I wanted the ClientName to contain the user’s roaming status (“I” for internal, and “E” for external), and the value of the Department header, the rule might look something like:

<appSettings>

    <add key=”clientNameRewriteRule”  value=”$R-$H’Department’ “ />

</appSettings>



If the value if the Department header was Dept007, then the ClientName would look like:

 
You must keep in mind the ClientName restrictions - the total length of the result must be 20 characters or less – anything longer will be truncated, and may not contain any of the following characters: “/ []:;|=\,+*?<>.
You must also set the value of overrideIcaClientName to “On”. For newer versions of StoreFront, you can find the setting in the GUI under Configure Store Settings | Advanced Settings. For older versions of StoreFront, see Simon’s blog post for manually modifying the configuration file (always make sure that the GUI console is not open when manually modifying any of the configuration files).
There is one final (but essential) requirement. For security reasons, StoreFront will not forward any unknown headers, so you must add your new header to the StoreFront whitelist:
  • Backup web.config in your <Store>Web directory.
  • Find the <communication> section (under <webReceiver> | <serverSettings>).
  • Add the highlighted lines:

  • Save the file ... an IISRESET is not required.


Sunday, January 29, 2017

Adding an EULA for AAA Login


With the release of NetScaler version 11, administrators have the option of configuring an End User License Agreement (EULA) that users must accept before being allowed to log on to the NetScaler. Configuring the EULA adds a checkbox to the logon page:



Note that the Log On button is grayed out until the EULA is accepted. The exact wording (what the user sees when clicking on the Terms and Conditions hyperlink) are configured by the administrator.

1) To create the EULA using the GUI, go to NetScaler > NetScaler Gateway > Resources > EULA (If you’ve already created and globally bound your EULA, skip to step 11).


2) Click Add.


3) Give your EULA a name (your can have multiple EULAs and switch between them), enter the text of the EULA, and click OK. Note that you can enter HTML codes to format your text.

4) Now, if we wanted the EULA for a specific Access Gateway vServer, we would bind the EULA under the Advanced Settings of the gateway vServer.


Here, however, we want to bind the EULA globally, so we head over to NetScaler > NetScaler Gateway > Global Settings.


5) Click on Configure an End User License Agreement.


6) Click Add Binding.


7) Click to select the EULA.


8) Select the EULA you just created...


9) ... and click Bind.


10) The EULA is now bound globally. Click Close.

11) If you browse to the FQDN of your gateway vServer, you should now see the screen shown at the top of the post. Note: If you don’t see the checkbox and hyperlink, see step 15 below.

12) If you click on the Terms and Conditions hyperlink, you should see:


Notice that I specifically said the FQDN of your gateway vServer. Since the EULA is bound globally, you would expect to see the same thing for an AAA login page (you can see what an AAA login page looks like by simply replacing index.html in the URL with tmindex.html). While the actual login page looks the same, clicking on the Terms and Conditions hyperlink for the AAA login page shows:


What happened? It seems that there is an “unanticipated feature” (a “bug” in layman’s terms) in the AAA logic. Is there a workaround? Of course there is (or I wouldn’t be writing this post! :))

13) Using your favorite SCP editor, go to the /resources directory of the theme you are using, and make a backup of the language XML file(s) that you are using (e.g. en.xml for English). Open up the XML file and look for Partition id=”logon” (which contains the text strings for the logon page).


Now, the id for the EULA text is agreement, so to add the EULA, we need to add a line containing that id to the file. Since the entire string must be contained on a single line, if you wish to keep the same formatting as above, you might be tempted to simply add the following line:


Unfortunately, the above will not accomplish what you want. In fact, if you browse to the FQDN of the site, you will now see only:


Yup ... just a blank page. That’s because you need to HTML-encode the < and > symbols.

14) Simply replacing each < (less than symbol) with &lt; and each > (greater than symbol) with &gt; (the semi-colon is mandatory) will do the trick. So the final line will look like:


15) It has come to my attention that certain versions of the NetScaler may not display the EULA checkbox and hyperlink, even if the EULA is bound globally. If this is the case, you will need to make a source-code modification to one of the JavaScript files.

Disclaimer: Source-code modifications are not supported by Citrix Support, and you may be asked to reverse any changes made. The below modification is provided as is. There is no guarantee that it will work in your environment. Test out any changes on a non-production system first, and keep a backup of the original file(s).

16) Make a backup copy of /netscaler/ns_gui/vpn/js/tmindex_view.js. Open the file and search for the highlighted line below:


17) Insert the highlighted line:


18) Save the file. Clear your cache and refresh the page (you may need to wait 2 mins for the NetScaler cache to purge). You should now see the checkbox and hyperlink.

19) In order to survive a reboot, you will also need to copy the modified tmindex_view.js file to /var/vpn/vpn/js (if the /js directory does not exist, create it).

20) If the NetScaler is part of an HA pair, you will need to copy the modified file into both directories of the secondary appliance, as source-level modifications are not synched between nodes.

Sunday, December 18, 2016

Adding Text, Links and Other Elements to the NetScaler Logon Page - Part 2


As we discussed in Part 1 of this post, there are three categories of NetScaler customizations:

1) Customizations that do not require any rewrite policies/actions (“policies”) or source code modifications (“modifications”),

2) Customizations that can be accomplished using either policies or modification, and

3) Customizations that will most probably need modification of the source code.

In Part 1, we focused on the DefaultGreen Bubble, and X1 (the "original") themes. In part 2, we will discuss the newest theme - Receiver for Web UI (RfWebUI), which uses a completely new mechanism.

While the logon page looks like the X1 theme:

... if you will be using the RfWebUI theme, you can forget pretty much everything you've learned about customizing the NetScaler logon page.  

Let's begin with the location of the logon page. When using the original themes, the logon page (index.html) was served from the  /netscaler/ns_gui/vpn/ directory. With the RfWebUI theme, the file is now served from /var/netscaler/logon/LogonPoint/. The good news is that since the page is now in the /var partition, any modifications to the page (or copies of the logon page) are automatically persisted across a reboot. No longer do you need to add lines to rc.netscaler to copy modified files into the flash partition.

The next major change is how you modify text. The portal wizard allows you to easily make changes to commonly updated text. Let's say you wanted to change the labels on the logon screen. After clicking OK at the bottom of the wizard, you will then be prompted for the language you wish to use:
After clicking OK, you will see the pages you can modify on the right-hand side of the page.   
Click Login Page. You are presented with the default labels for the page:

Let's modify them as follows:



Click Ok.


Finally, click Done. Now, let's refresh the page (you may need to clear your cache).

If you've had experience customizing the original NetScaler themes, at this point you are probably thinking "Tell me something that I don't already know ... this is no different than modifying any of the other themes"! Not quite ... The portal wizard functions differently depending on which theme has been chosen. For the original themes, the portal wizard would modify the values in the appropriate XML file in the resources directory (e.g. /var/netscaler/logon/themes/<theme>/resources/en.xml). The keys affected are located under the Partition ID of Logon. Now, what if you wished to modify the text of the logon button above, which you can't do from the portal wizard? No problem. In the same file, as few lines down, we find:
<Property id="Log_On" property="value">Log On</Property>
Simply change it to:
<Property id="Log_On" property="value">Click to log on</Property>


... and then save the file.

Try that with a theme based on RfWebUI and you will find that it doesn't work. In fact, if you look at the en.xml file in the resources directory of a theme based on RfWebUI, you will notice that the portal wizard did *not* modify ANY of the keys in that file. What gives??

For RfWebUI-based themes, those text changes are written to /var/netscaler/logon/themes/<theme>/strings.<language code>.json:



The portal wizard writes all the values on a single line. I would suggest placing each override on a separate line for readability and maintainability. Also note that this "key":"value" format differs from the XML format of the other themes. 

In order to change other text elements on the page, you will need to get the key to be placed in the override file. The keys may be found in the file /var/netscaler/logon/LogonPoint/receiver/js/localization/<language code>/ctxs.strings.js. The key for the logon button is nsg_LogOnbutton. Let's add an override for it in the above file:

Then save the file, and refresh the page. In addition to clearing the cache on your PC, it may take 2-3 minutes for the cache to be cleared on the NetScaler.



Now, let's try to add our own custom text fields as we did with the X1 theme in part 1.

Here we come to another major difference. In the original themes, we would:

- Add a <div> with a unique id (either via a rewrite action/policy or a modified gateway_login_form_view.js file),

- Add a string with the <div>'s id to the XML file in the resources directory, and finally 

- Add a CSS selector with the <div>'s id to the custom.css file in the selected theme.

Note that everything is keyed off the id of the <div>.

With RfWebUI-based themes, text is keyed off of class names, and the class name must begin with _ctxstxt_. So, to add the "authorized users only" warning (see Part 1), we make a copy of the index.html file (let's call it custom.html), and add our custom <div>:



We then add an entry with the text into strings.en.json:



We also need to add style information, but instead of adding it to custom.css, for RfWebUI-based themes, it must go into theme.css. Here you can either use the <div>'s id (if specified), or the class name, which is required (include the _ctxstxt_ prefix).



We can test by browsing to our custom.html file.



Let's not forget our footer ... add another custom <div>:



Then add the text:



... and then the CSS:



Browsing to our custom page:



Our final step is to create a responder policy and bind it to our AG vServer. This will automatically invoke our custom page when we browse to the AG vServer FQDN:

> add responder action "Logon Page Redirect Action" redirect "\"custom.html\"" -responseStatusCode 302
> add responder policy "Logon Page Redirect Policy" "HTTP.REQ.URL.PATH_AND_QUERY.CONTAINS(\"LogonPoint/index.html\")" "Logon Page Redirect Action"
> bind vpn vserver "AG vServer" -policy "Logon Page Redirect Policy" -priority 100 -gotoPriorityExpression END -type REQUEST
> save config

To remove the customizations, simply unbind the responder policy.

Here’s a handy cheat-sheet summarizing all of the above:


Original Themes
(Default, Green Bubble, X1)
Receiver for Web UI
(RfWebUI) Theme
Logon page directory
/netscaler/ns_gui/vpn/
/var/netscaler/logon/LogonPoint/
Updates to text strings
/var/netscaler/logon/themes/<theme>/
      resources/<language code>.xml
/var/netscaler/logon/themes/
      <theme>/strings/<language code>.json
String format
<String id=”key”>Value</String>    OR
<Property id="key"
      property="property">Value</Property>

“key” : “value”
Custom text based on
<div> ID
class name (existing keys are in /var/netscaler/
   logon/LogonPoint/receiver/js/localization/
   <language code>/ctxs.strings.js
CSS file
custom.css
theme.css


Thanks to the NetScaler development team for their help, and especially Bidyut H.

Monday, November 28, 2016

Adding Text, Links and Other Elements to the NetScaler Logon Page - Part 1


There have been a number of posts, discussions, and KB articles on adding text and links to the NetScaler logon page, such as:


 In this 2-part post, I will divide such customizations of the login page into three categories:

1) Customizations that do not require any rewrite policies/actions (which we’ll call “policies” for brevity) or source code modifications (“modifications”),

2) Customizations that can be accomplished using either policies or modification, and

3) Customizations that will most probably need modification of the source code (usually gateway_login_view.js and/or gateway_login_form_view.js).

In part 1, we will focus on the Default, Green Bubble, and X1 NetScaler 11 themes. In part 2, we will discuss the newest Receiver for Web UI (RfWebUI) theme, which uses a completely new mechanism, which is more similar to modifications made for StoreFront.

First off, regardless of whether modifications are made via policies or modifications, the policies or modifications should be minimized to whatever extent possible. There should never be style information or text strings in the policy or modification. Style information should be confined to the custom.css file, and strings should be placed into the appropriate XML file(s) in the resources directory of the theme, especially if you need localization (multi-language support).

So, for example, instead of:

<div style=’font-family: arial,helvetica,sans-serif;color:white;font-weight:bold;’>WARNING: Use of this system is limited to authorized users. Activities on this system are monitored, and subject to audit.</div>

 ... the policy or modification itself should only contain:

<div id=’unauthorizedUse’ />

Then, add the following to custom.css:

#unauthorizedUse {
      font-family: arial,helvetica,sans-serif;
      color:white;
      font-weight:bold;
}


... and the following to en.xml (with appropriate entries in other language files):

<String id="unauthorizedUse"> WARNING: Use of this system is limited to authorized users. Activities on this system are monitored, and subject to audit.</String>
Let's take it a step further. As mentioned above, the first category of customizations do not require any policies or modifications at all. Included in this category would be the addition of a few lines of footer text (with or without links), such as the customization above. For example, I can create the following:




 ... by adding the following to the bottom of custom.css:

#logonbelt-bottomshadow,
#logonbelt-bottomshadow a {
   text-align: center;
   color: #FFFFFF;
   font-size:12pt;
}   
#logonbelt-bottomshadow a {
   font-weight: bold;
   text-decoration: underline;
} 


... and adding the following text to en.xml (all on one line):

<String id="logonbelt-bottomshadow">WARNING: Use of this system is limited to authorized users. This system contains confidential and proprietary information. Any unauthorized trespass into or use of this system is prohibited. Any such unauthorized trespass or use may be referred to law enforcement agencies for criminal prosecution and may subject you to civil penalties. Activities on this system are monitored and recorded, and subject to audit.&lt;br&gt;Please refer to the firm's &lt;a href='#'&gt;Acceptable Use Policy&lt;/a&gt; for additional details.</String>

The only item you need to be aware of, is that all HTML codes must be converted to their HTML-encoded equivalents. For example:

<a>     
&lt;a&gt;
</a>
&lt;/a&gt; 
<b>
&lt;b&gt;
<br>
&lt;br&gt;

Note that I am not using any policies or modifications for the above. I was able to do this by leveraging the built-in <div> called logonbelt-bottomshadow. There is also a <div> called logonbelt-topshadow that one can use to add text and links above the logon box.

So, when would a policy or modification be called for? When you need to add extra elements to the page. If, for instance, I wanted to add a footer to the previous example:


... I could inject a <div> called footer (following our "Keep It Small" rule, the added code would simply be <div id='footer' />), and then add <String id="footer">myCUGC.org - All Rights Reserved.</String> to en.xml, and the following to custom.css:

#footer {
   position: absolute;
   bottom: 5px;
   height: 30px;
   font-size: 12px;
   color: white;
   font-weight: bold;
   text-align: center;
   width: 100%;
   background-color: black;
   padding-top: 5px;
}


Finally, there will be times where the number of modifications, or their complexity make using policies impractical. In those cases, it makes more sense to use modifications. Consider the following example:

First, there are all the added elements (color bars and the extra text on the bottom of the screen). Then there are the additional buttons which will need to be backed by JavaScript calls. While it is certainly possible to use policies for all the elements, it would be much simpler to use modifications in a case like this.

When you use modifications, do not modify the original .js file. Instead, make a copy of the original, and make your modifications to the copy. You can then use a rewrite action and policy to swap the modified file for the original.  For example:

add rewrite action act_login_form_replace replace_all "HTTP.RES.BODY(120000)" q{"custom_login_form_view.js"} -search q{text("gateway_login_form_view.js")}

add rewrite policy pol_login_form_replace "HTTP.REQ.URL.EQ(\"/vpn/index.html\")" act_login_form_replace

bind vpn vserver "AG vServer" -policy pol_login_form_replace -priority 200 -gotoPriorityExpression NEXT -type RESPONSE 

You also need to make sure your modified file survives a reboot, so you need to also copy your file into /var/vpn/vpn/js.

Sunday, July 31, 2016

Configuring Duo Integration With NetScaler

The purpose of this blog post is to explain the two modes of Duo integration with the NetScaler, to point out the pros and cons of each method, and to explain the different configurations needed for NetScaler and StoreFront when using each mode. I will not go into the configuration of the Duo proxy itself, as that is covered quite well (except where noted – see below) in the online Duo documentation (links below).

Having said that, there is one extremely important Duo configuration issue that I must mention. Do NOT use Notepad when editing the authproxy.cfg file! Use either Notepad++ or Wordpad. The impetus in creating this document was caused by wasting a good portion of the day bouncing back and forth between Citrix Tech Support and Duo Tech Support, when the only thing wrong was that Notepad inserted extra line breaks (one right in the middle of the RADIUS secret key!) in the config file.

Mode 1 – ad_client
In this mode, Duo performs ALL authentication. It communicates with Active Directory to validate the AD password, and, once validated, sends the user the push, call, or SMS. Duo configuration for this mode is explained here: https://duo.com/docs/citrix_netscaler.

In this configuration, the NetScaler needs only 2 RADIUS profiles and policies – one for Web access, and the other for Receiver access. Both are defined as PRIMARY - there are no secondary profiles/policies. The above link explains the profiles and policies in detail. Take note that the port for the Receiver authentication server should be different (e.g. 18120) than the standard RADIUS port (1812) used for the Web authentication server, and must match the port defined in the Duo config file.

One important point omitted by the Duo documentation is the configuration of StoreFront. When using ad_client mode, you must ensure that the Logon Type in the StoreFront Gateway Appliance Authentication Settings is set to Domain.



PROS

Users see only a single password field for both web and Receiver, so you do not need to hide the second password field (for the web), and there is much less confusion when using Receiver.


You also only need to define 2 RADIUS policies, making configuration slightly easier.

CONS
Since authentication is not being handled by the NetScaler, users cannot change passwords using this method.


Mode 2 – duo_only_client (referred to in Duo documentation as the Alternate Configuration)

In this mode, the NetScaler performs Active Directory authentication, with Duo handling only the 2nd factor (RADIUS) authentication – hence the name duo_only_client. This mode is a bit more complicated to set up on the NetScaler. While the Duo documentation of this mode (https://duo.com/docs/citrix_netscaler-alt) explains the Duo portion of the setup well, do NOT use this document to set up your NetScaler policies. This document describes setting up two SECONDARY RADIUS policies, which will NOT work if you are using Receiver. Instead, refer to the following Duo article: https://duo.com/docs/citrix_netscaler-faq (see the section: Why might mobile Receiver clients have issues authenticating with Duo?). This will refer you to the following Citrix KB article: http://support.citrix.com/article/CTX125364, which explains how and why you need to set up 2 LDAP and 2 RADIUS policies and profiles. Note that the session profile for the Receiver must have the Credential Index set to SECONDARY.

Again, the Duo documentation omits the setup of StoreFront. When using duo_only_client mode, you must ensure that the Logon Type in the StoreFront Gateway Appliance Authentication Settings is set to Domain and security token.



If you leave the Logon Type at the default Domain setting, logon via the web will work (since this setting is not used by the NetScaler), but Receiver (which does use this setting) will not work.

PROS


Since the NetScaler is performing Active Directory authentication, users may change their password (as long as the LDAP authentication server is using either TLS or SSL – not PLAINTEXT).

CONS

Using this mode, users will see two password prompts for web and Receiver:


While the unused second password field may be hidden for the web (see the Duo alternate configuration link above), this requires modification of the NetScaler source files - unsupported by Citrix, and, if not done carefully, may corrupt your NetScaler configuration.  The second password field for the Receiver may NOT be hidden, and the user must enter a valid Duo factor name (push – recommended, or phone) into that field. This can get quite confusing for users. The NetScaler configuration for this mode is also a bit more complicated.

Monday, June 27, 2016

Synergy 2016 CTP Swag

It's hard to believe that Citrix Synergy 2016 is now behind us! It seems like it was only a few weeks ago that I was submitting my abstracts. It was an amazing event, and it seemed like there were quite a bit more new/updated product announcements than usual.

 NetScaler MAS

One of the most exciting announcements for me was NetScaler MAS (Management and Analytics System). If you haven't yet heard about NetScaler MAS, check out this excerpt from the Day Two General Session by CTO Abishek Chauhan.


HDX Ready Raspberry Pi

Another announcement that's shaking up the thin client world was the introduction of the HDX Ready Raspberry Pi. At $70 (plus the cost of a keyboard, mouse, and monitor), you don't need staff to maintain these devices. Should one fail, simply dump it, and replace it with another! Look at how small the device is - it's barely longer than a room key card:  



Networking

As impressive as the announcements were, I enjoy Synergy mostly for the networking. As a CTP, I get to spend two precious days prior to Synergy networking with 50 of the most Citrix-savvy individuals, as well as Citrix product managers, discussing technologies and product updates. Check out this link for more information on the Citrix Technology Professionals program. Recognizing that there are other individuals who may wish to contribute to the community, this year at Synergy, Citrix launched another tier of professionals - Citrix Technology Advocates.


Match.Geek

I also enjoyed participation in the Match.Geek program at Synergy, during which I was able to connect with 4-6 attendees in 30-minute discussions (much along the lines of technology speed-dating!) . Although my areas of expertise are gateway products (NetScaler, StoreFront, Web Interface) and development, any and all questions were still welcomed.


Breakout Session

I was also honored to present a breakout session this year on NetScaler Debugging and Troubleshooting Best Practices. If you missed it in person, have no fear. You can still view the event on SynergyTV, or via YouTube. The SynergyTV recordings this year have swappable picture-in-picture technology, which you can control. I hope you filled out your survey if you were at the session, as I take all feedback to heart (yep, even the constructive criticism). As promised during the session, here is some swag from Synergy - CTP style - a ShareFile link containing my full session presentation (including speaker notes), as well as the NSBackup utility (for backing up critical NetScaler files) discussed during the session:

SYN317 - NetScaler Debugging and Troubleshooting Best Practices - Full Presentation and NSBackup Utility. (please refer to the package contents document contained within).

Thursday, February 4, 2016

Welcome Citrix Technology Professional (CTP) Class of 2016!

Wow! It's hard to believe that a whole year has passed since I was honored by being elected to the CTP Class of 2015. I am also thrilled to have been re-accepted to the program for 2016. It’s been a fantastic year!

What is the CTP Program?

For those who may not be aware, the CTP Program is a group of only 50 individuals worldwide who have invested a significant amount of time and resources to become experts in Citrix products and solutions. The wealth of knowledge they have developed - and more importantly, openly shared - has proven invaluable to the Citrix technology community.  They have consistently demonstrated their real-world knowledge by:

  • Sharing insights (blogging) on web sites and contributing to online discussions (e.g. Citrix forums, Citrix Developer Network)
  • Sharing ideas and code with the Citrix developer community
  • Creating or fostering communities of Citrix users (e.g. CUGC)
  • Publishing technical documentation, articles, or books
  • Offering technical expertise in the field
  • Speaking at IT conferences


CTP members engage with Citrix product teams via in-person meetings and private webinars for detailed discussions on current technologies and product roadmaps. They help shape the future of Citrix technologies by providing valuable input on business needs and feedback on product strategy.

We are proud to welcome the CTP Class of 2016 to the group:


By their continued contributions to the Citrix community, each of the above has proven that they are deserving of the CTP designation. 

Congratulations to the new awardees, as well as to all the CTP members.
You can see the full list of CTP awardees here: CTP Awardees.

CTP accomplishments are reviewed annually, and are evaluated based on recent community contributions. If you have what it takes, I encourage you to apply for the program. You can review benefits and eligibility here: 
Citrix Technology Professionals Program.


Thanks

I would like to thank the following, whose invaluable support has enabled me to continue giving back to the community: 
  • My family
  • The management team and my colleagues at IPM
  • Current and previous CTP members
  • Perrine Crampton and Brad Nunn, CTP Program Managers
  • Citrix