AAA Access Control

The Right Tools

I friend of mine reached out cause the company he works for wanted to limit the access of subset of users to HDX Applications when working from home or public networks, and not being in a company branch office.

These branch offices use NetScaler for HDX access. Most users should be able to open all apps from everywhere. I immediately proposed Smart Access\Smart Control for limiting the apps. However, this has been rejected as apparently it is considered a design change and that's not allowed by the CVAD Architect.

For one reason or another you can't always use the appropriate tools for the task!

They prefered to deny all external access to the specific group of users than to expose the applications.

The Task

I've shown below a diagram of what we need to accomplish:

Image

We have two sets of Users:

  • Restricted User Group
  • Unrestricted User Group

We have branch offices where we know the Public Source IPs.

Image

The Plan

We have a simple plan. Upon successful login we'll do the following:

  • Group extraction to identify the Restricted Users
  • Source IP Check
  • Deny Authorization for the Users if the TCP Connection is not from a branch office Source IP.

I like having Default DENY Policy and authorizing each connection.

Meaning:

  • DENY Authorization Action on AAA Level
  • DENY Authorization Action in Session Policy
  • ALLOW Authorization on group level

That's my approach, it could be simplified if you wish to do so.

The Implementation

  1. Add Default AAA Group upon successful authentication to your existing Authentication Policy. This Group is a NetScaler group, it doesn't need to exist in your AD.
set authentication ldapAction <LDAP_ACTION_NAME> -defaultAuthenticationGroup AAA_CTX_ALL_USERS
  1. Add Default AAA Group for 'All Users'.
add aaa group AAA_CTX_ALL_USERS -weight 100
  1. Add Default Authorization Policy for the Default AAA Group.
add authorization policy AUTHR_CTX_DEFAULT_ALLOW true ALLOW
bind aaa group AAA_CTX_ALL_USERS -policy AUTHR_CTX_DEFAULT_ALLOW -priority 100 -gotoPriorityExpression END
  1. Add AAA Group for 'Deny Homeworking'. This Group is an AD group, you need to place your restricted users in it. Make sure group extraction works.
add aaa group AAA_CTX_DENY_HOME -weight 80
  1. Add Deny Homeworking Authorization Policy for the Deny AAA Group.
add authorization policy AUTHR_CTX_HOME_DENY "CLIENT.IP.SRC.IN_SUBNET(78.92.19.0/27).NOT||CLIENT.IP.SRC.EQ(78.35.14.20).NOT" DENY
bind aaa group AAA_CTX_DENY_HOME -policy AUTHR_CTX_HOME_DENY -priority 100 -gotoPriorityExpression END
  1. Set Default Authorization Action to Deny in your session profile for Receiver and ReciverForWeb.
set vpn sessionaction <VPN_SESSION_ACTION_NAME> -defaultAuthorizationAction DENY

Group Weights & Cascading Policies

You generally need to know a few things about AAA Group weights and their evaluation.

Each individual AAA group is evaluated separately based on weight, hence the policy priority only applies to other policies in that same group. The group with the lower weight takes priority and individual policy priorities are evaluated afterwards.

If a user is a member of multiple AAA Groups their settings will be applied in cascading manner, because you do not have "Go To Expression" concept as with policies. Meaning all settings applied on the previous AAA Group will be retained if not overwritten by the next AAA Group and its set of policies.

To illustrate this, upon cascading through all groups your user could end up with settings from different groups if not overwritten:

Image

Generally be careful when users will be members of more than 1 group. Policy priorities are evaluated ONLY within the group!

Our settings look like this:

Image
Image

Optimizations & tweaks

I performed all the tests on a RDP Gateway portal (that's what I had on hand), in contrast the customer config will be applied to a HDX Gateway.

Dispite my authorization being DENIED, the portal enumerates the applications I have access to. Upon launching an app I'm greeted with the familiar "Not a privileged user" message. (This might not be the case with the HDX Gateway.)

Image

This kept bothering me, so I decided to present a custom "Access Denied" page (I guess just having too much free time).

We just need to evaluate User's Authorization once logged in. If Authorization has failed (DENY) to configure Responder policy with Custom Error Page.

Unfortunately there is no expression evaluating the current Authorization state.

I decided to store the Authorization of the user into a custom variable and to evaluate it later on. This could be handy for logging as well.

  1. Create the "map" variable we're going to populate. We're going to use it like a hash table - KEY - VALUE. In our case USERNAME - AUTHORIZATION STATUS. We want short expiration time (seconds).
add ns variable NS_VAR_AUTHR -type "map(text(15),text(6),5000)" -expires 30
  1. Create the assignments of the hash table. We need two, one assignment for DENY and one for ALLOW.
add ns assignment NS_ASSIGN_ALLOW -variable "$NS_VAR_AUTHR[AAA.USER.NAME]" -set "\"ALLOW\""
add ns assignment NS_ASSIGN_DENY -variable "$NS_VAR_AUTHR[AAA.USER.NAME]" -set "\"DENY\""
  1. Create proper logging for debugging and troubleshooting purposes.
set audit syslogParams -userDefinedAuditlog YES
set audit nslogParams -userDefinedAuditlog YES
add audit messageaction AUDIT_AUTHR ALERT "\"AUDITType=[VAR] src.ip=[\" + client.IP.SRC + \"] USERNAME=[\" + AAA.USER.NAME+ \"] VAR=[\" + $NS_VAR_AUTHR[AAA.USER.NAME] + \"]\" " -logtoNewnslog YES
  1. Create the responder policies for Auditing, ALLOW Assignment, DENY Assignmnet and DENY Access.
add responder policy RES_POL_VAR "$NS_VAR_AUTHR.valueExists(AAA.USER.NAME)" NOOP -logAction AUDIT_AUTH
add responder policy RES_POL_AUTHR_ALLOW "AAA.USER.IS_MEMBER_OF(\"AAA_CTX_ALL_USERS\")" NS_ASSIGN_ALLOW
add responder policy RES_POL_AUTHR_DENY "AAA.USER.IS_MEMBER_OF(\"AAA_CTX_DENY_HOME\")&&(CLIENT.IP.SRC.IN_SUBNET(78.92.19.0/27).NOT||CLIENT.IP.SRC.EQ(78.35.14.20).NOT)" NS_ASSIGN_DENY
add responder policy RES_POL_DENY "$NS_VAR_AUTHR[AAA.USER.NAME].EQ(\"DENY\")" RS_ACT_ACCESS_DENIED

You need to create an Error Page per your liking in RS_ACT_ACCESS_DENIED

  1. Bind the policies to your vServer.
bind vpn vserver <VPN_VSERVER> -policy RES_POL_VAR -priority 90 -gotoPriorityExpression NEXT -type REQUEST
bind vpn vserver <VPN_VSERVER> -policy RES_POL_AUTHR_ALLOW -priority 100 -gotoPriorityExpression NEXT -type REQUEST
bind vpn vserver <VPN_VSERVER> -policy RES_POL_AUTHR_DENY -priority 110 -gotoPriorityExpression NEXT -type REQUEST
bind vpn vserver <VPN_VSERVER> -policy RES_POL_DENY -priority 120 -gotoPriorityExpression END -type REQUEST

First 3 Policies need to have Go to Expression NEXT. They need to be cascaded!

You can check the logging:

tail -f /var/log/ns.log | grep VAR
Image

You need the short var expiration in case the user goes to an office.

Have in mind that the function valueExists() resets value's expiration without new assignment actually heppening. If you want to avoid this use valueCount() function.

Now I have a nice error page when my authorization is failing:

Image

You could accomplish the same during the nFactor with DeadEnd Login Schema, but that's for another afternoon.

Thank you for reading!