How to Secure Apache Ignite From Scratch
Currently, Apache Ignite doesn't provide a security implementation out-of-the-box. So, I'm going to show you how to create an Apache Ignite security plugin from scratch.
Join the DZone community and get the full member experience.Join For Free
To use Apache Ignite securely, you need an implementation of GridSecurityProcessor, a security plugin. Currently, Apache Ignite doesn't provide this implementation out-of-the-box. So, I'm going to show you how to create an Apache Ignite security plugin from scratch.
To create and test our plugin, we will use Apache Ignite 2.9.1, which, at this time, is the most recent version of Ignite.
The plug-in that we're going to create will be able to do the following:
- Authenticate an Apache Ignite node that is joining to a cluster.
- Authorize Ignite operations.
- Authenticate a thin client.
- Run a user-defined code with restrictions.
The code that is used in this article is available on GitHub.
Implementation of Apache Ignite Security Interfaces
Let's start with the
pom.xml file. All we need do is define the following dependencies:
Now, we are ready to create implementations of the security interfaces.
The first implementation is SecuritySubject.
The SecurityContext interface can be implemented in the following way:
There are a few tricky points. The SecurityContext interface has to extend the Serializable interface; so, don't forget to add
Serializable to the list of implementing interfaces.
Permissions are divided into four groups:
- system operations:
- service operations:
- cache operations:
- task operations:
To determine whether a security subject has been given permission, you have to know what the permission group is. The
operationAllowed() method shows how you can identify the permission group.
CACHE_DESTROY are included in two groups (system operations and cache operations). When
CACHE_DESTROY) is treated as a system permission, it applies to all caches. In other cases, when
CACHE_DESTROY) is treated as cache permission, permission checking is executed with the account of the cache name.
In the future, the SecurityPermission enum can be extended by new constants. So, when you get unknown permissions, you need to decide on an appropriate reaction.
The GridSecurityProcessor is the central interface for Ignite security. We will improve our implementation of
GridSecurityProcessor step-by-step by adding the capabilities that are described in the introduction to this article.
You can use the GridProcessorAdapter to create your processor. This abstract class enables you to override only the methods that you need to override and to hide boilerplate code. In our case, we want to override the
start() method and, thus, add the credentials to the node's attributes. One or more cluster nodes will use the credentials to authenticate the node that is joined to the cluster in the
authenticateNode() method. Before you create a
SecuritySubject instance, make sure that the credentials are valid.
The code that we wrote for
GridSecurityProcessor enables nodes to join a cluster.
Let's try it!
Start Ignite Nodes With a Security Plugin
To start Ignite nodes with our
GridSecurityProcessor, we must define the processor as an Ignite plugin. You can read about Ignite plugins in the Plugins document, but when you create an Ignite processor plugin, you need to be aware of a few idiosyncrasies.
When Ignite's node-starting routine requires a security processor, the
createComponent() method returns an instance of
GridSecurityProcessor. We don't need an
IgnitePlugin object, but we cannot return
null, so we created this instance of
IgnitePlugin that does nothing.
Now, we are ready to use our plugin to start nodes. Using
ignite.bat and Spring configuration, I started a node:
And, the second node was started from the following Java code:
Let's look at the console output on the joining node:
We see that the security plugin is in place and started, and authentication is on.
The first node that started is the coordinator. In our case, the coordinator processes authentication of the node that is joining. The coordinator displays the following text:
For all nodes in the cluster, the same security plugin must be configured and security must be enabled. Otherwise, when the node starts, you receive an error.
Authorize Ignite Operations
To authorize Ignite operations, we need to implement the
authorize() method, which can look like the following:
authorize() method throws a SecurityException if the requested access, defined by the name and the permission, is not permitted based on passed
Let's try to authorize cache operations.
Create two caches by adding a definition of cache configurations to the Ignite configuration file:
Add to the implementation of
GridSecurityProcessor, the method that emulates a security poliсy:
The subject with the
secondSubject login can write to and read from only the cache that is named
common_cache. The process of node authentication uses the following method to get the subject's permissions:
Now, we are ready to start the
The output looks like the following:
In regard to the
put operation, the output indicates that the subject that has the
secondSubject login doesn't have permission to manipulate the cache that is named
secret_cache. The lack of permission is due to the fact that we didn't mention the name
secret_cache in our security policy. However, the subject can perform
read operations, but not remove operations, on the cache that is named
common_cache. This behavior adheres to the permissions that we defined in the security policy method.
Thin Client Authentication
A thin client is a lightweight Ignite client that establishes a socket connection to a standard Ignite node. The node performs authentication and creates a security context associated with the thin client. The
GridSecurityProcessor provides the interface to get a thin client's security context on every node in the cluster. But how to implement this feature is the developers' decision. I'm going to use Ignite's cache to make a thin client's security context accessible throughout the cluster.
Define the following methods in the
We use the
thin_clients cache to implement transmission of a client's security context between nodes.
Now, we can start a thin client:
Now, we remember that the
secondSubject subject has
read permissions for the
common_cache cache but that the subject cannot remove anything from the cache. Therefore, the output looks like the following:
Run User-Defined Code With Restrictions
User-defined code contains custom logic via various APIs, including compute tasks, event filters, and message listeners. In some cases, you might want to restrict the code's capabilities on the nodes that execute the code. For this purpose, you can use the Ignite Sandbox feature.
Let's consider changes that have been made in the security interface implementations that allow code to be run inside Sandbox.
sandboxEnable() method enables you to switch off Ignite Sandbox when Java's
SecurityManager is in place. By default, Ignite Sandbox is off, so we will override this method. Where a node passes authentication, we use the
getSandboxPermissions() method to emulate a security policy inside Sandbox. A security subject with the
sandboxSubject login has only read access to the system property that is named
The implementation of
SecuritySubject contains the field and the methods that are required to pass permissions to Sandbox.
Ignite needs enough permissions to work correctly. I'm going to use the most straightforward way to grant all permissions to Ignite. I will use the
To start an Ignite node with
SecurityManager and a specific security policy, we can modify the
ignite.bat file by adding the following text into the
ADD YOUR/CHANGE ADDITIONAL OPTIONS HERE section:
Now, we are ready to do our final test. When we start an Ignite node, we see the following output:
Notice that Sandbox is on.
The test example looks like the following:
For the first calling, we get the Java version that is used on the remote node. But, for the reading
java.home property, we get the following output:
Peer class loading should be enabled on every node.
It is hard to find all the information that you need about how to implement an Ignite security plugin in one place, so I hope that this tutorial will be useful to you. If you find the tutorial useful, please like this blog post.
If you have a question or comment, please enter it below.
Opinions expressed by DZone contributors are their own.