Securing an Apache Ignite cluster

Steve Neal Development, Java Programming 4 Comments

I’ve been building a data grid at an investment bank using Apache Ignite and have had a tough time securing it. The documentation for Ignite is really very good but doesn’t touch on how to secure a cluster at all.

In this blog I’ll give an overview of how to secure a cluster in the hope that it will prove useful to anyone facing a similar task. I can’t give complete sources for a solution of course, but I will do my best to illustrate what needs to be done and where you can look in the existing Ignite code for some clues.

Ignite is a new open source project managed by Apache. It is a mature code base and is derived from GridGain’s data grid product and so far I’m very impressed with it. The initial version of this software (currently 1.0.0-RC1) ships with no security so you have to provide your own. Another option is to purchase a license for GridGain 7.0 (due to be released in a couple of weeks time) which will offer a security component on top of Ignite.

My security requirements are fairly simple, and I just want to be able to have a white list of I.P. addresses that are allowed to join the cluster. There are hooks for full authentication but I’ll not be discussing them in this blog.

Plugins

Ignite has a nice modular architecture that allows different modules to be configured. The default security module, which simply grants all permissions to everyone, is implemented in the GridOsSecurityProcessor class. More on how this works later, the aim of this section is to outline how you go about replacing it with one of your own.

The first step is to inject a plugin to your IgniteConfiguration. I’m using Spring to bootstrap Ignite, so I need to add this to my Spring context:

<bean id="ignite" class="org.apache.ignite.configuration.IgniteConfiguration"
      p:gridName="mygrid">

    <property name="pluginConfigurations">
        <bean class="uk.co.smartkey.ignite.WhiteListPluginConfiguration"/>
    </property>
</bean>

The implementation of this configuration class is trivial, it just needs to create a plugin-provider:

public class WhiteListPluginConfiguration implements PluginConfiguration {
    @Override
    public Class<? extends PluginProvider> providerClass() {
        return WhiteListPluginProvider.class;
    }
}

The plugin-provider will be interrogated by the IgniteKernal at startup and asked to create plugins which support different interfaces. We’re interested in the security plugins, so we’re looking out for the request to create a GridSecurityProcessor implementation:

public class WhiteListPluginProvider 
                  implements PluginProvider<WhiteListPluginConfiguration> {

    @Override
    public String name() {
        return "WhiteListSecurity";
    }

    @Override
    public String version() {
        return "1.0.0";
    }

    @Nullable
    @Override
    public Object createComponent(PluginContext ctx, Class cls) {
        if (cls.isAssignableFrom(GridSecurityProcessor.class)) {
            return new WhiteListSecurityProcessor();
        } else {
            return null;
        }
    }

    @Override
    public IgnitePlugin plugin() {
        return new WhiteListAuthenticator();
    }

    //all other methods are no-op
}

See how we create our plugin class in the createComponent method? We must also return something form the plugin method, but from what I could tell this doesn’t seem to be used by Ignite (yet?).

All other methods on this class, and there are a lot of them, should just be no-op implementations.

WhiteListSecurityProcessor

So, we’ve configured Ignite to create and install our security component. All that’s left to do now is implement a few methods that will provide the authentication and authorisation for our cluster. In my case, I’m only really focusing on authentication, as once recognized all privileges will be granted.

Here are the important bits of code:

public class WhiteListSecurityProcessor
                          implements DiscoverySpiNodeAuthenticator, 
                                     GridSecurityProcessor, 
                                     IgnitePlugin {
				
    //the hosts that will be allowed to join the cluster
    private Set<String> whitelist = new HashSet<>();

    private boolean isAddressOk(Collection<String> addresses) {
        //return true if the address is in the whitelist
    }

    @Override
    public GridSecurityContext authenticateNode(ClusterNode node, 
                                                GridSecurityCredentials cred) 
                                                throws IgniteException {

        return new GridSecurityContext(new GridSecuritySubject() {
            
            @Override
            public GridSecurityPermissionSet permissions() {
                if (isAddressOk(node.addresses())) {
                    return WhiteListPermissionSets.ALLOW_ALL;
                } else {
                    return WhiteListPermissionSets.ALLOW_NONE;
                }
            }

            //all other methods are noop

        });
    }

    @Override
    public boolean isGlobalNodeAuthentication() {
        //allow any node to perform the authentication
        return true;
    }

    @Override
    public void start() throws IgniteCheckedException {
        //load the whitelist 
        //check that this process is running on a white listed server
        //if there's a problem throw new IgniteCheckedException
    }

    @Nullable
    @Override
    public IgniteSpiNodeValidationResult validateNode(ClusterNode node) {
        if (!isAddressOk(node.addresses())) {
            return new IgniteSpiNodeValidationResult(node.id(), 
                                                     "Access denied", 
                                                     "Access denied");
        } else {
            return null;
        }
    }

    //all other methods are noop

}

I’ve only included pseudo code for the mechanics of what this class does, but you should be able to get a good idea from this of what you might need to do for your own requirements.

The start method is called when Ignite bootstraps, so it’s a good place for me to load my white list of I.P. addresses. I also check here that this process is running on a white listed server. If there are any problems throwing an IgniteCheckedException will cause the process to shut down with an error message.

When a new cluster member starts and attempts to connect, the authenticateNode and validateNode methods will be called (in that order). The call to authenticateNode needs to return a security context which identifies the permissions granted to the process. To be safe, I return an ALLOW_NONE policy if the I.P. address is not on the white list. The call to validateNode is then made; here I get the I.P. addresses for the connecting node and identify whether it can join the cluster. Contrary to the JavaDocs, this method should return a response only if there’s a problem.

For examples of how to create policy lists, take a look at Ignite’s GridOsSecurityProcessor class.

Again, there are a lot of no-op methods that needed implementing. From what I could tell these were of no relevance to what I was trying to do.

Comments

The security plugin I’ve outlined above seems to work very well. If the cluster node that has been processing authentication crashes, a new one is elected and resumes service.

I’m impressed with Ignite. Considering it is only a release candidate at the moment, it seems to do everything I expect it to do without too much pain.

Comments 4

  1. ash

    I’m trying to follow the example above but I’m not clear on a few steps.
    You mention you are using Ignite above but I can not find the class GridSecurityContext . I only find the class SecurityContext.

    Also when I put in the plug-in configuration in the ..cache.xml config file for GridGain to start, It does not pick up the plugin at all.
    All I get is:
    Configured plugins:
    [14:45:39] ^– None

    I have the line as above in the xml :

    ….

    Does the plugin config only work in the Enterprise Edition or it works in the opensource Ignite version too ?

    Thanks.

    1. Post
      Author
  2. Giovanni

    What about WhiteListPermissionSets.ALLOW_ALL ?
    Do I need a class which needs to implement SecurityPermissionSet ?
    I’m using Open Source Ignite 1.5.0-
    Thanks.

  3. Nick G

    Hi Steve,

    Thanks for your blog – very useful!

    Just wondering why you didn’t try securing your environment using SSL/TLS? (Is SSL a recent addition, and just wasn’t available at the time? – https://apacheignite.readme.io/docs/ssltls)

    AFAIU, it keeps unwanted nodes off the “network” and secures all network traffic – probably what you’d want for an investment bank, right?

    Kind regards,
    Nick

Leave a Reply to Nick G Cancel reply

Your email address will not be published. Required fields are marked *