This chapter describes the following security features:
For information on transport layer security, see "Encryption Filters".
Security Framework in Coherence is based on a concept of Clustered Access Controller, which can be turned on (activated) by a configurable parameter or command line attribute.
The Access Controller manages access to the "clustered resources", such as clustered services and caches and controls operations that include (but not limited to) the following:
creating a new clustered cache or service;
joining an existing clustered cache or service;
destroying an existing clustered cache.
The Access Controller serves three purposes:
grant or deny access to a protected clustered resource based on the caller's permissions
encrypt outgoing communications based on the caller's private credentials
decrypt incoming communications based on the caller's public credentials
Coherence uses a local LoginModule (see JAAS Reference Guide for details) to authenticate the caller and an Access Controller on one or more cluster nodes to verify the caller's access rights.
The Access Controller is a pluggable component that could be declared in the Coherence deployment descriptior, tangosol-coherence.xml
. The specified class should implement the com.tangosol.net.security.AccessController interface.
Coherence provides a default Access Controller implementation that is based on the Key Management infrastructure that is shipped as a standard part of Sun's JDK.
Each clustered service in Coherence maintains a concept of a "senior" service member (cluster node), which serves as a controlling agent for a particular service. While the senior member does not have to consult anyone when accessing a clustered resource, any junior node willing to join that service has to request and receive a confirmation from the senior member, which in turn notifies all other cluster nodes about the joining node.
Since Coherence is a system providing distributed data management and computing, the security subsystem is designed to operate in a partially hostile environment. We assume that when there is data shared between two cluster nodes either node could be a malicious one - lacking sufficient credentials to join a clustered service or obtain access to a clustered resource.
Let's call a cluster node that may try to gain unauthorized access to clustered resources by using nonstandard means as a "malicious" node. The means of such an access could vary. They could range from attempts to get protected or private class data using reflection, replacing classes in the distribution (coherence.jar or other application binaries), modifying classes on-the-fly using custom ClassLoader(s) and so on. Alternatively, a cluster node that never attempts to gain unauthorized access to clustered resources by using nonstandard means will be called a "trusted" node. It's important to note that even a trusted node may attempt to gain access to resources without having sufficient rights, but it does so in a standard way by using the exposed standard API.
File system mechanisms (the same that is used to protect the integrity of the Java runtime libraries) and standard Java security policy could be used to resolve an issue of guarantying the trustworthiness of a given single node. In a case of inter-node communications there are two dangers that we have to consider:
A malicious node surpasses the local access check and attempts to join a clustered service or gain access to a clustered resource controlled by a trusted node;
A malicious node creates a clustered service or clustered resource becoming its controller.
To prevent either of these two scenarios from occurring Coherence uses two-ways encryption algorithm: all client requests must be accompanied by the proof of identity and all service responses must be accompanied by the proof of trustworthiness.
In a case of an active Access Controller the client code could use the following construct to authenticate the caller and perform necessary actions:
import com.tangosol.net.security.Security; import java.security.PrivilegedAction; import javax.security.auth.Subject; ... Subject subject = Security.login(sName, acPassword); PrivilegedAction action = new PrivilegedAction() { public Object run() { // all processing here is taking place with access // rights assigned to the corresponding Subject ... } }; Security.runAs(subject, action);
During the "login" call Coherence uses JAAS that runs on the caller's node to authenticate the caller. In a case of successful authentication, it uses the local Access Controller to:
Determine whether the local caller has sufficient rights to access the protected clustered resource (local access check);
Encrypt the outgoing communications regarding the access to the resource with the caller's private credentials retrieved during the authentication phase;
Decrypt the result of the remote check using the requester's public credentials;
In the case that access is granted verify whether the responder had sufficient rights to do so.
Step 2 (above) serves a role of the proof of identity for the responder preventing a malicious node pretending to pass the local access check phase.
There are two alternative ways to provide the client authentication information. First, a reference to a CallbackHandler could be passed instead of the user name and password. Second, a previously authenticated Subject could be used, which could become handy when Coherence is used by a Java EE application that could retrieve an authenticated Subject from the application container.
If a caller's request comes without any authentication context, Coherence will instantiate and call a CallbackHandler implementation declared in the Coherence operational descriptor to retrieve the appropriate credentials. However that "lazy" approach is much less efficient, since without externally defined call scope, every access to a protected clustered resource will force repetitive authentication calls.
Every clustered resource in Coherence is created by an explicit API call. A senior service member retains the private credentials that are presented during that call as a proof of trustworthiness. When the senior service member receives an access request to a protected clustered resource, it use the local Access Controller to:
Decrypt the incoming communication using the remote caller's public credentials;
Determine whether the remote caller has sufficient rights to access the protected clustered resource (remote access check);
Encrypt the response of access check using the private credentials of the service.
Since the requester will accept the response as valid only after decrypting it, step 3) in this cycle serves a role of the proof of trustworthiness for the requester preventing a malicious node pretending to be a valid service senior.
Coherence ships with an Access Controller implementation that uses a standard Java KeyStore. The implementation class is com.tangosol.net.security.DefaultController and the corresponding part of the Coherence operational descriptor used to configure the default implementation is:
<security-config> <enabled system-property="tangosol.coherence.security">true</enabled> <login-module-name>Coherence</login-module-name> <access-controller> <class-name>com.tangosol.net.security.DefaultContoller</class-name> <init-params> <init-param id="1"> <param-type>java.io.File</param-type> <param-value>./keystore.jks</param-value> </init-param> <init-param id="2"> <param-type>java.io.File</param-type> <param-value>./permissions.xml</param-value> </init-param> </init-params> </access-controller> <callback-handler> <class-name/> </callback-handler> </security-config>
The login-module-name element serves as the application name in a login configuration file (see JAAS Reference Guide1 for complete details). Coherence is shipped with a Java keystore (JKS) based login module that is contained in the coherence-login.jar, which depends only on standard Java runtime classes and could be placed in the JRE's lib/ext (standard extension) directory. The corresponding login module declaration would look like:
// LoginModule Configuration for Oracle Coherence(TM) Coherence { com.tangosol.security.KeystoreLogin required keyStorePath="${user.dir}${/}keystore.jks"; };
The access-controller element defines the AccessController implementation that takes two parameters to instantiate.
The first parameter is a path to the same keystore that will be used by both controller and login module.
The second parameter is a path to the access permission file (see discussion below).
The callback-handler is an optional element that defines a custom implementation of the javax.security.auth.callback.CallbackHandler interface that would be instantiated and used by Coherence to authenticate the client when all other means are exhausted.
Two more steps have to be performed, To make the default Access Controller implementation usable in your application, you must perform two additional steps:
Create a keystore with necessary principals.
Create the permissions
file that would declare the access right for the corresponding principals.
Consider the following example that creates three principals: admin
to be used by the Java Security framework; manager
and worker
to be used by Coherence:
keytool -genkey -v -keystore ./keystore.jks -storepass password -alias admin -keypass password -dname CN=Administrator,O=MyCompany,L=MyCity,ST=MyState keytool -genkey -v -keystore ./keystore.jks -storepass password -alias manager -keypass password -dname CN=Manager,OU=MyUnit keytool -genkey -v -keystore ./keystore.jks -storepass password -alias worker -keypass password -dname CN=Worker,OU=MyUnit
Consider the following example that assigns all rights to the Manager
principal, only join
rights to the Worker
principal for caches that have names prefixed by common
and all rights to the Worker
principal for the invocation service named invocation
:
<?xml version='1.0'?> <permissions> <grant> <principal> <class>javax.security.auth.x500.X500Principal</class> <name>CN=Manager,OU=MyUnit</name> </principal> <permission> <target>*</target> <action>all</action> </permission> </grant> <grant> <principal> <class>javax.security.auth.x500.X500Principal</class> <name>CN=Worker,OU=MyUnit</name> </principal> <permission> <target>cache=common*</target> <action>join</action> </permission> <permission> <target>service=invocation</target> <action>all</action> </permission> </grant> </permissions>
The policy file format is fully described in Java SE Security Guide. Example:
grant codeBase "file:${coherence.home}/lib/coherence.jar" { permission java.security.AllPermission; };
The minimum set of privileges required for Coherence to function are specified in the security.policy file which is included as part of the Coherence installation. This file can be found in coherence/lib/security/security.policy.
The binaries could be signed using the JDK jarsigner tool, for example:
jarsigner -keystore ./keystore.jks -storepass password coherence.jar admin
and then additionally protected in the policy file:
grant SignedBy "admin" codeBase "file:${coherence.home}/lib/coherence.jar" { permission java.security.AllPermission; };
All relevant files such as policy format, coherence binaries, and permissions should be protected by operating system mechanisms to prevent malicious modifications.