Role-based Authorization with Active Directory for GridGain

Valentin Kulichenko
7 min readAug 2, 2019

--

Photo by rawpixel.com from Pexels

GridGain Enterprise Edition is built on top of Apache Ignite to add capabilities required for mission-critical production deployments. One such feature is Enterprise-Grade Security which you can use to protect data in your clusters from unauthorized access.

What makes it even better is that it comes with out-of-the-box JAAS integration, so you can easily integrate with LDAP, Kerberos, and other external systems for authentication and authorization.

Unfortunately, the devil is in the detail. The actual configuration, both on JAAS and GridGain side, would differ drastically depending on your security requirements.

Recently I had an opportunity to work on such a setup. GridGain cluster that required enhanced security was running within an Active Directory domain. The requirements seemed simple:

  1. Every node or client application has to authenticate itself with the domain providing correct credentials of one of the user accounts.
  2. Access to the cluster has to be restricted based on groups, i.e. a user can be granted a certain set of permissions if and only if it is a member of a specific group.

The solution was not as obvious as I expected though, as I had to play with different configurations for quite a while to make it work. Below is a detailed description of the solution.

Active Directory Settings

First and foremost, we need to define requirements and create Active Directory groups and users that will be used for access control.

Let’s say we want to have four different groups of users for GridGain:

  • Read-only users. Can read from any cache in the cluster, but can’t do updates.
  • Read-write users. Can both read and update data in caches.
  • Web Console users. Don’t have direct access to the cluster, but can use Web Console (monitoring and management tool provided by GridGain).
  • Superusers. Have full access to all cluster functions.
List of GridGain groups created in Active Directory

Once groups are created, we can go ahead and assign users to them. For example, here is how I created a new user with read-only access.

And here is how entry for this user looks like in an LDAP viewer. Note the memberOf attribute which lists all the groups the user is part of — we’re going to use this going forward.

Cluster Configuration

On the cluster side, we will need to separately configure JAAS and GridGain. JAAS part will define how we interact with Active Directory. GridGain part will provide actual permissions for different groups, and tie everything together.

Note that from now on I assume that you know the basics about JAAS, and deliberately omit some technical details that are not directly related to integration with GridGain and the use case described here. In case you want to make yourself more familiar with JAAS and its LdapLoginModile which we’re going to use, here are a couple of links:

Configuring JAAS

This is the trickiest part. If you take a look at GridGain’s documentation, you will notice that it’s assumed that every user has a special attribute in LDAP to store GridGain permissions in a form of JSON string (name of the attribute is provided to JAAS via authzIdentity parameter). While this might be a straightforward convenient solution for many deployments, this is not going to work with the typical Active Directory structure shown above. In our case, we want to assign permissions based on groups and roles, rather than store them directly in LDAP.

Luckily, JaasAuthenticator (which implements GridGain-JAAS integration) allows specifying a custom mapping between principals and permissions — this can be done via JaasPermissionsProvider. So it might look like we can configure JAAS with authzIdentity=”{memberOf}” parameter, and then use permissions provider to convert the list of groups fetched from the memberOf attribute to corresponding permissions. Unfortunately, that’s not the case. The issue here is that the principal created based on authzIdentity can only be a single string, while memberOf is a collection of strings. As a matter of fact, with such configuration, permissions provider will get the first group name in the list, effectively ignoring all others.

What JAAS allows us to do though, is specify userFilter parameter to search for user entry that is included in a particular group. Filter condition like memberOf=GROUP_NAME will basically mimic ‘contains’ operation and return true if one of the items in memberOf collection equals to the provided name.

Now, if we apply the above to every group we have and try them one by one, we will achieve what we are looking for. In JAAS terms, this means that we need to create multiple login modules, one for each group. Here is the full configuration:

Let’s break it down. Here we have four LDAP login modules, every module marked as ‘sufficient’, which means that if at least one module is successful, the user will be authenticated. Two common parameters are self-explanatory — userProvider and authIdentity. Parameters userFilter and authzIdentity are unique for every module and are used to identify which group the user is part of (if any).

For every module JAAS will go through the following process:

  1. Connect to the LDAP server and authenticate with provided username and password.
  2. Use userFilter search string to look for user entry based on the username and the group name current module is responsible for.
  3. If such entry exists (i.e. the user is included in the group), create a principal with name provided in the authzIdentity parameter, and stop the whole process with success.
  4. If entry can’t be found, move on to the next module (or fail if there are no modules left).

As a result, a successfully authenticated user will be created with a principal holding the group name — this name will then be passed to GridGain’s permissions provider.

Configuring GridGain

GridGain configuration is much more straightforward. All we need is to enable JaasAuthenticator and use permissions provider to specify the mapping between possible group names and corresponding permissions. For the latter, we will use JaasBasicPermissionsProvider which is provided by GridGain out of the box for convenience.

For the four groups created earlier, the XML will look like this:

Here, we simply provide permission set for every group. For example, users included in GG_CacheReadOnlyUsers will have access to all caches, but will only be able to read the data. For the full list of possible permissions, refer to this page: https://docs.gridgain.com/docs/authorization-and-permissions

Bringing Everything Together

To fully demonstrate how this integration works, I created an example which you can find here: https://github.com/vkulichenko/gridgain-jaas-ad

In the repository, you will find full configuration files for GridGain server nodes (server.xml) and client nodes (client.xml), as well as the JAAS configuration file that is also shown above (jaas.config).

The server application (Server.java) uses a special user account called GG_ServerAccount which is a member of the GG_SuperUsers group. This means that it has full access to all APIs and cluster functions. This is a general practice — although server nodes have to authenticate in the same way as clients, there is typically no need to put restrictions on them.

On the other side, the client application (Client.java) authenticates with the read-only account which I created in the very beginning. Therefore, this application will only be able to read the data, but not update it. Indeed, when trying to execute a cache put operation, it fails with an authorization error:

org.apache.ignite.plugin.security.SecurityException: Authorization failed [perm=CACHE_PUT, name=TestCache, subject=SecuritySubjectAdapter [id=60c0c45e-69a1-498f-aa9d-5aeb1c78125d, subjType=REMOTE_NODE, addr=/0:0:0:0:0:0:0:1:0, permissions=SecurityBasicPermissionSet [cachePermissions=LinkedHashMap {*=ArrayList [CACHE_READ]}, taskPermissions=HashMap {}, servicePermissions=HashMap {}, systemPermissions=null, dfltAllowAll=false], login=vkulichenko]]

In addition, the server is configured to print out a list of currently authenticated users every time a new node joins topology. This way we can confirm that everything is configured correctly. Here is the output after both server and client nodes are up:

Authenticated subjects:
GG_ServerAccount: SecurityBasicPermissionSet [cachePermissions=HashMap {}, taskPermissions=HashMap {}, servicePermissions=HashMap {}, systemPermissions=null, dfltAllowAll=true]
vkulichenko: SecurityBasicPermissionSet [cachePermissions=LinkedHashMap {*=ArrayList [CACHE_READ]}, taskPermissions=HashMap {}, servicePermissions=HashMap {}, systemPermissions=null, dfltAllowAll=false]

Looks good. We have a user which runs under the server account and has all possible permissions — that’s the server node. The second user is the client which is expectedly granted with CACHE_READ permission only.

If you’re using GridGain clusters within Active Directory domains, there is a big chance you will face security requirements similar to what is described above. In such a case, feel free to take the provided configuration as a base and adjust it to your specific needs. Hopefully, this will save some time and effort for you.

--

--