ADFS Claims Rules Sample # 2 – Enforce Multifactor Authentication

The ADFS GUI allows for some rudimentary control of multifactor authentication (MFA).  This control centers around Device Trusted/Untrusted, Network Inside/Outside, and User Group Members.  But what if you wanted an exception group, or possibly only on the passive endpoint, not the active endpoint, of if you wanted to lock down MFA to only specific apps?  You can not do that through the GUI.  You need Powershell.

Multifactor authentication rules are set on the AdditionalAuthenticationRules parameter when running the command-let Get-AdfsRelyingPartyTrust.  In my previous post, I outlined how to control Office 365 via policies.  The same goes for here, where you can use any combination of claims rules to control the behavior of MFA.

Please note you do need the MFA provider configured on ADFS.  ADFS claims rules can not differentiate between providers if more than one is selected.

Step 1:  You need to focus these claims rules on a particular trust.  You can do it global as well, but for this example we are going to focus again on Office 365.  Run the command-let:

 Get-AdfsRelyingPartyTrust - Name "Microsoft Office 365 Identity Platform"

You will see that parameter –AdditionalAuthenticationRules  is empty.

Step 2:  Determine the claims you want to invoke MFA.  In this sample, I want to target the passive endpoints for all external users, that do not belong to a specific Active Directory group.  Remember that the AD group is via SID, not name.

exists([Type == ""]) && exists([Type == "", Value =~ "(/adfs/ls)|(/adfs/oauth2)"]) && Not exists([Type == "", Value =~ "S-1-5-21-0000000000-000000000-0000000000-000000"]) => issue(Type = "", Value = "");

Step 3:  Pulling it all together.  We want to insert this claim into  –AdditionalAuthenticationRules for Office 365.  To do this, run the following powershell:

$rp = Get-AdfsRelyingPartyTrust –Name "Microsoft Office 365 Identity Platform"
Set-AdfsRelyingPartyTrust –TargetRelyingParty $rp –AdditionalAuthenticationRules ‘exists([Type == ""]) && exists([Type == "", Value =~ "(/adfs/ls)|(/adfs/oauth2)"]) && Not exists([Type == "", Value =~ "S-1-5-21-1338325200-504760778-2079600828-465239"]) => issue(Type = "", Value = "");’

And there you go.  Now if you ever need to remove the rule, just blank it out:

$rp = Get-AdfsRelyingPartyTrust –Name "Microsoft Office 365 Identity Platform"
Set-AdfsRelyingPartyTrust –TargetRelyingParty $rp –AdditionalAuthenticationRules ‘’

That will remove the MFA authentication.

ADFS Claims Rules Sample # 1 – Office 365

Microsoft has a nice tutorial on understanding and implementing claims rules for the Office 365 platform, however if you set the default rule to deny all (by removing the Permit All claims rule), there are a few additional rules that need to be configured based on what you are trying to do.  Please note that as conditional access policies mature, some of these rules can instead be accomplished in Azure AD.

Sample Rules for Office 365.

Permit OWA and other Passive Claim Access

This rule allows all passive claims (anything accessing the /adfs/ls URL) to ADFS.

exists([Type == "", Value =~ "(/adfs/ls)|(/adfs/oauth2)"])
=> issue(Type = "", Value = "true");


Permit Active Sync

Users accessing the active endpoint of ADFS with the client names of Auto Discover or ActiveSync are allowed to authenticate.

exists([Type == "", Value == "/adfs/services/trust/2005/usernamemixed"])
&& exists([Type == "", Value =~ "Microsoft.Exchange.ActiveSync|Microsoft.Exchange.Autodiscover"])
=> issue(Type = "", Value = "true");


Permit Outlook (No Modern Authentication).

This rule is for legacy access of the Outlook client.  It connects to the active endpoint of ADFS, and access can be restricted based on client IP range (public address).  In this example, there is a reference to a single external IP address, as well as a reference to a /24 Class C address range.

exists([Type == "", Value == "/adfs/services/trust/2005/usernamemixed"])
&& NOT exists([Type == "", Value =~ "Microsoft.Exchange.ActiveSync|Microsoft.Exchange.Autodiscover"])
&& exists([Type == "", Value =~ "\b192\.168\.1\.1\b||\b192\.168\.10\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\b"])
=> issue(Type = "", Value = "true");


Additional Rules that you may require.

By limiting access to the endpoints of ADFS, you will inadvertently break access to other Office 365 applications.  Here are three that I have come across in my testing.

Allow the Office Suite, as well as SharePoint Designer

This rule will allow connections from Word, Excel, etc. to connect to Office 365

exists([Type == "", Value =~ "/adfs/services/trust/2005/usernamemixed|/adfs/services/trust/2005/windowstransport"])
=> issue(Type = "", Value = "true");

Allow Azure AD Join

This rule will reinstate the ability for Azure AD join of Windows 10 devices.

exists([Type == "", Value == "/adfs/services/trust/13/usernamemixed"])
&& exists([Type == "", Value =~ "Windows-AzureAD-Authentication-Provider"])
=> issue(Type = "", Value = "true");


Catch all rule for the Active Endpoint

This rule will allow all access to the active endpoint of ADFS.  This will allow PowerShell access to the Office 365 tenant.  In this sample, I am locking down this access based on group membership (SID value of the group).

exists([Type == "", Value =~ "/adfs/services/trust/2005/usernamemixed|/adfs/services/trust/2005/windowstransport"])
&& exists([Type == "", Value =~ "S-1-5-21-1000000000-500000000-2000000000-540000"])
=> issue(Type = "", Value = "true");

Office 365 – HIPAA in 3 easy steps

Like all compliance policies, the Health Insurance Portability and Accountability Act (HIPAA) is not without its twists and turns.  The act itself details out the handling of patient health and identifying data (PHI and PII) (the cliff notes are here), but what does all of this mean for the Office 365 suite?

Office 365 in general contains the tools to meet the technical safeguards for HIPAA compliance.  Unfortunately out of the box it will absolutely not meet the safeguards, but thankfully a few minutes is all it takes to bring the suite up to snuff.

Continue reading Office 365 – HIPAA in 3 easy steps

Citrix Issue RESOLVED: Netscaler Gateway white screens after login/Missing or broken icons on clientless access.

In a recent project I was heavily involved in and lead the design of, the end state was the use of the Netscaler Unified Gateway to provide access capabilities to enterprise resources. Unbeknownst to myself, my team, and my Citrix consulting partners an issue was uncovered that brought the Netscaler platform to its knees.

The Symptoms

There are three primary use cases for the Unified Gateway platform that this design covered.  The first, VPN using the web interface of the Gateway, not through the gateway plugin.  The second, clientless VPN using bookmarks.  Lastly, Citrix Storefront, delivered through the clientless VPN web interface.  The Netscaler itself (version 11.1.57.x for this architecture) is shipped with a stock configuration that is not suitable for peak loads on the platform for example at the start of a work shift.  During these login storms, the Netscaler itself would run out of processes, causing the users to experience white screens, missing clientless VPN bookmarks, and session hanging.  Over a period of minutes the appliance would eventually crash.

The Issue

Netscaler firmware is a version of FreeBSD with an integrated version of Apache web server.  When using a custom theme on your gateway, all user traffic bypasses cache and hits the Apache server directly.  The Apache configuration is pretty stock, with HTTPD.CONF (location /etc/httpd.conf) consisting of the following values:

minspareservers: 1 

maxspareservers: 10 

StartServers: 5 

timeout: 120 

maxrequestserchild: 10000 

keepalivetimeout:  15 seconds 

maxclients: 30

What this means is that at startup, the Netscaler will start 5 processes of Apache, have a max of 30 processes, and keep anywhere between 1 to 10 processes spare and in a waiting state.  It will hold processes waiting for 15 seconds before moving on in the queue, with an overall timeout after 120 seconds between receives and sends.  Each process has a max request queue of 10000.

While observing the behavior of the Netscaler while the clients are having issues, I was seeing the process counts jump to the max (30), causing system instability as the appliance was trying to keep up with user demand.  At 2000 connections, with appliance theoretical limit at 10000 connections, the whole system would go down.

CLI Command for displaying apache processes: ps -aux | grep httpd -wc

The Misconception

One may ask oneself, should I just buy a bigger appliance?  What about upgrading to Netscaler 12?  The answer is no and no.  While a bigger appliance would handle more Gateway connections, the Apache web server and the amount of RAM dedicated to Apache is the same regardless of which model you have.  Netscaler 11 and 12 both use the same Apache configuration as well, so firmware version is does not matter.

The Initial Citrix “Fix”

While working with Citrix support, the recommended course of action was to increase the maxclients to 50, as this would give the Netscaler a higher process ceiling.  The end result of this was causing the appliance itself to run out of memory trying to manage 50 processes at the same time.

The second recommendation was to scale horizontally to accommodate the logins.  Why would I scale horizontally when I have a box with a theoretically limit of 10000 connections, to handle 2000 to 3000 burst logins.

The Real Fix

The actual fix to this issue was a collaborative effort between myself and my teams, and Citrix consulting services.  Each bringing a different piece to the puzzle:

My Contribution: Optimize Apache.

The stock configuration is a subpar configuration that does not mirror reality.  While it works fine in isolated use cases, when all of the bells and whistles are activated it can not keep up with demand.  Here are the values of the new Apache configuration:

minspareservers: 5 

maxspareservers: 10 

StartServers: 5 

timeout: 100 

maxrequestserchild: 1000 

keepalivetimeout:  5 seconds

maxclients: 50

While still starting the appliance with 5 processes, the spares are instead kept between 5 and 10 processes.  This will allow the appliance to focus on the requests, instead of focusing on starting up processes.  By decreasing the max requests to 1000, if there are issues with the process it will not wait for a request queue of 10000 to recycle, and instead recycle much sooner.  The processes themselves are also only waiting 5 seconds between clients before moving on to the next in queue, instead of the stock 15.  I kept the 50 maxclients in good faith for Citrix stock configuration, but due to the optimizations the queue cycles too fast to ever hit that limit.

Citrix Contribution:  Enable Integrated Caching

Citrix consulting recommended to include integrated caching in the mix as well.  Citrix engineering stated that the integrated caching piece will not help with the login storms, as custom themes can not and will not cache by design.  Where it will help is once in, bookmark and resource requests can be cached, taking load off of the Apache web server.

set cache contentGroup loginstaticobjects -memlimit 768 

 set cache contentGroup OcvpnLoginstaticobjects -memlimit 768 

 save config 

 add cache selector en_config_xml_cache_selector   http.req.url.path   http.req.method   http.req.hostname 

 add cache contentGroup en_config_xml_cache_group -relExpiry 120 -maxResSize 16000 -memLimit 28 -hitSelector  en_config_xml_cache_selector 

 add cache policy en_config_xml_cache_pol -rule "HTTP.REQ.URL.PATH_AND_QUERY.STARTSWITH_ANY(\"vpn_cache_dirs\") && (HTTP.REQ.URL.CONTAINS(\"/resources/config.xml\") || HTTP.REQ.URL.CONTAINS(\"/resources/en.xml\"))" -action CACHE -storeInGroup en_config_xml_cache_group 

 bind vpn vserver VSERVER -policy en_config_xml_cache_pol -priority 5 -gotoPriorityExpression END -type REQUEST 

save config


The Aftermath

Success!  Not only did the issue go away, but I still haven’t been able to find the limits to this.  Thread counts hover around 30 at peak logins, when before they were at 50 before crashing the appliance.  I will say that this collaborative effort between myself and my team, and Citrix company in general (one fantastic consultant in particular) has been very successful.  This issue has been made known, and I am hopeful that a knowledge base article will be the next step, with eventual incorporation into the stock configuration.  Until then if you come across this issue, don’t scale out, just fix the problem.

ADFS – Custom User Login experience outside of In-Network/Outside Network

There are a lot of different ways to tailor the login experience of Microsoft ADFS.  Out of the box when you create a trust, the log in experience for the application is if you are “in-network” (in other words not going through the proxy and using the Internet Explorer browser), you will be subject to the Windows Integrated login, where the application will use the credentials of the logged in user.  In the event that fails, the user will be prompted with the ugly Windows Login box.  Yes, we all know the one.  ADFS-WIA

When you are outside the network, you get the Microsoft ADFS forms based authentication page, all with your company logo and customized verbiage.


You have in network users that have corporate issued laptops and desktops that belong to them, and in network users that share a single workstation under a shared department user account.  You want the users with the issued computers to get the integrated sign in experience, but at the same time want the users on the shared computers to identify themselves using the Forms Based Authentication.

Right there it is NOT possible to use the ADFS management global authentication policy options that determine forms-based versus windows-integrated sign on experiences.


What to do?

There are a lot of thoughts on the situation, and this is the one that I implemented in my organization that made the most sense.  Microsoft consulting at the time did not necessarily agree with this approach, and they preferred the global route, or by pointing workstations that need forms based to the external proxy.  I did not agree with that approach, so here is what I did.

  1. Leave the default global settings alone.  We don’t want to break anything that is working.
  2. Think of a name for a custom user agent.  In this example, lets use “CUSTOM”
  3. For Windows domain joined computers, create a Group Policy that sets the following registry key:
      Key path Software\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\User Agent\Post Platform
    2. NAME:  CUSTOM
    3. TYPE: REG_SZ
    4. VALUE: IEAK
  4. You see in the above step that the name matches what our custom user string is.  NOTE:  DO NOT USE the Internet Explorer group policy option to change the user agent string.  You need to use the registry key, as the option will break or cause issues with versions of SharePoint.
  5. Apply the policy to the computers(or users) that you WANT to log in using integrated authentication.  In other words, exclude the shared computers, or the shared login accounts from this policy.
  6. If you do not have domain joined computers, or are using alternate browsers i.e. chrome, you will need to append the custom user agent string to those browsers.  For example, Chrome can be done via command line startup. chrome.exe –user-agent “CUSTOM”
  7. On the primary ADFS server, run the following powershell command: Set-AdfsProperties -WIASupportedUserAgents @(“MSAuthHost/1.0/In-Domain”,”MSIPC”,”Windows Rights Management Client”,”CUSTOM”)
  8. This command is telling ADFS that you want only browsers with the user agent CUSTOM using windows integrated authentication.  All others will use forms based.

Problem solved, without the need to invest in more servers or changing DNS settings. Users with the issued computers will get the integrated sign in experience, and at the same time users on the shared computers will be able to identify themselves using the Forms Based Authentication, and not the ugly Windows Integrated dialog box.

Default Settings:

To restore the default settings for Windows Integrated Authentication in ADFS, run the following command on the primary ADFS server:  Set-AdfsProperties -WIASupportedUserAgents @(“MSAuthHost/1.0/In-Domain”,”MSIE 6.0″,”MSIE 7.0″,”MSIE 8.0″,”MSIE 9.0″,”MSIE 10.0″,”Trident/7.0″,”MSIPC”,”Windows Rights Management Client”)

Getting Started with Microsoft Flow – Building Blocks

Microsoft Flow; the workflow engine that anyone can use?  Triggers, actions, connectors; the shear amount of options can be intimidating at first.  So much so that regardless of the tutorials and templates, there is still thought of “What the heck do I do with this?”  Turns out you can do a lot, and you do not need to be a programmer to get the most out of Flow.

Continue reading Getting Started with Microsoft Flow – Building Blocks

Integrating the NetScaler Gateway with Microsoft Active Directory Federated Services

After combing through documentation from a few sources, I wanted to write down exactly how to properly integrate a Citrix NetScaler Gateway virtual server with any of the Microsoft identity and federation services (specifically AD FS and Azure AD).  Reason being, there is no definitive article on how to do it, and none .  After some blood, sweat, tears, and a few “Malformed SAML Assertion” errors,  I was able to have a working configuration for both AD FS.

For reference, this has been tested on NetScaler 11.x and 12.x, using AD FS 3.0 for Windows Server 2012 R2. This does not go in depth on how to configure a virtual server on a NetScaler device, nor any advanced options in AD FS.

Continue reading Integrating the NetScaler Gateway with Microsoft Active Directory Federated Services