Home

WebSphere eXtreme Scale Security

+

Search Tips   |   Advanced Search


Scenario: Securing the data grid in eXtreme Scale

WebSphere eXtreme Scale data grids store information that is sensitive and must be protected.

For a secure deployment, use several layers of protection for optimal security. The first element of protection is the use of firewalls to segment the network. The standard tiered model for web applications is comprised of web clients, a presentation tier of HTTP servers, an application tier comprised of application servers, a data tier, and a storage tier.

eXtreme Scale data grid servers are deployed as part of the data tier. Standard practice is to put the presentation layer servers in a demilitarized zone (DMZ) protected by one firewall, and to put the application, data, and storage tiers in network segments protected by additional firewalls. Do not deploy eXtreme Scale servers in a DMZ. eXtreme Scale servers must be protected as all elements of the data tier are, according to standard industry practice.

However, for optimal protection against security threats, use an in-depth defense mechanism, where a number of additional measures protect eXtreme Scale operation and the data that is stored in the data grid. These additional measures not only help in defending against external threats, but also prevent unauthorized data access by employees and contractors who might have access to network segments in which the eXtreme Scale servers reside.

Use the following end-to-end steps to configure security in WebSphere eXtreme Scale, whether you have stand-alone servers, the Liberty profile, the OSGi framework, or WebSphere Application Server installed in your environment:


Data grid authentication

Use the secure token manager plug-in to enable server-to-server authentication, which requires you to implement the SecureTokenManager interface.

The generateToken(Object) method takes an object protect, and then generates a token that cannot be understood by others. The verifyTokens(byte[]) method does the reverse process: it converts the token back to the original object.

A simple SecureTokenManager implementation uses a simple encoding algorithm, such as a XOR algorithm, to encode the object in serialized form and then use corresponding decoding algorithm to decode the token. This implementation is not secure and is easy to break.

WebSphere eXtreme Scale default implementation

WebSphere eXtreme Scale provides an immediately available implementation for this interface. This default implementation uses a key pair to sign and verify the signature, and uses a secret key to encrypt the content. Every server has a JCKES type keystore to store the key pair, a private key and public key, and a secret key. The keystore has to be the JCKES type to store secret keys. These keys are used to encrypt and sign or verify the secret string on the sending end. Also, the token is associated with an expiration time. On the receiving end, the data is verified, decrypted, and compared to the receiver secret string. SSL communication protocols are not required between a pair of servers for authentication because the private keys and public keys serve the same purpose. However, if server communication is not encrypted, the data can be stolen by looking at the communication. Because the token expires soon, the replay attack threat is minimized. This possibility is significantly decreased if all servers are deployed behind a firewall.

The disadvantage of this approach is that the WebSphere eXtreme Scale administrators have to generate keys and transport them to all servers, which can cause security breach during transportation.


Data grid security

Data grid security ensures that a joining server has the right credentials, so a malicious server cannot join the data grid. Data grid security uses a shared secret string mechanism.

All WebSphere eXtreme Scale servers, including catalog servers, agree on a shared secret string. When a server joins the data grid, it is challenged to present the secret string. If the secret string of the joining server matches the string in the president server or catalog server, the joining server is accepted. If the string does not match, the join request is rejected.

Sending a clear text secret is not secure. The WebSphere eXtreme Scale security infrastructure provides a secure token manager plug-in to allow the server to secure this secret before sending. You must decide how to implement the secure operation. WebSphere eXtreme Scale provides an out-of-the-box implementation, in which the secure operation is implemented to encrypt and sign the secret.

The secret string is set in the server.properties file. See Server properties file for more information about the authenticationSecret property.


SecureTokenManager plug-in

A secure token manager plug-in is represented by the com.ibm.websphere.objectgrid.security.plugins.SecureTokenManager interface.

The generateToken(Object) method takes an object, and then generates a token that cannot be understood by others. The verifyTokens(byte[]) method does the reverse process: the method converts the token back to the original object.

A simple SecureTokenManager implementation uses a simple encoding algorithm, such as an exclusive or (XOR) algorithm, to encode the object in serialized form and then use the corresponding decoding algorithm to decode the token. This implementation is not secure.

WebSphere eXtreme Scale provides an immediately available implementation for this interface.

The default implementation uses a key pair to sign and verify the signature, and uses a secret key to encrypt the content. Every server has a JCKES type keystore to store the key pair, a private key and public key, and a secret key. The keystore has to be the JCKES type to store secret keys.

These keys are used to encrypt and sign or verify the secret string on the sending end. Also, the token is associated with an expiration time. On the receiving end, the data is verified, decrypted, and compared to the receiver secret string. SSL communication protocols are not required between a pair of servers for authentication because the private keys and public keys serve the same purpose. However, if server communication is not encrypted, the data can be stolen by looking at the communication. Because the token expires soon, the replay attack threat is minimized. This possibility is significantly decreased if all servers are deployed behind a firewall.

The disadvantage of this approach is that the WebSphere eXtreme Scale administrators have to generate keys and transport them to all servers, which can cause security breach during transportation.


Sample scripts to create default secure token manager properties

As noted in the previous section, we can create a keystore that contains a key pair to sign and verify the signature and a secret key to encrypt the content.

For example, we can use the JDK 6 keytool command to create the keys as follows:

keytool -genkeypair -alias keypair1 -keystore key1.jck -storetype JCEKS -keyalg rsa -dname "CN=sample.ibm.com, OU=WebSphere eXtreme Scale" -storepass key111 -keypass keypair1 -validity 10000

keytool -genseckey -alias seckey1 -keystore key1.jck -storetype JCEKS -keyalg DES  -storepass key111 -keypass seckey1 -validity 1000
These two commands create a key pair "keypair1" and a secret key "seckey1". We can then configure the following in the server property file:

secureTokenKeyStore=key1.jck secureTokenKeyStorePassword=key111
secureTokenKeyStoreType=JCEKS
secureTokenKeyPairAlias=keypair1
secureTokenKeyPairPassword=keypair1
secureTokenSecretKeyAlias=seckey1
secureTokenSecretKeyPassword=seckey1
secureTokenCipherAlgorithm=DES
secureTokenSignAlgorithm=RSA


Configuration

See Server properties for more information about the properties that you use to configure the secure token manager.


Authenticating and authorizing clients

We can enable security and credential authentication to authenticate clients. In addition, we can authorize administrative clients to access the data grid.


Authenticating application clients

Application client authentication consists of enabling client-server security and credential authentication, and configuring an authenticator and a system credential generator.


Authorizing application clients

Application client authorization consists of ObjectGrid permission classes, authorization mechanisms, a permission checking period, and access by creator only authorization.

For WXS, authorization is based on the Subject object and permissions. The product supports two kinds of authorization mechanisms: Java Authentication and Authorization Service (JAAS) and custom authorization.

There are four different types of permission classes as follows.

  1. Set the permission checking period.

    eXtreme Scale supports caching the map permission checking results for performance reasons. Without this mechanism, when a method that is in the list of methods that for your particular permission class is called, the runtime calls the configured authorization mechanism to authorize access. With this permission checking period set, the authorization mechanism is called periodically based on the permission checking period.

    The permission authorization information is based on the Subject object. When a client tries to access the methods, the eXtreme Scale runtime looks up the cache based on the Subject object. If the object cannot be found in the cache, the runtime checks the permissions granted for this Subject object, and then stores the permissions in a cache.

    The permission checking period must be defined before the ObjectGrid is initialized. The permission checking period can be configured in two ways:

    Use the ObjectGrid XML file to define an ObjectGrid and set the permission check period.

    In the following example, the permission check period is set to 45 seconds:

    <objectGrids>
     <objectGrid name="secureClusterObjectGrid" securityEnabled="true"
     authorizationMechanism="AUTHORIZATION_MECHANISM_JAAS" 
     permissionCheckPeriod="45">
      <bean id="bean id="TransactionCallback"
    className="com.ibm.websphere.samples.objectgrid.HeapTransactionCallback" />
    ...
    </objectGrids>

    To create an ObjectGrid with APIs, call the following method to set the permission checking period. This method can be called only before the ObjectGrid instance is initialized. This method applies only to the local eXtreme Scale programming model when you instantiate the ObjectGrid instance directly.

    /**
     * This method takes a single parameter indicating how often you 
     * want to check the permission used to allow a client access. If the 
     * parameter is 0 then every single get/put/update/remove/evict call  
     * asks the authorization mechanism, either JAAS authorization or custom 
     * authorization, to check if the current subject has permission. This might be 
     * prohibitively expensive from a performance point of view depending on 
     * the authorization implementation, but if you need to have ever call check the 
     * authorization mechanism, then set the parameter to 0. 
     * Alternatively, if the parameter is > 0 then it indicates the number 
     * of seconds to cache a set of permissions before returning to 
     * the authorization mechanism to refresh them. This value provides much 
     * better performance, but if the back-end 
     * permissions are changed during this time then the ObjectGrid can 
     * allow or prevent access even though the back-end security 
     * provider was modified.
     * 
     * @param period the permission check period in seconds.
     */
    void setPermissionCheckPeriod(int period);

  2. Configure access-by-creator-only authorization.

    Access by creator only authorization ensures that only the user (represented by the Principal objects associated with it) who inserts the entry into the ObjectGrid map can access (read, update, invalidate and remove) that entry.

    The existing ObjectGrid map authorization model is based on the access type but not data entries. In other words, a user has a particular type of access, such as read, write, insert, delete, or invalidate, to either all the data in the map or none of the data. However, eXtreme Scale does not authorize users for individual data entry. This feature offers a new way to authorize users to data entries.

    In a scenario where different users access different sets of data, this model can be useful. When the user loads data from the persistent store into the ObjectGrid maps, the access can be authorized by the persistent store. In this case, there is no need to do another authorization in the ObjectGrid map layer. You need only ensure that the person who loads the data into the map can access it by enabling the access by creator only feature.

    Creator only mode attribute values:

    disabled

    The access by creator only feature is disabled.

    complement

    The access by creator only feature is enabled to complement the map authorization. In other words, both map authorization and access by creator only feature takes effect. Therefore, we can further limit the operations to the data. For example, the creator cannot invalidate the data.

    supersede

    The access by creator only feature is enabled to supersede the map authorization. In other words, the access by creator only feature supersedes the map authorization; no map authorization occurs.

    1. Configure the access-by-creator-only mode with an XML file.

      Use the ObjectGrid XML file to define an ObjectGrid and set the access by creator only mode to either

      disabled,

      complement, or supersede, as shown in the following example:

      <objectGrids>
          <objectGrid name="secureClusterObjectGrid" securityEnabled="true" 
               accessByCreatorOnlyMode="supersede"
              <bean id="TransactionCallback"
                    classname="com.ibm.websphere.samples.objectgrid.HeapTransactionCallback" />
          ...
      </objectGrids>

    2. Configure the access-by-creator-only mode programmatically.

      To create an ObjectGrid programmatically, we can call the following method to set the access by creator only mode. Calling this method applies only to the local eXtreme Scale programming model when you directly instantiate the ObjectGrid instance:

      /**
       * Set the "access by creator only" mode.
       * Enabling "access by creator only" mode ensures that only the user (represented * by the Principals associated with it), who inserts the record into the map,
       * can access (read, update, invalidate, and remove) the record.
       * The "access by creator only" mode can be disabled, or can complement the * ObjectGrid authorization model, or it can supersede the ObjectGrid * authorization model. The default value is disabled:
       * {@link SecurityConstants#ACCESS_BY_CREATOR_ONLY_DISABLED}.
       * @see SecurityConstants#ACCESS_BY_CREATOR_ONLY_DISABLED
       * @see SecurityConstants#ACCESS_BY_CREATOR_ONLY_COMPLEMENT
       * @see SecurityConstants#ACCESS_BY_CREATOR_ONLY_SUPERSEDE
       *
       * @param accessByCreatorOnlyMode the access by creator mode.
       *
       * @since WAS XD 6.1 FIX3
      */
      void setAccessByCreatorOnlyMode(int accessByCreatorOnlyMode);

      To further illustrate, consider a scenario in which an ObjectGrid map account is in a banking grid, and Manager1 and Employee1 are the two users. The eXtreme Scale authorization policy grants all access permissions to Manager1, but only read access permission to Employee1. The JAAS policy for the ObjectGrid map authorization is shown the following example:

      grant codebase "http://www.ibm.com/com/ibm/ws/objectgrid/security/PrivilegedAction" 
          Principal com.acme.PrincipalImpl "Manager1" {
          permission com.ibm.websphere.objectgrid.security.MapPermission 
              "banking.account", "all"};
      grant codebase "http://www.ibm.com/com/ibm/ws/objectgrid/security/PrivilegedAction" 
          Principal com.acme.PrincipalImpl "Employee1" {
          permission com.ibm.websphere.objectgrid.security.MapPermission 
              "banking.account", "read, insert"};

      Remember: Consider how the access by creator only feature affects authorization:

      • disabled If the access by creator only feature is disabled, the map authorization is no different. The user "Manager1" can access all the data in the "account" map. The user "Employee1" can read and insert all the data in the map but cannot update, invalidate, remove any data in the map.

      • complement If the access by creator only feature is enabled with "complement" option, both the map authorization and access by creator only authorization will take effect. The user "Manager1" can access the data in the "account" map, but only if the user alone loaded them into the map. The user "Employee1" can read the data in the "account" map, but only if that user alone loaded them into the map. (However, this user cannot update, invalidate, or remove any data in the map.)

      • supersede If the access by creator only feature is enabled with "supersede" option, the map authorization will not be enforced. The access by creator only authorization will be the only authorization policy. The user "Manager1" has the same privilege as in the "complement" mode: this user can access the data in the "account" map only if the same user loaded the data into the map. However, the user "Employee1" now has full access to the data in the "account" map if this user loaded them into the map. In other words, the authorization policy defined in the Java Authentication and Authorization Service (JAAS) policy will then not be enforced.


Authorizing administrative clients

Through administrative security, we can authorize users to access the data grid. Certain conditions are required, depending on the WXS installation environment and the users to have access.

When users are authorized to access a WXS data grid, those users might also be authorized to perform management operations using the xscmd command or the stopOgServer command. Most data grid deployers restrict administrative access to only a subset of the users who can access grid data.

  1. Configure authorization for xscmd operations and the stopOgServer command.

    If you use the following command to access the data grid, you might also be authorized to perform administrative actions, such as running the listAllJMXAddresses command:

    ./xscmd.sh -user <user> -password <password> <other_parameters>
    If the user can run the previous command, then any xscmd operation or the stopOgServer command might also be performed by the same user.

    When eXtreme Scale components run with WebSphere Application Server, use the WebSphere Application Server administrative console to activate the security manager. To restrict application access to local resources, click Security > Global Security, and select the check boxes, Enable administrative security and Use Java 2 Security, to restrict application access to local resources.

    Access to the management operations is controlled by the WebSphere Application Server security manager and is granted only to the users who belong to the WebSphere Administrator role. Run the xscmd command and the stopOgServer command from the WebSphere Application Server directory.

  2. Configure administrative authorization in stand-alone installations.

    When eXtreme Scale components run in a stand-alone environment, more steps are required to implement administrative security. Run the catalog servers and container servers using the Java security manager, which requires a policy file.

    The policy file resembles the following example:

    Remember: The policy file also typically contains MapPermission entries, as documented in Java SE security tutorial - Step 5 .

    grant codeBase "file:${objectgrid.home}/lib/*" {
    permission java.security.AllPermission;};
    
    grant principal javax.security.auth.x500.X500Principal "CN=manager,O=acme,OU=OGSample" {
    permission javax.management.MBeanPermission "*", "getAttribute,setAttribute,invoke,queryNames";};
    If the client is a Java Spring application, the following AgentPermission entry is needed in policy file, to allow the CN=manager account to access the data grid from the Spring client.
    grant codebase "http://www.ibm.com/com/ibm/ws/objectgrid/security/PrivilegedAction"
    principal javax.security.auth.x500.X500Principal "CN=manager,O=acme,OU=OGSample" {
    permission com.ibm.websphere.objectgrid.security.AgentPermission "*", "com.ibm.ws.objectgrid.spring.PutAgent";};
    If you configuring authorization security in a Multi-Master Replication (MMR) environment, then all catalog and container servers must run with the following policy in the og_auth.config file:
    grant {
      permission java.net.SocketPermission "localhost", "resolve";
      permission java.lang.RuntimePermission "accessDeclaredMembers";};

    In this example, only the manager principal is authorized for administrative operations with the xscmd command or the stopOgServer command. We can add other lines as necessary to give more principals MBean permissions. A different type of principal is needed if you use LDAP authentication.

    Enter the following command:

    startOgServer.sh <arguments> -jvmargs -Djava.security.auth.login.config=jaas.config 
    -Djava.security.manager -Djava.security.policy="auth.policy" -Dobjectgrid.home=$OBJECTGRID_HOME

    startXsServer.sh <arguments> -jvmargs -Djava.security.auth.login.config=jaas.config 
    -Djava.security.manager -Djava.security.policy="auth.policy" -Dobjectgrid.home=$OBJECTGRID_HOME

    startOgServer.bat <arguments> -jvmargs -Djava.security.auth.login.config=jaas.config 
    -Djava.security.manager -Djava.security.policy="auth.policy" -Dobjectgrid.home=%OBJCTGRID_HOME%
    startXsServer.bat <arguments> -jvmargs -Djava.security.auth.login.config=jaas.config -Djava.security.manager -Djava.security.policy="auth.policy" -Dobjectgrid.home=%OBJCTGRID_HOME%


Enable LDAP authentication in eXtreme scale catalog and container servers

Enable the WXS servers and catalog servers for Lightweight Directory Access Protocol (LDAP) authentication with a Java Authentication and Authorization Service (JAAS) policy file used for authorization.

In this task, you use LDAP as an authentication mechanism that provides access to the data grid, according to the permissions that you set in the JAAS authorization policy configuration file.

  1. Create a wxs_ldap.config file; for example:
    LDAPLogin {      
     com.ibm.websphere.objectgrid.security.plugins.builtins.SimpleLDAPLoginModule required      
     providerURL=.ldap://yourldapserver.yourcompany.com:389/.      
     factoryClass=.com.sun.jndi.ldap.LdapCtxFactory.  }; 

  2. Create a wxs_ldap.auth.config file. Replace the principal with the user that logs in to the data grid. Also replace YourGridName with the name of the data grid. Repeat this step as necessary for additional users and data grids. See the following example:
    grant codebase .http://www.ibm.com/com/ibm/ws/objectgrid/security/PrivilegedAction.
        principal javax.security.auth.x500.X500Principal .CN=manager,O=acme,OU=sample. {
        permission com.ibm.websphere.objectgrid.security.MapPermission .*.*., .all.;
    
        permission com.ibm.websphere.objectgrid.security.ObjectGridPermission .*.*., .all.;};
    
    Alternatively, we can grant permission to all data grids; for example:
    grant codebase .http://www.ibm.com/com/ibm/ws/objectgrid/security/PrivilegedAction.
        principal javax.security.auth.x500.X500Principal .CN=manager,O=acme,OU=sample. {
        permission com.ibm.websphere.objectgrid.security.MapPermission .*., .all.;
    
        permission com.ibm.websphere.objectgrid.security.ObjectGridPermission .*., .all.;};

  3. Create a server-side security.xml file; for example:
    <?xml version=.1.0. encoding=.UTF-8.?>
    <securityConfig xmlns:xsi=.http://www.w3.org/2001/XMLSchema-instance.
        xsi:schemaLocation=.http://ibm.com/ws/objectgrid/config/security ../objectGridSecurity.xsd.
    xmlns=.http://ibm.com/ws/objectgrid/config/security.>
    <security securityEnabled=.true. loginSessionExpirationTime=.300. >     
            <authenticator className =.com.ibm.websphere.objectgrid.security.plugins.builtins.LDAPAuthenticator.>
            </authenticator>
        </security>
    </securityConfig>

  4. Edit your objectGridServer.properties file with the following properties. If you do not have an objectGridServer.properties file, we can use the sampleServer.properties file that is in the wxs_home/properties directory to create your properties file.
    securityEnabled=true 
    credentialAuthentication=Required

  5. Start the catalog servers.

    Deprecated: The startOgServer and stopOgServer commands start servers that use the ORB transport mechanism. The ORB is deprecated, but we can continue using these scripts if you were using the ORB in a previous release. The IBM eXtremeIO (XIO) transport mechanism replaces the ORB. Use the startXsServer and stopXsServer scripts to start and stop servers that use the XIO transport.

    -Dobjectgrid.cluster.security.url=file:///security/security.xml 
    -Dobjectgrid.server.props="/security/objectGridServer.properties" 
    -Djava.security.policy="/security/wxs_ldap_auth.config"

  6. Start your container servers.
    Dobjectgrid.server.props="/security/objectGridServer.properties" 
    -Djava.security.policy="/security/wxs_ldap_auth.config"

  7. Edit your client-side objectGridClient.properties file. If WebSphere Application Server is the client, then the file that you update is was_profile_dir/properties.
       securityEnabled=true 
       credentialAuthentication=Supported

  8. Configure your client to pass the required LDAP login credentials. Load a client properties file. This file can contain the user ID and password. If the properties file does not include the user ID and password, add them to the configuration in the client program.

    In the following example, a client properties file is loaded using a program parameter. Then, the user ID and password are added to the configuration.

    String userid = .CN=manager,O=acme,OU=sample.;
    
    String pw=.password.; 
    
    //Creates a ClientSecurityConfiguration object using the specified file ClientSecurityConfiguration clientSC = ClientSecurityConfigurationFactory .getClientSecurityConfiguration(args[0]);
    
    //Creates a CredentialGenerator using the user and password.
    CredentialGenerator credGen = new UserPasswordCredentialGenerator(userid,password);
    clientSC.setCredentialGenerator(credGen);
           
    // Create an ObjectGrid by connecting to the catalog server ClientClusterContext ccContext = ogManager.connect(.cataloghostname:2809., clientSC, null);
    ObjectGrid og = ogManager.getObjectGrid(ccContext, .YourGridName.);.


Enable keystore authentication in eXtreme Scale container and catalog servers

Enable the WXS servers and catalog servers for keystore authentication with a Java Authentication and Authorization Service (JAAS) policy file used for authorization.

In this task, you use a keystore file as an authentication mechanism that provides access to the data grid, according to the permissions that you set in the JAAS authorization policy configuration file.

  1. Create a keystore with login aliases

  2. Create a wxs_keystore.config file. Replace the principal with the user that logs in to the data grid. Also, replace YourGridName with the name of the data grid. Repeat this step as necessary for more users and data grids. See the following example:
    KeyStoreLogin {
    com.ibm.websphere.objectgrid.security.plugins.builtins.KeyStoreLoginModule required keyStoreFile="/security/sampleKS.jks";}

  3. Create a server-side security.xml file; for example:
    <?xml version=.1.0. encoding=.UTF-8.?>
    <securityConfig xmlns:xsi=.http://www.w3.org/2001/XMLSchema-instance.
        xsi:schemaLocation=.http://ibm.com/ws/objectgrid/config/security ../objectGridSecurity.xsd.
    xmlns=.http://ibm.com/ws/objectgrid/config/security.>
    <security securityEnabled=.true. loginSessionExpirationTime=.300. >     
            <authenticator className="com.ibm.websphere.objectgrid.security.plugins.builtins.KeyStoreLoginAuthenticator>
            </authenticator>
        </security>
    </securityConfig>

  4. Edit your objectGridServer.properties file with the following properties. If you do not have an objectGridServer.properties file, we can use the sampleServer.properties file that is in the wxs_home/properties directory to create your properties file.
    securityEnabled=true 
    credentialAuthentication=Required

  5. Start the catalog servers.

    Deprecated: The startOgServer and stopOgServer commands start servers that use the ORB transport mechanism. The ORB is deprecated, but we can continue using these scripts if you were using the ORB in a previous release. The IBM eXtremeIO (XIO) transport mechanism replaces the ORB. Use the startXsServer and stopXsServer scripts to start and stop servers that use the XIO transport.

    startOgServer.sh catalogServer -clusterSecurityFile /security/security.xml -serverProps /security/objectGridServer.properties -jvmArgs -Djava.security.auth.login.config=./security/wxs_keystore.config.
    
    -Djava.security.policy=./security/wxs_ldap_auth.config.
    startXsServer.sh catalogServer -clusterSecurityFile /security/security.xml -serverProps /security/objectGridServer.properties -jvmArgs -Djava.security.auth.login.config=./security/wxs_keystore.config.
    
    -Djava.security.policy=./security/wxs_ldap_auth.config.

  6. Start your container servers.
    startOgServer.sh c0 -objectgridFile /xml/objectgrid.xml -deploymentPolicyFile /xml/deployment.xml -catalogServiceEndPoints cataloghostname:2809 
    -serverProps /security/objectGridServer.properties -jvmArgs -Djava.security.auth.login.config=./security/wxs_keystore.config.
    
    -Djava.security.policy=./security/wxs_ldap_auth.config.
    startXsServer.sh c0 -objectgridFile /xml/objectgrid.xml -deploymentPolicyFile /xml/deployment.xml -catalogServiceEndPoints cataloghostname:2809 
    -serverProps /security/objectGridServer.properties -jvmArgs -Djava.security.auth.login.config=./security/wxs_keystore.config.
    
    -Djava.security.policy=./security/wxs_ldap_auth.config.

  7. Edit your client-side objectGridClient.properties file. If WebSphere Application Server is the client, then the file that you update is was_profile_dir/properties.
       securityEnabled=true 
       credentialAuthentication=Supported 
       transportType=TCP/IP    
    
       singleSignOnEnabled=false

  8. Modify your client application to pass the required keystore login credentials.
    String userid = .CN=manager,O=acme,OU=sample.;
    
    String pw=.password.; 
    // Creates a ClientSecurityConfiguration object using the specified file ClientSecurityConfiguration clientSC = ClientSecurityConfigurationFactory .getClientSecurityConfiguration(args[0]);
           
    // Creates a CredentialGenerator using the passed-in user and password.
    CredentialGenerator credGen = new UserPasswordCredentialGenerator(userid,password);
    clientSC.setCredentialGenerator(credGen);
           
    // Create an ObjectGrid by connecting to the catalog server ClientClusterContext ccContext = ogManager.connect(.cataloghostname:2809., clientSC, null);
    ObjectGrid og = ogManager.getObjectGrid(ccContext, .YourGridName.);.


Configure secure transport types

Transport layer security (TLS) provides secure communication between the client and server. The communication mechanism used depends on the value of the transportType parameter specified in the client and server configuration files.

When SSL is used, the SSL configuration parameters must be provided on both the client and server side. In a Java SE environment, the SSL configuration is configured in the client or server property files. If the client or server is in WebSphere Application Server, then we can use the existing WebSphere Application Server CSIV2 transport settings for your container servers and clients. See Security integration with WebSphere Application Server for more information.

Table 1. Transport protocol to use under client transport and server transport settings.

If the transportType settings are different between the client and server, the resulting protocol can vary or result in an error.

Client transportType property Server transportType property Resulting protocol
TCP/IP TCP/IP TCP/IP
TCP/IP SSL-supported TCP/IP
TCP/IP SSL-required Error
SSL-supported TCP/IP TCP/IP
SSL-supported SSL-supported SSL (if SSL fails, then TCP/IP)
SSL-supported SSL-required SSL
SSL-required TCP/IP Error
SSL-required SSL-supported SSL
SSL-required SSL-required SSL


Transport layer security and secure sockets layer

WebSphere eXtreme Scale supports both TCP/IP and Transport Layer Security/Secure Sockets Layer (TLS/SSL) for secure communication between clients and servers.


Enable TLS/SSL in both directions

TLS/SSL is sometimes enabled in one direction. For example, the server public certificate is imported in the client truststore, but not the client public certificate is not imported to the server truststore. However, WebSphere eXtreme Scale extensively uses data grid agents. A characteristic of a data grid agent is when the server sends responds back to the client, it creates a new connection. The eXtreme Scale server then acts as a client. Therefore, you must import the client public certificate into the server truststore.


Enable transport security for Oracle JDK

WebSphere eXtreme Scale requires IBM Java Secure Sockets Extension (IBMJSSE) or the IBM Java Secure Sockets Extension 2 (IBMJSSE2). The IBMJSSE and IBMJSSE2 providers contain a reference implementation supporting SSL and Transport Layer Security (TLS) protocols and an application programming interface (API) framework.

The Oracle JDK does not ship the IBM JSSE and IBM JSSE2 providers, therefore transport security cannot be enabled with an Oracle JDK. In order to make this work, an Oracle JDK shipped with WebSphere Application Server is required. The WebSphere Application Server shipped Oracle JDK contains the IBM JSSE and IBM JSSE2 providers.

See Configuring a custom Object Request Broker for information about using a non-IBM JDK for WebSphere eXtreme Scale. If

-Djava.endorsed.dirs is configured, it points to both the objectgridRoot/lib/endorsed and the JRE/lib/endorsed directories. The directory objectgridRoot/lib/endorsed is required so the IBM ORB is used, and the directory JRE/lib/endorsed is required to load the IBM JSSE and IBM JSSE2 providers.

Use the Java SE security tutorial - Step 4 to configure your required SSL properties, to create keystores and truststores, and to start secure servers in WebSphere eXtreme Scale.


Configure SSL parameters for clients or servers

How you configure SSL parameters varies between clients and servers.

TLS/SSL is sometimes enabled in one direction. For example, the server public certificate is imported in the client truststore, but the client public certificate is not imported to the server truststore. However, WebSphere eXtreme Scale extensively uses data grid agents. A characteristic of a data grid agent is when the server sends responds back to the client, it creates a connection. The eXtreme Scale server then acts as a client. Therefore, you must import the client public certificate into the server truststore.


JMX security

We can secure managed beans (MBean) invocations in a distributed environment.

In the distributed deployment topology, MBeans are directly hosted in the catalog servers and container servers. In general, JMX security in a distributed topology follows the JMX security specification as specified in the JMX Specification. It consists of the following three parts:

  1. Authentication: The remote client needs to be authenticated in the connector server.

  2. Access control: MBean access control limits who can access the MBean information and who can perform the MBean operations.

  3. Secure transport: The transport between the JMX client and server can be secured with TLS/SSL.


Authentication

JMX provides methods for the connector servers to authenticate the remote clients. For the RMI connector, authentication is completed by supplying an object that implements the JMXAuthenticator interface when the connector server is created. So eXtreme Scale implements this JMXAuthenticator interface to use the ObjectGrid Authenticator plug-in to authenticate the remote clients. See Java SE security tutorial - Step 2 for details on how eXtreme Scale authenticates a client.

The JMX client follows the JMX APIs to provide credentials to connect to the connector server. The JMX framework passes the credential to the connector server, and then calls the JMXAuthenticator implementation for authentication. As described previously, the JMXAuthenticator implementation then delegates the authentication to the ObjectGrid Authenticator implementation.

Review the following example that describes how to connect to a connector server with a credential:

javax.management.remote.JMXServiceURL jmxUrl = new JMXServiceURL(
        "service:jmx:rmi:///jndi/rmi://localhost:1099/objectgrid/MBeanServer");

    environment.put(JMXConnector.CREDENTIALS, new UserPasswordCredential("admin", "xxxxxx"));

    // Create the JMXCconnectorServer JMXConnector cntor = JMXConnectorFactory.newJMXConnector(jmxUrl, null);

    // Connect and invoke an operation on the remote MBeanServer cntor.connect(environment);

In the preceding example, a UserPasswordCredential object is provided with the user ID set to admin and the password set to xxxxx. This UserPasswordCredential object is set in the environment map, which is used in the JMXConnector.connect(Map) method. This UserPasswordCredential object is then passed to the server by the JMX framework, and finally passed to the ObjectGrid authentication framework for authentication.

The client programming model strictly follows the JMX specification.


Access control

A JMX MBean server might have access to sensitive information and might be able to perform sensitive operations. JMX provides necessary access control that identifies which clients can access that information and who can perform those operations. The access control is built on the standard Java security model by defining permissions that control access to the MBean server and its operations.

For JMX operation access control or authorization, eXtreme Scale relies on the JAAS support provided by the JMX implementation. At any point in the execution of a program, there is a current set of permissions that a thread of execution holds. When such a thread calls a JMX specification operation, these permissions are known as the held permissions. When a JMX operation is performed, a security check is done to check whether the needed permission is implied by the held permission.

The MBean policy definition follows the Java policy format. For example, the following policy grants all signers and all code bases with the right to retrieve the server JMX address for the PlacementServiceMBean. However, the signers and code bases are restricted to the com.ibm.websphere.objectgrid domain.

grant {
    permission javax.management.MBeanPermission "com.ibm.websphere.objectgrid.management.PlacementServiceMBean#retrieveServerJMXAddress 
     [com.ibm.websphere.objectgrid:*,type=PlacementService]",
        "invoke";}

Use the following policy example to complete authorization based on remote client identity. The policy grants the same MBean permission as shown in the preceding example, except only to users with X500Principal name as:

CN=Administrator,OU=software,O=IBM,L=Rochester,ST=MN,C=US.

grant principal javax.security.auth.x500.X500Principal "CN=Administrator,OU=software,O=IBM,
    L=Rochester,ST=MN,C=US" {permission javax.management.MBeanPermission "com.ibm.websphere.objectgrid.management.PlacementServiceMBean#retrieveServerJMXAddress 
    [com.ibm.websphere.objectgrid:*,type=PlacementService]",
        "invoke";}

Java policies are checked only if the security manager is turned on. Start catalog servers and container servers with the -Djava.security.manager JVM argument to enforce the MBean operation access control.


Secure transport

The transport between the JMX client and server can be secured with TLS/SSL. If the transportType of catalog server or container server is set to SSL_Required or SSL_Supported, then use SSL to connect to the JMX server.

To use SSL, you need to configure the truststore, truststore type, and truststore password on the MBean client with -D system properties:

  1. -Djavax.net.ssl.trustStore=TRUST_STORE_LOCATION
  2. -Djavax.net.ssl.trustStorePassword=TRUST_STORE_PASSWORD
  3. -Djavax.net.ssl.trustStoreType=TRUST_STORE_TYPE

If you use com.ibm.websphere.ssl.protocol.SSLSocketFactory as your SSL socket factory in your java_home /jre/lib/security/java.security file, then use the following properties:

  1. -Dcom.ibm.ssl.trustStore=TRUST_STORE_LOCATION
  2. -Dcom.ibm.ssl.trustStorePassword=TRUST_STORE_PASSWORD
  3. -Dcom.ibm.ssl.trustStoreType=TRUST_STORE_TYPE

To obtain this information when Transport Layer Security/Secure Sockets Layer (TLS/SSL) is enabled in stand-alone configurations, start the catalog and container servers with the JMX service port set. Use one of the following methods to set the JMX service port:

The default value for the JMX service port on catalog servers is

1099. You must use a different port number for each JVM in the configuration. To use JMX/RMI, explicitly specify the-JMXServicePort option and port number, even to use the default port value.

Setting the JMX service port is required when we want to display container server information from the catalog server. For example, the port is required when we are using the xscmd -c showMapSizes command.

Set the JMX connector port to avoid ephemeral port creation. Use one of the following methods to set the JMX connector port.


Security integration with external providers

To protect your data, the product can integrate with several security providers.

WebSphere eXtreme Scale can integrate with an external security implementation. This external implementation must provide authentication and authorization services for WebSphere eXtreme Scale. WebSphere eXtreme Scale has plug-in points to integrate with a security implementation.WebSphere eXtreme Scale has been successfully integrated with the following components:

eXtreme Scale uses the security provider for the following tasks:

eXtreme Scale has the following types of authorizations:

Map authorization

Clients or groups can be authorized to perform insert, read, update, evict or delete operations on maps.

ObjectGrid authorization

Clients or groups can be authorized to perform object or entity queries on objectGrids.

DataGrid agent authorization

Clients or groups can be authorized to allow DataGrid agents to be deployed to an ObjectGrid.

Server-side map authorization

Clients or groups can be authorized to replicate a server map to client side or create a dynamic index to the server map.

Administration authorization

Clients or groups can be authorized to perform administration tasks.
If we had security already enabled for the back end, remember that these security settings are no longer sufficient to protect your data. Security settings from your database or other datastore does not in any way transfer to the cache. Separately protect the data that is now cached using the eXtreme Scale security mechanism, including authentication, authorization, and transport level security.

Use a Development Kit or Runtime Environment at Version 1.6 and later to support SSL Transport security with WebSphere eXtreme Scale Version 7.1.1 and later.


10. Securing the REST data service

Secure multiple aspects of the REST data service. Access to the eXtreme Scale REST data service can be secured through authentication and authorization. Access can also be controlled by service-scoped configuration rules, known as access rules. Transport security is the third consideration.

Access to the eXtreme Scale REST data service can be secured through authentication and authorization. Authentication and authorization is accomplished by integrating with WXS security.

Access can also be controlled by service-scoped configuration rules, known as access rules Two types of access rules exist, service operation rights which control the CRUD operations that are allowed by the service and entity access rights which control the CRUD operations that are allowed for a particular entity type.

Transport security is provided by the hosting container configuration for connections between the web client and the REST service. And transport security is provided by eXtreme Scale client configuration (for REST service to WXS data grid connections).


11. Security integration with WebSphere Application Server

When WebSphere eXtreme Scale is deployed in a WebSphere Application Server environment, we can simplify the authentication flow and transport layer security configuration from WebSphere Application Server.


Simplified authentication flow

When eXtreme Scale clients and servers are running in WebSphere Application Server and in the same security domain, we can use the WebSphere Application Server security infrastructure to propagate the client authentication credentials to the eXtreme Scale server. For example, if a servlet acts as a WXS client to connect to a WXS server in the same security domain, and the servlet is already authenticated, it is possible to propagate the authentication token from the client (servlet) to the server, and then use the WebSphere Application Server security infrastructure to convert the authentication token back to the client credentials.

Figure 1. Authentication flow for servers within the same security domain

In the previous diagram, the application servers are in the same security domain. One application server hosts the web application, which is also a WXS client. The other application server hosts the container server. The dmgr or node agent JVM hosts the catalog service. Use this type of configuration in development environments. However, for production environments run the catalog servers in separate processes, and if possible, run catalog servers on a different system from where the container servers are running. The arrows in the diagram indicate how the authentication process flows:

  1. An enterprise application user uses a Web browser to log in to the first application server with a user name and password.

  2. The first application server sends the client user name and password to the WebSphere Application Server security infrastructure to authenticate with the user registry. For example, this user registry might be an LDAP server. As a result, the security information is stored in the application server thread.

  3. The JavaServer Pages (JSP) file acts as a WXS client to retrieve the security information from the server thread. The JSP file calls the WebSphere Application Server security infrastructure to get the security tokens that represent the enterprise application user.

  4. The eXtreme Scale client, or JSP file, sends the security tokens with the request to the container server and catalog service that is hosted in the other JVMs. The catalog server and container server use the WebSphere Application Server security tokens as a WXS client credential.

  5. The catalog and container servers send the security tokens to the WebSphere Application Server security infrastructure to convert the security tokens into user security information. This user security information is represented by a Subject object, which contains the principals, public credentials, and private credentials. This conversion can occur because the application servers that are hosting the eXtreme Scale client, catalog server, and container server are sharing the same WebSphere Application Server Lightweight Third-Party Authentication (LTPA) tokens.


Authentication integration

Distributed security integration with WebSphere Application Server:

For the distributed model, use the following classes:

On the server side, use the WSTokenAuthentication authenticator to authenticate the WSTokenCredential object.

Local security integration with WebSphere Application Server:

For the local ObjectGrid model, use the following classes:

We can configure the WSSubjectSourceImpl class as the SubjectSource plug-in, and the WSSubjectValidationImpl class as the SubjectValidation plug-in.


Transport layer security support in WebSphere Application Server

When a WXS client, container server, or catalog server is running in a WebSphere Application Server process, eXtreme Scale transport security is managed by the WebSphere Application Server CSIV2 transport settings. For the eXtreme Scale client or container server, you should not use eXtreme Scale client or server properties to configure the SSL settings. All the SSL settings should be specified in the WebSphere Application Server configuration.

However, the catalog server is a little different. The catalog server has its own proprietary transport paths which cannot be managed by the WebSphere Application Server CSIV2 transport settings. Therefore, the SSL properties still need to be configured in the server properties file for the catalog server. See Tutorial: Integrate WebSphere eXtreme Scale security with WebSphere Application Server for more information.


Configure client security on a catalog service domain

By configuring client security on a catalog service domain, we can define default client authentication configuration properties. These properties are used when a client properties file is not located in the JVM that is hosting the client or when the client does not programmatically specify security properties. If a client properties file exists, the properties that you specify in the console override the values in the file. We can override these properties by specifying a splicer.properties file with the com.ibm.websphere.xs.sessionFilterProps custom property or by splicing the application EAR file.

Configure client security on the catalog service domain when you have enabled credential authentication on the server side, by configuring one of the following scenarios:

In these scenarios, a credential must be passed from the client. The credential that is passed from the client is retrieved from the getCredential method on a class that implements the CredentialGenerator interface. In an HTTP session configuration scenario, the run time must know the CredentialGenerator implementation to use to generate a credential that is passed to a remote data grid. If you do not specify the CredentialGenerator implementation class to use, the remote data grid would reject requests from the client because the client cannot be authenticated.

Define client security properties. In the WebSphere Application Server administrative console, click System administration > WebSphere eXtreme Scale > Catalog service domains > catalog_service_domain_name > Client security properties. Specify client security properties on the page and save your changes. See Client security properties for a list of the properties we can set.


Results

The client security properties that you configured on the catalog service domain are used as default values. The values you specify override any properties that are defined in the client.properties files.


What to do next

Configure the applications to use WebSphere eXtreme Scale for session management. See Automatically splicing applications for HTTP session management in WebSphere Application Server for more information.


12. Configure data grid security and SSL for .NET

We can configure .NET and Java to communicate over SSL and to use the UserPassword authentication logic.

You must have the key.jks and trust.jks files for your environment.

  1. Enable and configure security in your servers. If security is not already configured on your servers... to configure security with the external authenticator sample.

    1. Obtain the sample security files. Download the sample files in the security_extauth.zip file from on the WebSphere eXtreme Scale wiki .

      • xsjaas3.config : Defines the Java Authentication and Authorization Service (JAAS) configuration.

      • sampleKS3.jks Contains the keystore of JAAS user and password values.

      • security3.xml Defines the authenticator to use for security.

    2. Edit the xsjaas3.config file and fix the path to the sampleKS3.jks file.

    3. To generate your own private keystore instead of using the sample sampleKS3.jks file, use the keytool utility to generate the private key.
      keytool -genkey -alias myalias -keysize 2048 -keystore key.jks -keyalg rsa -dname 
      "CN=www.mydomain.com" -storepass password -keypass password -validity 3650

    4. Edit the sampleServer.properties to enable security. The sampleServer.properties file is in the wxs_install_root \properties directory. Uncomment and edit the following property values:
      securityEnabled=true
      secureTokenManagerType=none 
      alias=ogsample
      contextProvider=IBMJSSE2
      protocol=SSL
      keyStoreType=JKS
      keyStore=../../../../xio.test/etc/test/security/key.jks
      keyStorePassword=ogpass
      trustStoreType=JKS
      trustStore=../../../../xio.test/etc/test/security/trust.jks
      trustStorePassword=ogpass

    5. Start the catalog and container servers.
      startXsServer.bat cs0 -catalogServiceEndPoints cs0:localhost:6600:6601 
      -listenerPort 2809 -objectgridFile gettingstarted\xml\objectgrid.xml
      -deploymentPolicyFile gettingstarted\xml\deployment.xml -serverProps 
      ..\properties\sampleServer.properties 
      -clusterSecurityFile security3.xml -jvmArgs 
      -Djava.security.auth.login.config="xsjaas3.config"
      startXsServer.bat c0 -catalogServiceEndPoints localhost:2809 
      -objectgridFile gettingstarted\xml\objectgrid.xml
      -deploymentPolicyFile gettingstarted\xml\deployment.xml -serverProps 
      ..\properties\sampleServer.properties 
      -clusterSecurityFile security3.xml -jvmArgs 
      -Djava.security.auth.login.config="xsjaas3.config"

  2. Configure security for the .NET client.

    1. Optional: Using the keytool utility, extract the public certificate from the key.jks file that you configured for the server.
      keytool -export -alias myalias -keystore key.jks -file public.cer -storepass password
      Import this public key into the Windows Certificate store with the Certificate Management Tool, certmgr.msc, to import the key into the .Trusted Root Certification Authority. or .Trusted People. certificate folder. (The keyStore property in the client.properties file can point to this file)

    2. Edit the Client.Net.properties file to include the following property values:
      securityEnabled=true
      credentialAuthentication=supported
      authenticationRetryCount=3
      credentialGeneratorAssembly=IBM.WebSphere.Caching.CredentialGenerator,Version=8.6.0.0,
      Culture=neutral,PublicKeyToken=b439a24ee43b0816
      credentialGeneratorProps=manager manager1
      transportType=ssl-required
      publicKeyFile=<name>.cer
      The value of the credentialGeneratorProps property,

      manager manager1 is used as the user name and password values that are supplied to the server in the Credential object.

      The publicKeyFile property is set as a relative path to the .NET run time. If the publicKeyFile property is not set, the Windows certificate store is searched for the public.cer file. If the publicKeyFile property is set, then the specified file is used for the SSL public certificate file. If the specified file cannot be found, the .NET client attempts to find a matching public.cer file in the certificate store.

    3. Copy the net_client_home \IBM.WebSphere.Caching.CredentialGenerator.dll to the net_client_home \sample\SimpleClient\bin\<ConfigurationName> directory

    4. Build the sample with the ConfigurationName project context. Run the sample against your server.


13. Enable data grid authorization

WebSphere eXtreme Scale provides several security endpoints to integrate custom mechanisms. In the local programming model, the main security function is authorization, and has no authentication support. Authenticate independently from the already existing WebSphere Application Server authentication. However, we can use the provided plug-ins to obtain and validate Subject objects.

We can enable local security with the ObjectGrid XML descriptor file or programmatically.


What to do next

Start the container and catalog servers with security enabled.


14. Starting and stopping secure servers

Security is enabled by specifying security-specific configurations when you start and stop servers.


Starting secure servers in a stand-alone environment

To start secure stand-alone servers, you pass the proper configuration files by specifying parameters on the startOgServer or startXsServer command.

Deprecated: The startOgServer and stopOgServer commands start servers that use the ORB transport mechanism. The ORB is deprecated, but we can continue using these scripts if you were using the ORB in a previous release. The IBM eXtremeIO (XIO) transport mechanism replaces the ORB. Use the startXsServer and stopXsServer scripts to start and stop servers that use the XIO transport.

Next topic: Start and stopping secure servers in the Liberty profile


Starting secure servers in WebSphere Application Server

To start secure servers in WebSphere Application Server, specify the security configuration files in the generic Java virtual machine (JVM) arguments.


Stopping secure servers

Stopping secure catalog servers or container servers requires one security configuration file.


15. Configure WebSphere eXtreme Scale to use FIPS 140-2

Federal Information Processing Standard (FIPS) 140-2 specifies required levels of encryption for Transport Layer Security/Secure Sockets Layer (TLS/SSL). This standard ensures high protection of data as it is sent over the wire.

Use the following steps to configure the catalog servers and container servers in the WXS stand-alone installation to use FIPS.

If we are using WebSphere eXtreme Scale integrated with WebSphere Application Server, the catalog servers and container servers inherit the security properties from the application server. When a catalog server runs in WebSphere Application Server, some of the communication is controlled by the server.properties file. Update the server.properties file to contain the same properties that are required for stand-alone catalog servers.

  1. Edit the java.security file. The location of the java.security depends on your JVM configuration:

    • If we are using the default JVM that ships with the product, the file is in the /path/to/java/jre/lib/security directory.

    • If we are using a different JVM, edit the file in the java_home /jre/lib/security directory.

    The file must contain the following text:

    security.provider.1=com.ibm.crypto.fips.provider.IBMJCEFIPS
    security.provider.2=com.ibm.jsse2.IBMJSSEProvider2
    security.provider.3=com.ibm.crypto.provider.IBMJCE
    security.provider.4=com.ibm.security.jgss.IBMJGSSProvider security.provider.5=com.ibm.security.cert.IBMCertPath security.provider.6=com.ibm.security.sasl.IBMSASL
    security.provider.7=com.ibm.xml.crypto.IBMXMLCryptoProvider security.provider.8=com.ibm.xml.enc.IBMXMLEncProvider security.provider.9=org.apache.harmony.security.provider.PolicyProvider security.provider.10=com.ibm.security.jgss.mech.spnego.IBMSPNEGO

  2. Edit the server properties files for the catalog server and container servers.

    These files must contain the following properties and values:

    contextProvider=IBMJSSE2
    transportType=SSL-Required

  3. Configure key pairs that use the RSA key generation algorithm in the key ring for the catalog server and container servers. The minimum key length is 1024 bits.

  4. Restart the catalog and container servers.

    When you start the catalog servers, specify JVM arguments. The arguments you use depend on which version of Java SE we are using.

    • For Java 5 and Java 6 up to SR 9, specify the -Dcom.ibm.jsse2.JSSEFIPS=true argument when you start the server.

    • For Java 6 SR 10 and later, or Java 7, specify the -Dcom.ibm.jsse2.usefipsprovider=true argument when you start the server.


16. Configure security profiles for the xscmd utility

By creating a security profile, we can use saved security parameters to use the xscmd utility with secure environments.

Use the -ssp profile_name or --saveSecProfile profile_name parameter with the rest of your xscmd command. to save a security profile. The profile can contain settings for user names and passwords, credential generators, keystores, truststores, and transport types. The ProfileManagement command group in the xscmd utility contains commands for managing your security profiles.


17. Securing J2C client connections

Use the Java 2 Connector (J2C) architecture to secure connections between WebSphere eXtreme Scale clients and the applications.

Applications reference the connection factory, which establishes the connection to the remote data grid. Each connection factory hosts a single eXtreme Scale client connection that is reused for all application components.

Since the eXtreme Scale client connection might include a near cache, it is important that applications do not share a connection. A connection factory must exist for a single application instance to avoid problems sharing objects between applications.

We can set the credential generator with the API or in the client properties file. In the client properties file, the securityEnabled and credentialGenerator properties are used.

In the following example, some lines of code are continued on the next line for publication purposes.

securityEnabled=true credentialGeneratorClass=com.ibm.websphere.objectgrid.security.plugins.builtins.
     UserPasswordCredentialGenerator credentialGeneratorProps=operator XXXXXX

The credential generator and credential in the client properties file are used for the eXtreme Scale connect operation and the default J2C credentials. Therefore, the credentials specified with the API are used at J2C connect time for the J2C connection. However, if no credentials are specified at J2C connect time, then the credential generator in the client properties file is used.

  1. Set up secure access where the J2C connection represents the eXtreme Scale client. Use the ClientPropertiesResource connection factory property or the ClientPropertiesURL connection factory property to configure client authentication.

    If we are using WebSphere eXtreme Scale with WebSphere Application Server, then specify the client properties on the catalog service domain configuration. When the connection factory references the domain, it automatically uses this configuration.

  2. Configure the client security properties to use the connection factory that references the appropriate credential generator object for eXtreme Scale. These properties are also compatible with WXS server security. For example, use the WSTokenCredentialGenerator credential generator for WebSphere credentials when eXtreme Scale is installed with WebSphere Application Server. Alternatively, use the UserPasswordCredentialGenerator credential generator when we run the eXtreme Scale in a stand-alone environment.

    In the following example, credentials are passed programmatically using the API call instead of using the configuration in the client properties:

    XSConnectionSpec spec = new XSConnectionSpec();
    spec.setCredentialGenerator(new UserPasswordCredentialGenerator("operator", "xxxxxx"));
    Connection conn = connectionFactory.getConnection(spec);

  3. (Optional) Disable the near cache, if required.

    All J2C connections from a single connection factory share a single near cache. Grid entry permissions and map permissions are validated on the server, but not on the near cache . When an application uses multiple credentials to create J2C connections, and the configuration uses specific permissions for grid entries and maps for those credentials, then disable the near cache . Disable the near cache using the connection factory property, ObjectGridResource or ObjectGridURL.

  4. (Optional) Set security policy settings, if required.

    If the J2EE application contains the embedded eXtreme Scale resource adapter archive (RAR) file configuration, you might be required to set additional security policy settings in the security policy file for the application. For example, these policies are required:

    permission com.ibm.websphere.security.WebSphereRuntimePermission "accessRuntimeClasses";
    permission java.lang.RuntimePermission "accessDeclaredMembers";
    permission javax.management.MBeanTrustPermission "register";
    permission java.lang.RuntimePermission "getClassLoader";

    Additionally, any property or resource files used by connection factories require file or other permissions, such as permission java.io.FilePermission "filePath";. For WebSphere Application Server, the policy file is META-INF/was.policy, and it is located in the J2EE EAR file.


Results

The client security properties that you configured on the catalog service domain are used as default values. The values that you specify override any properties that are defined in the client.properties files.


What to do next

Use eXtreme Scale data access APIs to develop client components to use transactions.


18. Programming for security

Use programming interfaces to handle various aspects of security in a WXS environment.


Security API

WebSphere eXtreme Scale adopts an open security architecture. It provides a basic security framework for authentication, authorization, and transport security, and requires users to implement plug-ins to complete the security infrastructure.

The following image shows the basic flow of client authentication and authorization for a WXS server.

Figure 1. Flow of client authentication and authorization

The authentication flow and authorization flow are as follows.

Authentication flow

  1. The authentication flow starts with a WXS client getting a credential. This is done by the com.ibm.websphere.objectgrid.security.plugins.CredentialGenerator plug-in.

  2. A CredentialGenerator object knows how to generate a valid client credential, for example, a user ID and password pair, Kerberos ticket, and so on. This generated credential is sent back to the client.

  3. After the client retrieves the Credential object using the CredentialGenerator object, this Credential object is sent along with the eXtreme Scale request to the eXtreme Scale server.

  4. The eXtreme Scale server authenticates the Credential object before processing the eXtreme Scale request. Then the server uses the Authenticator plug-in to authenticate the Credential object.

  5. The Authenticator plug-in represents an interface to the user registry, for example, a Lightweight Directory Access Protocol (LDAP) server or an operating system user registry. The Authenticator consults the user registry and makes authentication decisions.

  6. If the authentication is successful, a Subject object is returned to represent this client.

    Authorization flow

    WebSphere eXtreme Scale adopts a permission-based authorization mechanism, and has different permission categories represented by different permission classes. For example, a com.ibm.websphere.objectgrid.security.MapPermission object represents permissions to read, write, insert, invalidate, and remove the data entries in an ObjectMap. Because WebSphere eXtreme Scale supports Java Authentication and Authorization Service (JAAS) authorization out-of-box, we can use JAAS to handle authorization by providing authorization policies.

    Also, eXtreme Scale supports custom authorizations. Custom authorizations are plugged in by the plug-in com.ibm.websphere.objectgrid.security.plugins.ObjectGridAuthorization. The flow of the customer authorization is as follows.

  7. The server runtime sends the Subject object and the required permission to the authorization plug-in.

  8. The authorization plug-in consults the Authorization service and makes an authorization decision. If permission is granted for this Subject object, a value of true is returned, otherwise falseis returned.

  9. This authorization decision, true or false, is returned to the server runtime.

Security implementation

The topics in this section discuss how to program a secure WebSphere eXtreme Scale deployment and how to program the plug-in implementations. The section is organized based on the various security features. In each subtopic, you will learn about relevant plug-ins and how to implement the plug-ins. In the authentication section, you will see how to connect to a secure WebSphere eXtreme Scale deployment environment.

Client Authentication: The client authentication topic describes how a WXS client gets a credential and how a server authenticates the client. It will also discuss how a WXS client connects to a secure WebSphere eXtreme Scale server.

Authorization: The authorization topic explains how to use the ObjectGridAuthorization to do customer authorization besides JAAS authorization.

Grid Authentication: The data grid authentication topic discusses how we can use SecureTokenManager to securely transport server secrets.

JMX programming: When the WebSphere eXtreme Scale server is secured, the JMX client might need to send a JMX credential to the server.


Client authentication programming

For authentication, WebSphere eXtreme Scale provides a runtime to send the credential from the client to the server side, and then calls the authenticator plug-in to authenticate the users.

WebSphere eXtreme Scale requires you to implement the following plug-ins to complete the authentication.


Credential and CredentialGenerator plug-ins

When a WXS client connects to a server that requires authentication, the client is required to provide a client credential. A client credential is represented by a com.ibm.websphere.objectgrid.security.plugins.Credential interface. A client credential can be a user name and password pair, a Kerberos ticket, a client certificate, or data in any format that the client and server agree upon. This interface explicitly defines the equals(Object) and hashCode methods. These two methods are important because the authenticated Subject objects are cached using the Credential object as the key on the server side. WebSphere eXtreme Scale also provides a plug-in to generate a credential. This plug-in is represented by the com.ibm.websphere.objectgrid.security.plugins.CredentialGenerator interface and is useful when the credential can expire. In this case, the getCredential method is called to renew a credential.

The Credential interface explicitly defines the equals(Object) and hashCode methods. These two methods are important because the authenticated Subject objects are cached using the Credential object as the key on the server side.

You may also use the provided plug-in to generate a credential. This plug-in is represented by the com.ibm.websphere.objectgrid.security.plugins.CredentialGenerator interface, and is useful when the credential can expire. In this case, the getCredential method is called to renew a credential. See CredentialGenerator interface for more details.

There are three provided default implementations for the Credential interfaces:

WebSphere eXtreme Scale also provides a plug-in to generate a credential. This plug-in is represented by the com.ibm.websphere.objectgrid.security.plugins.CredentialGenerator interface.WebSphere eXtreme Scale provides two default built-in implementations:

UserPasswordCredential and UserPasswordCredentialGenerator

For testing purposes, WebSphere eXtreme Scale provides the following plug-in implementations:

  1. com.ibm.websphere.objectgrid.security.plugins.builtins.UserPasswordCredential
  2. com.ibm.websphere.objectgrid.security.plugins.builtins.UserPasswordCredentialGenerator

The user password credential stores a user ID and password. The user password credential generator then contains this user ID and password.

The following example code shows how to implement these two plug-ins.

UserPasswordCredential.java
// This sample program is provided AS IS and may be used, executed, copied and modified
// without royalty payment by customer
// (a) for its own instruction and study,
// (b) to develop applications designed to run with an IBM WebSphere product,
// either for customer's own internal use or for redistribution by customer, as part of such an
// application, in customer's own products.
// Licensed Materials - Property of IBM
// 5724-J34 © COPYRIGHT International Business Machines Corp. 2007
package com.ibm.websphere.objectgrid.security.plugins.builtins;

import com.ibm.websphere.objectgrid.security.plugins.Credential;

/**
 * This class represents a credential containing a user ID and password.
 *
 * @ibm-api
 * @since WAS XD 6.0.1
 *
 * @see Credential
 * @see UserPasswordCredentialGenerator#getCredential()
 */
public class UserPasswordCredential implements Credential {

    private static final long serialVersionUID = 1409044825541007228L;

    private String ivUserName;

    private String ivPassword;

    /**
     * Creates a UserPasswordCredential with the specified user name and      * password.
     *
     * @param userName the user name for this credential
     * @param password the password for this credential
     *
     * @throws IllegalArgumentException if userName or password is <code>null</code>
     */
    public UserPasswordCredential(String userName, String password) {
        super();
        if (userName == null || password == null) {
            throw new IllegalArgumentException("User name and password cannot be null.");
        }
        this.ivUserName = userName;
        this.ivPassword = password;
    }

    /**
     * Gets the user name for this credential.
     *
     * @return   the user name argument that was passed to the constructor
     *           or the <code>setUserName(String)</code>
     *           method of this class      *
     * @see #setUserName(String)
     */
    public String getUserName() {
        return ivUserName;
    }

    /**
     * Sets the user name for this credential.
     *
     * @param userName the user name to set.
     *
     * @throws IllegalArgumentException if userName is <code>null</code>
     */
    public void setUserName(String userName) {
        if (userName == null) {
            throw new IllegalArgumentException("User name cannot be null.");
        }
        this.ivUserName = userName;
    }

    /**
     * Gets the password for this credential.
     *
     * @return   the password argument that was passed to the constructor
     *           or the <code>setPassword(String)</code>
     *           method of this class      *
     * @see #setPassword(String)
     */
    public String getPassword() {
        return ivPassword;
    }

    /**
     * Sets the password for this credential.
     *
     * @param password the password to set.
     *
     * @throws IllegalArgumentException if password is <code>null</code>
     */
    public void setPassword(String password) {
        if (password == null) {
            throw new IllegalArgumentException("Password cannot be null.");
        }
        this.ivPassword = password;
    }

    /**
     * Checks two UserPasswordCredential objects for equality.
     * <p>
     * Two UserPasswordCredential objects are equal if and only if their user names
     * and passwords are equal.
     *
     * @param o the object we are testing for equality with this object.
     *
     * @return <code>true</code> if both UserPasswordCredential objects are equivalent.
     *
     * @see Credential#equals(Object)
     */
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o instanceof UserPasswordCredential) {
            UserPasswordCredential other = (UserPasswordCredential) o;
            return other.ivPassword.equals(ivPassword) && other.ivUserName.equals(ivUserName);
        }

        return false;
    }

    /**
     * Returns the hashcode of the UserPasswordCredential object.
     *
     * @return the hash code of this object
     *
     * @see Credential#hashCode()
     */
    public int hashCode() {
        return ivUserName.hashCode() + ivPassword.hashCode();
    }}
UserPasswordCredentialGenerator.java
// This sample program is provided AS IS and may be used, executed, copied and modified
// without royalty payment by customer
// (a) for its own instruction and study,
// (b) to develop applications designed to run with an IBM WebSphere product,
// either for customer's own internal use or for redistribution by customer, as part of such an
// application, in customer's own products.
// Licensed Materials - Property of IBM
// 5724-J34 © COPYRIGHT International Business Machines Corp. 2007
package com.ibm.websphere.objectgrid.security.plugins.builtins;

import java.util.StringTokenizer;

import com.ibm.websphere.objectgrid.security.plugins.Credential;
import com.ibm.websphere.objectgrid.security.plugins.CredentialGenerator;

/**
 * This credential generator creates <code>UserPasswordCredential</code> objects.
 * <p>
 * UserPasswordCredentialGenerator has a one to one relationship with
 * UserPasswordCredential because it can only create a UserPasswordCredential
 * representing one identity.
 *
 * @since WAS XD 6.0.1
 * @ibm-api
 *
 * @see CredentialGenerator
 * @see UserPasswordCredential
 */
public class UserPasswordCredentialGenerator implements CredentialGenerator {

    private String ivUser;

    private String ivPwd;

    /**
     * Creates a UserPasswordCredentialGenerator with no user name or password.
     *
     * @see #setProperties(String)
     */
    public UserPasswordCredentialGenerator() {
        super();
    }

    /**
     * Creates a UserPasswordCredentialGenerator with a specified user name and      * password
     *
     * @param user the user name
     * @param pwd the password
     */
    public UserPasswordCredentialGenerator(String user, String pwd) {
        ivUser = user;
        ivPwd = pwd;
    }

    /**
     * Creates a new <code>UserPasswordCredential</code> object using this
     * object's user name and password.
     *
     * @return a new <code>UserPasswordCredential</code> instance      *
     * @see CredentialGenerator#getCredential()
     * @see UserPasswordCredential
     */
    public Credential getCredential() {
        return new UserPasswordCredential(ivUser, ivPwd);
    }

    /**
     * Gets the password for this credential generator.
     *
     * @return   the password argument that was passed to the constructor
     */
    public String getPassword() {
        return ivPwd;
    }

    /**
     * Gets the user name for this credential.
     *
     * @return   the user argument that was passed to the constructor
     *           of this class      */
    public String getUserName() {
        return ivUser;
    }
    /**
     * Sets additional properties namely a user name and password.
     *
     * @param properties a properties string  with a user name and      *                   a password separated by a blank.
     *
     * @throws IllegalArgumentException if the format is not valid
     */
    public void setProperties(String properties) {
        StringTokenizer token = new StringTokenizer(properties, " ");
        if (token.countTokens() != 2) {
            throw new IllegalArgumentException(
                "The properties should have a user name and password and separated by a blank.");
        }

        ivUser = token.nextToken();
        ivPwd = token.nextToken();
    }
    /**
     * Checks two UserPasswordCredentialGenerator objects for equality.
     * <p>
     * Two UserPasswordCredentialGenerator objects are equal if and only if
     * their user names and passwords are equal.
     *
     * @param obj the object we are testing for equality with this object.
     *
     * @return <code>true</code> if both UserPasswordCredentialGenerator objects
     *         are equivalent.
     */
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }

        if (obj != null && obj instanceof UserPasswordCredentialGenerator) {
            UserPasswordCredentialGenerator other = (UserPasswordCredentialGenerator) obj;

            boolean bothUserNull = false;
            boolean bothPwdNull = false;

            if (ivUser == null) {
                if (other.ivUser == null) {
                    bothUserNull = true;
                } else {
                    return false;
                }
            }

            if (ivPwd == null) {
                if (other.ivPwd == null) {
                    bothPwdNull = true;
                } else {
                    return false;
                }
            }

            return (bothUserNull || ivUser.equals(other.ivUser)) && (bothPwdNull || ivPwd.equals(other.ivPwd));
        }

        return false;
    }

    /**
     * Returns the hashcode of the UserPasswordCredentialGenerator object.
     *
     * @return the hash code of this object
     */
    public int hashCode() {

        return ivUser.hashCode() + ivPwd.hashCode();
    }}

The UserPasswordCredential class contains two attributes: user name and password. The UserPasswordCredentialGenerator serves as a factory that contains the UserPasswordCredential objects.

WSTokenCredential and WSTokenCredentialGenerator

When the WebSphere eXtreme Scale clients and servers are all deployed in WebSphere Application Server, the client application can use these two built-in implementations when the following conditions are satisfied:

  1. WebSphere Application Server global security is turned on.

  2. All WebSphere eXtreme Scale clients and servers are running in WebSphere Application Server Java virtual machines.

  3. The application servers are in the same security domain.

  4. The client is already authenticated in WebSphere Application Server.

In this situation, the client can use the com.ibm.websphere.objectgrid.security.plugins.builtins.WSTokenCredentialGenerator class to generate a credential. The server uses the WSAuthenticator implementation class to authenticate the credential.

This scenario takes advantage of the fact that the eXtreme Scale client has already been authenticated. Because the application servers that have the servers are in the same security domain as the application servers that house the clients, the security tokens can be propagated from the client to the server so that the same user registry does not need to be authenticated again. Do not assume that a CredentialGenerator always generates the same credential. For an expirable and refreshable credential, the CredentialGenerator should be able to generate the latest valid credential to make sure the authentication succeeds. One example is using the Kerberos ticket as a Credential object. When the Kerberos ticket refreshes, the CredentialGenerator should retrieve the refreshed ticket when CredentialGenerator.getCredential is called.


Authenticator plug-in

After the eXtreme Scale client retrieves the Credential object using the CredentialGenerator object, this client Credential object is sent along with the client request to the eXtreme Scale server. The server authenticates the Credential object before processing the request. If the Credential object is authenticated successfully, a Subject object is returned to represent this client.

This Subject object is then cached, and it expires after its lifetime reaches the session timeout value. The login session timeout value can be set using the loginSessionExpirationTime property in the cluster XML file. For example, setting loginSessionExpirationTime="300" makes the Subject object expire in 300 seconds.

This Subject object is then used for authorizing the request, which is shown later. An eXtreme Scale server uses the Authenticator plug-in to authenticate the Credential object. See Authenticator for more details.

The Authenticator plug-in is where the eXtreme Scale runtime authenticates the Credential object from the client user registry, for example, a Lightweight Directory Access Protocol (LDAP) server.

WebSphere eXtreme Scale does not provide an immediately available user registry configuration. The configuration and management of user registry is left outside of WXS for simplicity and flexibility. This plug-in implements connecting and authenticating to the user registry. For example, an Authenticator implementation extracts the user ID and password from the credential, uses them to connect and validate to an LDAP server, and creates a Subject object as a result of the authentication. The implementation might use JAAS login modules. A Subject object is returned as a result of authentication.

Notice that this method creates two exceptions: InvalidCredentialException and ExpiredCredentialException. The InvalidCredentialException exception indicates that the credential is not valid. The ExpiredCredentialException exception indicates that the credential expired. If one of these two exceptions result from the authenticate method, the exceptions are sent back to the client. However, the client runtime handles these two exceptions differently:

The Authenticator interface provides great flexibility. We can implement the Authenticator interface in your own specific way. For example, we can implement this interface to support two different user registries.

WebSphere eXtreme Scale provides sample authenticator plug-in implementations. Except for the WebSphere Application Server authenticator plug-in, the other implementations are only samples for testing purposes.

KeyStoreLoginAuthenticator

This example uses a WXS built-in implementation: KeyStoreLoginAuthenticator, which is for testing and sample purposes (a keystore is a simple user registry and should not be used for a production environment). Again, the class is displayed to further demonstrate how to implement an authenticator.

In the following example, some lines of code are continued on the next line for publication purposes.

KeyStoreLoginAuthenticator.java
// This sample program is provided AS IS and may be used, executed, copied and modified
// without royalty payment by customer
// (a) for its own instruction and study,
// (b) to develop applications designed to run with an IBM WebSphere product,
// either for customer's own internal use or for redistribution by customer, as part of such an
// application, in customer's own products.
// Licensed Materials - Property of IBM
// 5724-J34 © COPYRIGHT International Business Machines Corp. 2007

package com.ibm.websphere.objectgrid.security.plugins.builtins;

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import com.ibm.websphere.objectgrid.security.plugins.Authenticator;
import com.ibm.websphere.objectgrid.security.plugins.Credential;
import com.ibm.websphere.objectgrid.security.plugins.ExpiredCredentialException;
import com.ibm.websphere.objectgrid.security.plugins.InvalidCredentialException;
import com.ibm.ws.objectgrid.Constants;
import com.ibm.ws.objectgrid.ObjectGridManagerImpl;
import com.ibm.ws.objectgrid.security.auth.callback.UserPasswordCallbackHandlerImpl;

/**
 * This class is an implementation of the <code>Authenticator</code> interface
 * when a user name and password are used as a credential.
 * <p>
 * When user ID and password authentication is used, the credential passed to the  * <code>authenticate(Credential)</code> method is a UserPasswordCredential object.
 * <p>
 * This implementation will use a <code>KeyStoreLoginModule</code> to authenticate
 * the user into the keystore using the JAAS login module "KeyStoreLogin". The key
 * store can be configured as an option to the <code>KeyStoreLoginModule</code>
 * class. Please see the <code>KeyStoreLoginModule</code> class for more details
 * about how to set up the JAAS login configuration file.
 * <p>
 * This class is only for sample and quick testing purpose. Users should
 * write your own Authenticator implementation which can fit better into
 * the environment.
 *
 * @ibm-api
 * @since WAS XD 6.0.1
 *
 * @see Authenticator
 * @see KeyStoreLoginModule
 * @see UserPasswordCredential
 */
public class KeyStoreLoginAuthenticator implements Authenticator {

    /**
     * Creates a new KeyStoreLoginAuthenticator.
     */
    public KeyStoreLoginAuthenticator() {
        super();
    }

    /**
     * Authenticates a <code>UserPasswordCredential</code>.
     * <p>
     * Uses the user name and password from the specified UserPasswordCredential
     * to login to the KeyStoreLoginModule named "KeyStoreLogin".
     *
     * @throws InvalidCredentialException if credential isn't a      *         UserPasswordCredential or some error occurs during processing
     *         of the supplied UserPasswordCredential
     *
     * @throws ExpiredCredentialException if credential is expired.  This exception
     *         is not used by this implementation
     *
     * @see Authenticator#authenticate(Credential)
     * @see KeyStoreLoginModule
     */
    public Subject authenticate(Credential credential) throws InvalidCredentialException, 
   ExpiredCredentialException {

        if (credential == null) {
            throw new InvalidCredentialException("Supplied credential is null");
        }

        if (! (credential instanceof UserPasswordCredential) ) {
            throw new InvalidCredentialException("Supplied credential is not a UserPasswordCredential");
        }

        UserPasswordCredential cred = (UserPasswordCredential) credential;
        LoginContext lc = null;
        try {
            lc = new LoginContext("KeyStoreLogin",
                    new UserPasswordCallbackHandlerImpl(cred.getUserName(), cred.getPassword().toCharArray()));

            lc.login();

            Subject subject = lc.getSubject();

            return subject;
        }
        catch (LoginException le) {
            throw new InvalidCredentialException(le);
        }
        catch (IllegalArgumentException ile) {
            throw new InvalidCredentialException(ile);
        }
    }}

In the following example, some lines of code are continued on the next line for publication purposes.

KeyStoreLoginModule.java
// This sample program is provided AS IS and may be used, executed, copied and modified
// without royalty payment by customer
// (a) for its own instruction and study,
// (b) to develop applications designed to run with an IBM WebSphere product,
// either for customer's own internal use or for redistribution by customer, as part of such an
// application, in customer's own products.
// Licensed Materials - Property of IBM
// 5724-J34 © COPYRIGHT International Business Machines Corp. 2007
package com.ibm.websphere.objectgrid.security.plugins.builtins;

import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import javax.security.auth.x500.X500Principal;
import javax.security.auth.x500.X500PrivateCredential;

import com.ibm.websphere.objectgrid.ObjectGridRuntimeException;
import com.ibm.ws.objectgrid.Constants;
import com.ibm.ws.objectgrid.ObjectGridManagerImpl;
import com.ibm.ws.objectgrid.util.ObjectGridUtil;

/**
 * A KeyStoreLoginModule is keystore authentication login module based on 
 * JAAS authentication.
 * <p>
 * A login configuration should provide an option "<code>keyStoreFile</code>" to 
 * indicate where the keystore file is located. If the <code>keyStoreFile</code> 
 * value contains a system property in the form, <code>${system.property}</code>,
 * it will be expanded to the value of the system property.
 * <p>
 * If an option "<code>keyStoreFile</code>" is not provided, the default keystore
 * file name is <code>"${java.home}${/}.keystore"</code>.
 * <p>
 * Here is a Login module configuration example: 
 * <pre><code>
 *    KeyStoreLogin {
 *        com.ibm.websphere.objectgrid.security.plugins.builtins.KeystoreLoginModule required
 *            keyStoreFile="${user.dir}${/}security${/}.keystore";
 *    };
 * </code></pre>
 * 
 * @ibm-api
 * @since WAS XD 6.0.1  
 * 
 * @see LoginModule
 */
public class KeyStoreLoginModule implements LoginModule {

    private static final String CLASS_NAME = KeyStoreLoginModule.class.getName();

    /** 
     * keystore file property name 
     */
    public static final String KEY_STORE_FILE_PROPERTY_NAME = "keyStoreFile";

    /**
     * keystore type. Only JKS is supported 
     */
    public static final String KEYSTORE_TYPE = "JKS";

    /**
     * The default keystore file name 
     */
    public static final String DEFAULT_KEY_STORE_FILE = "${java.home}${/}.keystore";

    private CallbackHandler handler;

    private Subject subject;

    private boolean debug = false;

    private Set principals = new HashSet();

    private Set publicCreds = new HashSet();

    private Set privateCreds = new HashSet();

    protected KeyStore keyStore;

    /**
     * Creates a new KeyStoreLoginModule.
     */
    public KeyStoreLoginModule() {
    }

    /**
     * Initializes the login module.
     * 
     * @see LoginModule#initialize(Subject, CallbackHandler, Map, Map)
     */
    public void initialize(Subject sub, CallbackHandler callbackHandler,
            Map mapSharedState, Map mapOptions) {

        // initialize any configured options
        debug = "true".equalsIgnoreCase((String) mapOptions.get("debug"));

        if (sub == null)
            throw new IllegalArgumentException("Subject is not specified"); 

        if (callbackHandler == null)
            throw new IllegalArgumentException(
            "CallbackHander is not specified"); 

        // Get the keystore path
        String sKeyStorePath = (String) mapOptions
            .get(KEY_STORE_FILE_PROPERTY_NAME);

        // If there is no keystore path, the default one is the .keystore
        // file in the java home directory
        if (sKeyStorePath == null) {
            sKeyStorePath = DEFAULT_KEY_STORE_FILE;
        }

        // Replace the system enviroment variable
        sKeyStorePath = ObjectGridUtil.replaceVar(sKeyStorePath);

        File fileKeyStore = new File(sKeyStorePath);

        try {
            KeyStore store = KeyStore.getInstance("JKS");
            store.load(new FileInputStream(fileKeyStore), null);

            // Save the keystore
            keyStore = store;

            if (debug) {
                System.out.println("[KeyStoreLoginModule] initialize: Successfully loaded keystore");
            }
        }
        catch (Exception e) {
            ObjectGridRuntimeException re = new ObjectGridRuntimeException(
                    "Failed to load keystore: " + fileKeyStore.getAbsolutePath()); 
            re.initCause(e);
            if (debug) {
                System.out.println("[KeyStoreLoginModule] initialize: keystore loading failed with exception "
                        + e.getMessage());
            }
        }

        this.subject = sub;
        this.handler = callbackHandler;
    }

    /**
     * Authenticates a user based on the keystore file.
     * 
     * @see LoginModule#login()
     */
    public boolean login() throws LoginException {

        if (debug) {
            System.out.println("[KeyStoreLoginModule] login: entry");
        }

        String name = null;
        char pwd[] = null;

        if (keyStore == null || subject == null || handler == null) {
            throw new LoginException("Module initialization failed"); 
        }

        NameCallback nameCallback = new NameCallback("Username:");
        PasswordCallback pwdCallback = new PasswordCallback("Password:", false);

        try {
            handler.handle(new Callback[] { nameCallback, pwdCallback });
        }
        catch (Exception e) {
            throw new LoginException("Callback failed: " + e); 
        }

        name = nameCallback.getName();
        char[] tempPwd = pwdCallback.getPassword();

        if (tempPwd == null) {
            // treat a NULL password as an empty password
            tempPwd = new char[0];
        }
        pwd = new char[tempPwd.length];
        System.arraycopy(tempPwd, 0, pwd, 0, tempPwd.length);

        pwdCallback.clearPassword();

        if (debug) {
            System.out.println("[KeyStoreLoginModule] login: "
                    + "user entered user name: " + name);
        }

        // Validate the user name and password
        try {
            validate(name, pwd);
        }
        catch (SecurityException se) {
            principals.clear();
            publicCreds.clear();
            privateCreds.clear();
            LoginException le = new LoginException(
            "Exception encountered during login");
            le.initCause(se);

            throw le;
        }

        if (debug) {
            System.out.println("[KeyStoreLoginModule] login: exit");
        }
        return true;
    }

    /**
     * Indicates the user is accepted. 
     * <p>
     * This method is called only if the user is authenticated by all modules in
     * the login configuration file. The principal objects will be added to the 
     * stored subject.
     * 
     * @return false if for some reason the principals cannot be added; true
     *         otherwise
     * 
     * @exception LoginException
     *                LoginException is thrown if the subject is readonly or if
     *                any unrecoverable exceptions is encountered.
     * 
     * @see LoginModule#commit()
     */
    public boolean commit() throws LoginException {
        if (debug) {
            System.out.println("[KeyStoreLoginModule] commit: entry");
        }

        if (principals.isEmpty()) {
            throw new IllegalStateException("Commit is called out of sequence");
        }

        if (subject.isReadOnly()) {
            throw new LoginException("Subject is Readonly");
        }

        subject.getPrincipals().addAll(principals);
        subject.getPublicCredentials().addAll(publicCreds);
        subject.getPrivateCredentials().addAll(privateCreds);

        principals.clear();
        publicCreds.clear();
        privateCreds.clear();

        if (debug) {
            System.out.println("[KeyStoreLoginModule] commit: exit");
        }
        return true;
    }

    /**
     * Indicates the user is not accepted
     * 
     * @see LoginModule#abort()
     */
    public boolean abort() throws LoginException {
        boolean b = logout();
        return b;
    }

    /**
     * Logs the user out. Clear all the maps.
     * 
     * @see LoginModule#logout()
     */
    public boolean logout() throws LoginException {


        // Clear the instance variables
        principals.clear();
        publicCreds.clear();
        privateCreds.clear();

        // clear maps in the subject
        if (!subject.isReadOnly()) {
            if (subject.getPrincipals() != null) {
                subject.getPrincipals().clear();
            }

            if (subject.getPublicCredentials() != null) {
                subject.getPublicCredentials().clear();
            }

            if (subject.getPrivateCredentials() != null) {
                subject.getPrivateCredentials().clear();
            }
        }
        return true;
    }

    /**
     * Validates the user name and password based on the keystore.
     * 
     * @param userName user name
     * @param password password
     * @throws SecurityException if any exceptions encountered
     */
    private void validate(String userName, char password[])
        throws SecurityException {

        PrivateKey privateKey = null;

        // Get the private key from the keystore
        try {
            privateKey = (PrivateKey) keyStore.getKey(userName, password);
        }
        catch (NoSuchAlgorithmException nsae) {
            SecurityException se = new SecurityException();
            se.initCause(nsae);
            throw se;
        }
        catch (KeyStoreException kse) {
            SecurityException se = new SecurityException();
            se.initCause(kse);
            throw se;
        }
        catch (UnrecoverableKeyException uke) {
            SecurityException se = new SecurityException();
            se.initCause(uke);
            throw se;
        }

        if (privateKey == null) {
            throw new SecurityException("Invalid name: " + userName); 
        }

        // Check the certificats
        Certificate certs[] = null;
        try {
            certs = keyStore.getCertificateChain(userName);
        }
        catch (KeyStoreException kse) {
            SecurityException se = new SecurityException();
            se.initCause(kse);
            throw se;
        }

        if (debug) {
            System.out.println("  Print out the certificates:");
            for (int i = 0; i < certs.length; i++) {
                System.out.println("  certificate " + i);
                System.out.println("    " + certs[i]);
            }
        }

        if (certs != null && certs.length > 0) {

            // If the first certificate is an X509Certificate
            if (certs[0] instanceof X509Certificate) {
                try {
                    // Get the first certificate which represents the user                     X509Certificate certX509 = (X509Certificate) certs[0];

                    // Create a principal
                    X500Principal principal = new X500Principal(certX509
                            .getIssuerDN()
                            .getName());
                    principals.add(principal);

                    if (debug) {
                        System.out.println("  Principal added: " + principal);
                    }
                    // Create the certification path object and add it to the 
                    // public credential set
                    CertificateFactory factory = CertificateFactory
                        .getInstance("X.509");
                    java.security.cert.CertPath certPath = factory
                        .generateCertPath(Arrays.asList(certs));
                    publicCreds.add(certPath);

                    // Add the private credential to the private credential set
                    privateCreds.add(new X500PrivateCredential(certX509,
                            privateKey, userName));

                }
                catch (CertificateException ce) {
                    SecurityException se = new SecurityException();
                    se.initCause(ce);
                    throw se;
                }
            }
            else {
                // The first certificate is not an X509Certificate
                // We just add the certificate to the public credential set
                // and the private key to the private credential set.
                publicCreds.add(certs[0]);
                privateCreds.add(privateKey);
            }
        }
    }}

Using the LDAP authenticator plug-in

You are provided with the com.ibm.websphere.objectgrid.security.plugins.builtins.LDAPAuthenticator default implementation to handle the user name and password authentication to an LDAP server. This implementation uses the LDAPLogin login module to log the user into a Lightweight Directory Access Protocol (LDAP) server.The following snippet demonstrates how the authenticate method is implemented:

In the following example, some lines of code are continued on the next line for publication purposes.

/**
* @see com.ibm.ws.objectgrid.security.plugins.Authenticator#
* authenticate(LDAPLogin)
*/
public Subject authenticate(Credential credential) throws InvalidCredentialException, ExpiredCredentialException {

    UserPasswordCredential cred = (UserPasswordCredential) credential;
    LoginContext lc = null;
    try {
        lc = new LoginContext("LDAPLogin",
            new UserPasswordCallbackHandlerImpl(cred.getUserName(),
            cred.getPassword().toCharArray()));

        lc.login();

        Subject subject = lc.getSubject();

        return subject;
    }
    catch (LoginException le) {
        throw new InvalidCredentialException(le);
    }
    catch (IllegalArgumentException ile) {
        throw new InvalidCredentialException(ile);
    }}

Also, eXtreme Scale ships a login module com.ibm.websphere.objectgrid.security.plugins.builtins.LDAPLoginModule for this purpose. You must provide the following two options in the JAAS login configuration file.

The LDAPLoginModule module calls the com.ibm.websphere.objectgrid.security.plugins.builtins.LDAPAuthentcationHelper.authenticate method. The following code snippet shows how we can implement the authenticate method of the LDAPAuthenticationHelper.

/**
* Authenticate the user to the LDAP directory.
* @param user the user ID, e.g., uid=xxxxxx,c=us,ou=bluepages,o=ibm.com
* @param pwd the password
*
* @throws NamingException
*/
public String[] authenticate(String user, String pwd)
throws NamingException {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, factoryClass);
    env.put(Context.PROVIDER_URL, providerURL);
    env.put(Context.SECURITY_PRINCIPAL, user);
    env.put(Context.SECURITY_CREDENTIALS, pwd);
    env.put(Context.SECURITY_AUTHENTICATION, "simple");

    InitialContext initialContext = new InitialContext(env);

    // Look up for the user     DirContext dirCtx = (DirContext) initialContext.lookup(user);

    String uid = null;
    int iComma = user.indexOf(",");
    int iEqual = user.indexOf("=");
    if (iComma > 0 && iComma > 0) {
        uid = user.substring(iEqual + 1, iComma);
    }
    else {
        uid = user;
    }

    Attributes attributes = dirCtx.getAttributes("");

    // Check the UID
    String thisUID = (String) (attributes.get(UID).get());

    String thisDept = (String) (attributes.get(HR_DEPT).get());

    if (thisUID.equals(uid)) {
        return new String[] { thisUID, thisDept };
    }
    else {
        return null;
    }}

If authentication succeeds, the ID and password are considered valid. Then the login module gets the ID information and department information from this authenticate method. The login module creates two principals: SimpleUserPrincipal and SimpleDeptPrincipal. Use the authenticated subject for group authorization (in this case, the department is a group) and individual authorization.

The following example shows a login module configuration that is used to log in to the LDAP server:

LDAPLogin { com.ibm.websphere.objectgrid.security.plugins.builtins.LDAPLoginModule required
    providerURL="ldap://directory.acme.com:389/"
    factoryClass="com.sun.jndi.ldap.LdapCtxFactory";};

In the previous configuration, the LDAP server points to the ldap://directory.acme.com:389/server. Change this setting to your LDAP server. This login module uses the provided ID and password to connect to the LDAP server. This implementation is for testing purposes only.

Using the WebSphere Application Server authenticator plug-in

Also, eXtreme Scale provides the com.ibm.websphere.objectgrid.security.plugins.builtins.WSTokenAuthenticator built-in implementation to use the WebSphere Application Server security infrastructure. This built-in implementation can be used when the following conditions are true.

  1. WebSphere Application Server global security is turned on.
  2. All eXtreme Scale clients and servers are launched in WebSphere Application Server JVMs.
  3. These application servers are in the same security domain.
  4. The eXtreme Scale client is already authenticated in WebSphere Application Server.

The client can use the com.ibm.websphere.objectgrid.security.plugins.builtins.WSTokenCredentialGenerator class to generate a credential. The server uses this Authenticator implementation class to authenticate the credential. If the token is authenticated successfully, a Subject object returns.

This scenario takes advantage of the fact that the client has already been authenticated. Because the application servers that have the servers are in the same security domain as the application servers that house the clients, the security tokens can be propagated from the client to the server so that the same user registry does not need to be authenticated again.

Using the Tivoli Access Manager authenticator plug-in

Tivoli Access Manager is used widely as a security server. We can also implement Authenticator using the Tivoli Access Manager's provided login modules.

To authenticate a user for Tivoli Access Manager, apply the com.tivoli.mts.PDLoginModule login module, which requires that the calling application provide the following information:

  1. A principal name, specified as either a short name or an X.500 name (DN)
  2. A password

The login module authenticates the principal and returns the Tivoli Access Manager credential. The login module expects the calling application to provide the following information:

  1. The user name, through a javax.security.auth.callback.NameCallback object.
  2. The password, through a javax.security.auth.callback.PasswordCallback object.

When the Tivoli Access Manager credential is successfully retrieved, the JAAS LoginModule creates a Subject and a PDPrincipal. No built-in for Tivoli Access Manager authentication is provided, because it is just with the PDLoginModule module. See the IBM Tivoli Access Manager Authorization Java Classes Developer Reference for more details.


Connecting to WebSphere eXtreme Scale securely

To connect a WXS client to a server securely, we can use any connect method in the ObjectGridManager interface which takes a ClientSecurityConfiguration object. The following is a brief example.

public ClientClusterContext connect(String catalogServerEndpoints, 
   ClientSecurityConfiguration securityProps,
     URL overRideObjectGridXml) throws ConnectException;

This method takes a parameter of the ClientSecurityConfiguration type, which is an interface representing a client security configuration. Use com.ibm.websphere.objectgrid.security.config.ClientSecurityConfigurationFactory public API to create an instance with default values, or we can create an instance by passing the WebSphere eXtreme Scale client property file. This file contains the following properties that are related to authentication. The value marked with a plus sign (+) is the default.

After you create a com.ibm.websphere.objectgrid.security.config.ClientSecurityConfiguration object, set the credentialGenerator object on the client using the following method:

/**
* Set the {@link CredentialGenerator} object for this client.
* @param generator the CredentialGenerator object associated with this client */
void setCredentialGenerator(CredentialGenerator generator);

We can set the CredentialGenerator object in the WebSphere eXtreme Scale client property file too, as follows.

Here is a sample to instantiate a ClientSecurityConfiguration and then use it to connect to the server.

/**
* Get a secure ClientClusterContext
* @return a secure ClientClusterContext object
*/
protected ClientClusterContext connect() throws ConnectException {
ClientSecurityConfiguration csConfig = ClientSecurityConfigurationFactory
.getClientSecurityConfiguration("/properties/security.ogclient.props");

UserPasswordCredentialGenerator gen= new
UserPasswordCredentialGenerator("manager", "manager1");

csConfig.setCredentialGenerator(gen);

return objectGridManager.connect(csConfig, null);}

When the connect is called, the WebSphere eXtreme Scale client calls the CredentialGenerator.getCredential method to get the client credential. This credential is sent along with the connect request to the server for authentication.


Use a different CredentialGenerator instance per session

In some cases, a WXS client represents just one client identity, but in others, it might represent multiple identities. Here is one scenario for the latter case: An WebSphere eXtreme Scale client is created and shared in a Web server. All servlets in this Web server use this one WebSphere eXtreme Scale client. Because every servlet represents a different Web client, use different credentials when sending requests to WebSphere eXtreme Scale servers.

WebSphere eXtreme Scale provides for changing the credential on the session level. Every session can uses a different CredentialGenerator object. Therefore, the previous scenarios can be implemented by letting the servlet get a session with a different CredentialGenerator object. The following example illustrates the ObjectGrid.getSession(CredentialGenerator) method in the ObjectGridManager interface.

In the following example, some lines of code are continued on the next line for publication purposes.

/**
     * Get a session using a <code>CredentialGenerator</code>.
     * <p>
     * This method can only be called by the ObjectGrid client in an ObjectGrid      * client server environment. If ObjectGrid is used in a local model, that is,
     * within the same JVM with no client or server existing, <code>getSession(Subject)</code>
     * or the <code>SubjectSource</code> plugin should be used to secure the ObjectGrid.
     *
     * <p>If the <code>initialize()</code> method has not been invoked prior to      * the first <code>getSession</code> invocation, an implicit initialization
     * will occur.  This ensures that all of the configuration is complete
     * before any runtime usage is required.</p>
     *
     * @param credGen A <code>CredentialGenerator</code> for generating a credential
     *                for the session returned.
     *
     * @return An instance of <code>Session</code>
     *
     * @throws ObjectGridException if an error occurs during processing
     * @throws TransactionCallbackException if the <code>TransactionCallback</code>
     *         throws an exception
     * @throws IllegalStateException if this method is called after the      *         <code>destroy()</code> method is called.
     *
     * @see #destroy()
     * @see #initialize()
     * @see CredentialGenerator
     * @see Session
     * @since WAS XD 6.0.1
 */
Session getSession(CredentialGenerator credGen) throws ObjectGridException, TransactionCallbackException;

The following is an example:

ObjectGridManager ogManager = ObjectGridManagerFactory.getObjectGridManager();

CredentialGenerator credGenManager = new UserPasswordCredentialGenerator("manager", "xxxxxx");
CredentialGenerator credGenEmployee = new UserPasswordCredentialGenerator("employee", "xxxxxx");

ObjectGrid og = ogManager.getObjectGrid(ctx, "accounting");

// Get a session with CredentialGenerator;
Session session = og.getSession(credGenManager );

// Get the employee map
ObjectMap om = session.getMap("employee");

// start a transaction.
session.begin();

Object rec1 = map.get("xxxxxx");

session.commit();

// Get another session with a different CredentialGenerator;
session = og.getSession(credGenEmployee );

// Get the employee map
om = session.getMap("employee");

// start a transaction.
session.begin();

Object rec2 = map.get("xxxxx");

session.commit();

If you use the ObjectGird.getSession method to get a Session object, the session uses the CredentialGenerator object set on the ClientConfigurationSecurity object. The ObjectGrid.getSession(CredentialGenerator) method overrides the CredentialGenerator set in the ClientSecurityConfiguration object.

If we can reuse the Session object, a performance gain results. However, calling the ObjectGrid.getSession(CredentialGenerator) method is not very expensive. The major overhead is the increased object garbage collection time. Make sure that you release the references after we are done with the Session objects. Generally, if your Session object can share the identity, try to reuse the Session object. If not, use the ObjectGrid.getSession(CredentialGenerator) method.

Credential API


Client authorization programming

WebSphere eXtreme Scale supports Java Authentication and Authorization Service (JAAS) authorization that is ready to use and also supports custom authorization using the ObjectGridAuthorization interface.

The ObjectGridAuthorization plug-in is used to authorize ObjectGrid, ObjectMap, and JavaMap accesses to the Principals represented by a Subject object in a custom way. A typical implementation of this plug-in is to retrieve the Principals from the Subject object, and then check whether the specified permissions are granted to the Principals.

A permission passed to the checkPermission(Subject, Permission) method can be one of the following permissions:

Refer to ObjectGridAuthorization API documentation for more details.


MapPermission

The com.ibm.websphere.objectgrid.security.MapPermission public class represents permissions to the ObjectGrid resources, specifically the methods of ObjectMap or JavaMap interfaces. WebSphere eXtreme Scale defines the following permission strings to access the methods of ObjectMap and JavaMap:

Refer to MapPermission API documentation for more details.

We can construct a MapPermission object by passing the fully qualified ObjectGrid map name (in format [ObjectGrid_name].[ObjectMap_name]) and the permission string or integer value. A permission string can be a comma-delimited string of the previous permission strings such as read, insert, or it can be all. A permission integer value can be any previously mentioned permission integer constants or a mathematical value of several integer permission constants, such as MapPermission.READ|MapPermission.WRITE.

The authorization occurs when an ObjectMap or JavaMap method is called. The run time checks different permissions for different methods. If the required permissions are not granted to the client, an AccessControlException results.

Table 1. List of methods and the required MapPermission
Permission ObjectMap/JavaMap
read Boolean containsKey(Object)
Boolean equals(Object)
Object get(Object)
Object get(Object, Serializable)
List getAll(List)
List getAll(List keyList, Serializable)
List getAllForUpdate(List)
List getAllForUpdate(List, Serializable)
Object getForUpdate(Object)
Object getForUpdate(Object, Serializable)
public Object getNextKey(long)
write Object put(Object key, Object value)
void put(Object, Object, Serializable)
void putAll(Map)
void putAll(Map, Serializable)
void update(Object, Object)
void update(Object, Object, Serializable)
insert public void insert (Object, Object)
void insert(Object, Object, Serializable)
remove Object remove (Object)
void removeAll(Collection)
void clear()
invalidate public void invalidate (Object, Boolean)
void invalidateAll(Collection, Boolean)
void invalidateUsingKeyword(Serializable)
int setTimeToLive(int)

Authorization is based solely on which method is used, rather than what the method really does. For example, a put method can insert or update a record based on whether the record exists. However, the insert or update cases are not distinguished.

Note: The setPutMode(PutMode.UPSERT) method is added to change the default behavior of the ObjectMap and JavaMap put() and putAll() methods to behave like ObjectMap.upsert() and upsertAll() methods.

The PutMode.UPSERT method replaces the setPutMode(PutMode.INSERTUPDATE) method. Use the PutMode.UPSERT method to tell the BackingMap and loader that an entry in the data grid needs to place the key and value into the grid. The BackingMap and loader does either an insert or an update to place the value into the grid and loader. If we run the upsert API within the applications, then the loader gets an UPSERT LogElement type, which allows loaders to do database merge or upsert calls instead of using insert or update.

An operation type can be achieved by combinations of other types. For example, an update can be achieved by a remove and then an insert. Consider these combinations when designing your authorization policies.


ObjectGridPermission

A com.ibm.websphere.objectgrid.security.ObjectGridPermission represents permissions to the ObjectGrid:

Refer to ObjectGridPermission API documentation for more details.

The following table summarizes the methods and the required ObjectGridPermission:

Table 2. List of methods and the required ObjectGridPermission
Permission action Methods

query

com.ibm.websphere.objectgrid.Session.createObjectQuery(String)

query

com.ibm.websphere.objectgrid.em.EntityManager.createQuery(String)

dynamicmap

com.ibm.websphere.objectgrid.Session.getMap(String)


ServerMapPermission

An ServerMapPermission represents permissions to an ObjectMap hosted in a server. The name of the permission is the full name of the ObjectGrid map name. It has the following actions:

Refer to ServerMapPermission API documentation for more details. The detailed methods, which require different ServerMapPermission, are listed in the following table:

Table 3. Permissions to a server-hosted ObjectMap
Permission action Methods

replicate

com.ibm.websphere.objectgrid.ClientReplicableMap.enableClientReplication(Mode, int[], ReplicationMapListener)

dynamicIndex

com.ibm.websphere.objectgrid.BackingMap.createDynamicIndex(String, Boolean, String, DynamicIndexCallback)

dynamicIndex

com.ibm.websphere.objectgrid.BackingMap.removeDynamicIndex(String)


AgentPermission

An AgentPermission represents permissions to the datagrid agents. The name of the permission is the full name of the ObjectGrid map, and the action is a comma-delimited string of agent implementation class names or package names.

Refer to AgentPermission API documentation for more information.

The following methods in the class com.ibm.websphere.objectgrid.datagrid.AgentManager require AgentPermission.

com.ibm.websphere.objectgrid.datagrid.AgentManager#callMapAgent(MapGridAgent, Collection)
com.ibm.websphere.objectgrid.datagrid.AgentManager#callMapAgent(MapGridAgent)
com.ibm.websphere.objectgrid.datagrid.AgentManager#callReduceAgent(ReduceGridAgent, Collection)
com.ibm.websphere.objectgrid.datagrid.AgentManager#callReduceAgent(ReduceGridAgent, Collection)


Authorization mechanisms

WebSphere eXtreme Scale supports two kinds of authorization mechanisms: Java Authentication and Authorization Service (JAAS) authorization and custom authorization. These mechanisms apply to all authorizations. JAAS authorization augments the Java security policies with user-centric access controls. Permissions can be granted based not just on what code is running, but also on who is running it. JAAS authorization is part of the SDK Version 5 and later.

Additionally, WebSphere eXtreme Scale also supports custom authorization with the following plug-in:

We can implement your own authorization mechanism if you do not want to use JAAS authorization. By using a custom authorization mechanism, we can use the policy database, policy server, or Tivoli Access Manager to manage the authorizations.

We can configure the authorization mechanism in two ways:

JAAS authorization

A javax.security.auth.Subject object represents an authenticated user. A Subject consists of a set of principals, and each Principal represents an identity for that user. For example, a Subject can have a name principal, for example, Joe Smith, and a group principal, for example, manager.

Using the JAAS authorization policy, permissions can be granted to specific Principals. WebSphere eXtreme Scale associates the Subject with the current access control context. For each call to the ObjectMap or Javamap method, the Java runtime automatically determines if the policy grants the required permission only to a specific Principal and if so, the operation is allowed only if the Subject associated with the access control context contains the designated Principal.

You must be familiar with the policy syntax of the policy file. For detailed description of JAAS authorization, refer to the JAAS Reference Guide.

WebSphere eXtreme Scale has a special code base used for checking the JAAS authorization to the ObjectMap and JavaMap method calls. This special code base is http://www.ibm.com/com/ibm/ws/objectgrid/security/PrivilegedAction. Use this code base when granting ObjectMap or JavaMap permissions to principals. This special code was created because the JAR file for eXtreme Scale is granted with all permissions.

The template of the policy to grant the MapPermission permission is:

grant codeBase "http://www.ibm.com/com/ibm/ws/objectgrid/security/PrivilegedAction" 
   <Principal field(s)>{
    permission com.ibm.websphere.objectgrid.security.MapPermission 
               "[ObjectGrid_name].[ObjectMap_name]", "action";
    ....
    permission com.ibm.websphere.objectgrid.security.MapPermission 
               "[ObjectGrid_name].[ObjectMap_name]", "action";
  };
A Principal field looks like the following example:
principal Principal_class "principal_name"

In this policy, only insert and read permissions are granted to these four maps to a certain principal. The other policy file, fullAccessAuth.policy, grants all permissions to these maps to a principal. Before running the application, change the principal_name and principal class to appropriate values. The value of the principal_name depends on the user registry. For example, if local OS is used as user registry, the machine name is MACH1, the user ID is user1, and the principal_name is

MACH1/user1.

The JAAS authorization policy can be put directly into the Java policy file, or it can be put in a separate JAAS authorization file and then set in either of two ways:

Custom ObjectGrid authorization

ObjectGridAuthorization plug-in is used to authorize ObjectGrid, ObjectMap, and JavaMap accesses to the Principals represented by a Subject object in a custom way. A typical implementation of this plug-in is to retrieve the Principals from the Subject object, and then check whether or not the specified permissions are granted to the Principals.

A permission passed to the checkPermission(Subject, Permission) method could be one of the following:

Refer to ObjectGridAuthorization API documentation for more details.

The ObjectGridAuthorization plug-in can be configured in the following ways:


Implementing ObjectGridAuthorization

The Boolean checkPermission(Subject subject, Permission permission) method of the ObjectGridAuthorization interface is called by theWebSphere eXtreme Scale run time to check whether the passed-in subject object has the passed-in permission. The implementation of the ObjectGridAuthorization interface returns true if the object has the permission, and false if not.

A typical implementation of this plug-in is to retrieve the principals from the Subject object and check whether the specified permissions are granted to the principals by consulting specific policies. These policies are defined by users. For example, the policies can be defined in a database, a plain file, or a Tivoli Access Manager policy server.

For example, we can use Tivoli Access Manager policy server to manage the authorization policy and use its API to authorize the access. For how to use Tivoli Access Manager Authorization APIs, refer to the IBM Tivoli Access Manager Authorization Java Classes Developer Reference for more details.

This sample implementation has the following assumptions:

The following code snippet demonstrates how to implement the checkPermission method:

/**
* @see com.ibm.websphere.objectgrid.security.plugins.
*   MapAuthorization#checkPermission
* (javax.security.auth.Subject, com.ibm.websphere.objectgrid.security.
*   MapPermission)
*/
public boolean checkPermission(final Subject subject, 
 Permission p) {
  
  // For non-MapPermission, we always authorize.
  if (!(p instanceof MapPermission)){
    return true;
  }

  MapPermission permission = (MapPermission) p;

  String[] str = permission.getParsedNames();
  
  StringBuffer pdPermissionStr = new StringBuffer(5); 
  for (int i=0; i<str.length; i++) {
    pdPermissionStr.append(str[i].substring(0,1));
  }
  
  PDPermission pdPerm = new PDPermission(permission.getName(), 
  pdPermissionStr.toString());
  
  Set principals = subject.getPrincipals();
  
  Iterator iter= principals.iterator(); 
  while(iter.hasNext()) {
    try {
      PDPrincipal principal = (PDPrincipal) iter.next();
      if (principal.implies(pdPerm)) {
        return true;
      }
    }
    catch (ClassCastException cce) {
      // Handle exception
    }
  }
  return false;}

Module 4: Use Java Authentication and Authorization Service (JAAS) authorization in WebSphere Application Server


Local security programming

WebSphere eXtreme Scale provides several security endpoints to allow you to integrate custom mechanisms. In the local programming model, the main security function is authorization, and has no authentication support . Authenticate outside of WebSphere Application Server. However, there are provided plug-ins to obtain and validate Subject objects.


Authentication

In the local programming model, eXtreme Scale does not provide any authentication mechanism, but relies on the environment, either application servers or applications, for authentication. When eXtreme Scale is used in WebSphere Application Server or WebSphere Extended Deployment, applications can use the WebSphere Application Server security authentication mechanism. When eXtreme Scale is running in a Java 2 Platform, Standard Edition (J2SE) environment, the application has to manage authentications with Java Authentication and Authorization Service (JAAS) authentication or other authentication mechanisms. JAAS reference guide . The contract between an application and an ObjectGrid instance is the javax.security.auth.Subject object. After the client is authenticated by the application server or the application, the application can retrieve the authenticated javax.security.auth.Subject object and use this Subject object to get a session from the ObjectGrid instance by calling the ObjectGrid.getSession(Subject) method. This Subject object is used to authorize accesses to the map data. This contract is called a subject passing mechanism. The following example illustrates the ObjectGrid.getSession(Subject) API.

/**
 * This API allows the cache to use a specific subject rather than the one
 * configured on the ObjectGrid to get a session.
 * @param subject
 * @return An instance of Session
 * @throws ObjectGridException
 * @throws TransactionCallbackException
 * @throws InvalidSubjectException the subject passed in is not valid based
 * on the SubjectValidation mechanism.
 */
public Session getSession(Subject subject)
throws ObjectGridException, TransactionCallbackException, InvalidSubjectException;

The ObjectGrid.getSession() method in the ObjectGrid interface can also be used to get a Session object:

/**
 * This method returns a Session object that can be used by a single thread at a time.
 * We cannot share this Session object between threads without placing a  * critical section around it. While the core framework allows the object to move
 * between threads, the TransactionCallback and Loader might prevent this usage,
 * especially in J2EE environments. When security is enabled, this method uses the  * SubjectSource to get a Subject object.
 *
 * If the initialize method has not been invoked prior to the first
 * getSession invocation, then an implicit initialization occurs.  This  * initialization ensures that all of the configuration is complete before
 * any runtime usage is required.
 *
 * @see #initialize()
 * @return An instance of Session
 * @throws ObjectGridException
 * @throws TransactionCallbackException
 * @throws IllegalStateException if this method is called after the  *         destroy() method is called.
 */
public Session getSession()
throws ObjectGridException, TransactionCallbackException;
As the API documentation specifies, when security is enabled, this method uses the SubjectSource plug-in to get a Subject object. The SubjectSource plug-in is one of the security plug-ins defined in eXtreme Scale to support propagating Subject objects. See Security-related plug-ins for more information. The getSession(Subject) method can be called on the local ObjectGrid instance only. If you call the getSession(Subject) method on a client side in a distributed eXtreme Scale configuration, an IllegalStateException results.


Security plug-ins

WebSphere eXtreme Scale provides two security plug-ins that are related to the subject passing mechanism: the SubjectSource and SubjectValidation plug-ins.

SubjectSource plug-in

The SubjectSource plug-in, represented by the com.ibm.websphere.objectgrid.security.plugins.SubjectSource interface, is a plug-in used to get a Subject object from a WXS running environment. This environment can be an application using the ObjectGrid or an application server that hosts the application. Consider the SubjectSource plug-in an alternative to the subject passing mechanism. Using the subject passing mechanism, the application retrieves the Subject object and uses it to get the ObjectGrid session object. With the SubjectSource plug-in, the eXtreme Scale runtime retrieves the Subject object and uses it to get the session object. The subject passing mechanism gives the control of Subject objects to applications, while the SubjectSource plug-in mechanism frees applications from retrieving the Subject object. Use the SubjectSource plug-in to get a Subject object that represents a WXS client used for authorization. When the ObjectGrid.getSession method is called, the Subject getSubject throws an ObjectGridSecurityException if security is enabled. WebSphere eXtreme Scale provides a default implementation of this plug-in: com.ibm.websphere.objectgrid.security.plugins.builtins.WSSubjectSourceImpl. This implementation can be used to retrieve a caller subject or a RunAs subject from the thread when an application is running in WebSphere Application Server. We can configure this class in your ObjectGrid descriptor XML file as the SubjectSource implementation class when using eXtreme Scale in WebSphere Application Server. The following code snippet shows the main flow of the WSSubjectSourceImpl.getSubject method.

Subject s = null;
try {
  if (finalType == RUN_AS_SUBJECT) {
    // get the RunAs subject
    s = com.ibm.websphere.security.auth.WSSubject.getRunAsSubject();
  }
  else if (finalType == CALLER_SUBJECT) {
    // get the callersubject
    s = com.ibm.websphere.security.auth.WSSubject.getCallerSubject();
  }}
catch (WSSecurityException wse) {
  throw new ObjectGridSecurityException(wse);}

return s;
For other details, refer to the API documentation for the SubjectSource plug-in and the WSSubjectSourceImpl implementation.

SubjectValidation plug-in

The SubjectValidation plug-in, which is represented by the com.ibm.websphere.objectgrid.security.plugins.SubjectValidation interface, is another security plug-in. The SubjectValidation plug-in can be used to validate that a javax.security.auth.Subject, either passed to the ObjectGrid or retrieved by the SubjectSource plug-in, is a valid Subject that has not been tampered with.

The SubjectValidation.validateSubject(Subject) method in the SubjectValidation interface takes a Subject object and returns a Subject object. Whether a Subject object is considered valid and which Subject object is returned are all up to your implementations. If the Subject object is not valid, an InvalidSubjectException results.

Use this plug-in if you do not trust the Subject object that is passed to this method. This case is rare considering that you trust the application developers who develop the code to retrieve the Subject object.

An implementation of this plug-in needs support from the Subject object creator because only the creator knows if the Subject object has been tampered with. However, some subject creator might not know if the Subject has been tampered with. In this case, this plug-in is not useful.

WebSphere eXtreme Scale provides a default implementation of SubjectValidation: com.ibm.websphere.objectgrid.security.plugins.builtins.WSSubjectValidationImpl. Use this implementation to validate the WebSphere Application Server-authenticated subject. We can configure this class as the SubjectValidation implementation class when using eXtreme Scale in WebSphere Application Server. The WSSubjectValidationImpl implementation considers a Subject object valid only if the credential token that is associated with this Subject has not been tampered with. We can change other parts of the Subject object. The WSSubjectValidationImpl implementation asks WebSphere Application Server for the original Subject corresponding to the credential token and returns the original Subject object as the validated Subject object. Therefore, the changes made to the Subject contents other than the credential token have no effects. The following code snippet shows the basic flow of the WSSubjectValidationImpl.validateSubject(Subject).

// Create a LoginContext with scheme WSLogin and // pass a Callback handler.
LoginContext lc = new LoginContext("WSLogin",
new WSCredTokenCallbackHandlerImpl(subject));

// When this method is called, the callback handler methods // will be called to log the user in.
lc.login();

// Get the subject from the LoginContext
return lc.getSubject();
In the previous code snippet, a credential token callback handler object, WSCredTokenCallbackHandlerImpl, is created with the Subject object to validate. Then a LoginContext object is created with the login scheme WSLogin. When the lc.login method is called, WebSphere Application Server security retrieves the credential token from the Subject object and then returns the correspondent Subject as the validated Subject object.

For other details, refer to the Java APIs of SubjectValidation and WSSubjectValidationImpl implementation.

Plug-in configuration

We can configure the SubjectValidation plug-in and SubjectSource plug-in in two ways:


Write your own JAAS authentication code

We can write you own Java Authentication and Authorization Service (JAAS) authentication code to handle the authentication. You need to write your own login modules and then configure the login modules for your authentication module.

The login module receives information about a user and authenticates the user. This information can be anything that can identify the user. For example, the information can be a user ID and password, client certificate, and so on. After receiving the information, the login module verifies that the information represents a valid subject and then creates a Subject object. Currently, several implementations of login modules are available to the public.

After a login module is written, configure this login module for the run time to use. Configure a JAAS login module. This login module contains the login module and its authentication scheme. For example:

FileLogin
{
    com.acme.auth.FileLoginModule required};

The authentication scheme is FileLogin and the login module is com.acme.auth.FileLoginModule. The required token indicates that the FileLoginModule module must validate this login or the entire scheme fails.

Setting the JAAS login module configuration file can be done in one of the following ways:

If your code is running in WebSphere Application Server, configure the JAAS login in the administrative console and store this login configuration in the application server configuration. See Login configuration for Java Authentication and Authorization Service for details.


Programming .NET client authentication

To send credentials from the .NET client to the server side, you must implement the ICredentialGenerator and ICredential interfaces. These interfaces generate a credential object that is passed to the data grid and interpreted on the server side. On the server side, the corresponding plug-in interprets the credential object.

To complete authentication, your .NET application must implement the following interfaces:

When a .NET client application connects to a server that requires authentication, the client is required to provide a client credential. A client credential is represented by the ICredential interface. A client credential can be a user name and password pair, a Kerberos ticket, a client certificate, or data in any format that the client and server agree upon. This interface explicitly defines the equals(Object) and hashCode methods. These two methods are important because the authenticated Subject objects are cached using the Credential object as the key on the server side. We can also generate a credential with the ICredentialGenerator interface. This interface is useful when the credential can expire. A new credential is generated whenever the Credential property is obtained.

We can also use the provided CredentialGenerator plugin to create a credential that is based on the credentialGeneratorProps= setting in the Client.Net.Properties file. The additional settings that define the credential plug-in are credentialGeneratorAssembly and credentialGeneratorClass.

Implement the ICredentialGenerator and ICredential interfaces in your .NET client application.

Remember: If the you implement this client credential plug-in, then also implement a corresponding server credential plug-in that can interpret and receive the authentication credentials from the .NET client. Use the following examples to develop the application:


18.6.1. Example: Implementing a user password credential for .NET applications

Use this example to write your own implementation of the ICredential interface. The user password credential stores a user ID and password.


UserPasswordCredential.cs

// Module  :  UserPasswordCrediential.cs

using System;
using IBM.WebSphere.Caching.Security;

namespace com.ibm.websphere.objectgrid.security.plugins.builtins
{
    public class UserPasswordCredential : ICredential
    {
        private String ivUserName;

        private String ivPassword;

        /// <summary>
        ///Creates a UserPasswordCredential with the specified user name and 
        /// password.
        /// 
        /// ArgumentException if userName or password is null
        /// </summary>
        /// <param name="userName">the user name for this credential</param>
        /// <param name="password">the password for this credential</param>
        public UserPasswordCredential(String userName, String password)
        {
            if (userName == null || password == null) {
                throw new ArgumentException("User name and password cannot be null.");
            }
            this.ivUserName = userName;
            this.ivPassword = password;
        }

        /// <summary>Gets the user name for this credential.</summary>
        /// <returns>the user name argument that was passed to the constructor 
        ///or the setUserName(String) method of this class </returns>
        public String GetUserName() {
            return ivUserName;
        }

        /// <summary>Sets the user name for this credential.
        ///ArgumentException if userName is null
        /// </summary>
        /// <param name="userName">userName the user name to set.</param>
        public void SetUserName(String userName) {
            if (userName == null) {
                throw new ArgumentException("User name cannot be null.");
            }
            this.ivUserName = userName;
        }

        /// <summary>Gets the password for this credential.
        /// </summary>
        /// <returns>the password argument that was passed to the constructor or the setPassword(String) method of this class</returns>
        public String GetPassword() {
            return ivPassword;
        }

        /// <summary>Sets the password for this credential.
        ///ArgumentException if password is null
        /// </summary>
        /// <param name="password">the password to set.</param>
        public void SetPassword(String password) {
            if (password == null)
            {
                throw new ArgumentException("Password cannot be null.");
            }
            this.ivPassword = password;
        }

        /// <summary>Checks two UserPasswordCredential objects for equality.
        ///<p>
        /// Two UserPasswordCredential objects are equal if and only if their user names 
        /// and passwords are equal.
        /// </summary>
        /// <param name="o">the object we are testing for equality with this object.</param>
        /// <returns>true if both UserPasswordCredential objects are equivalent.</returns>
        public bool Equals(ICredential credential)
        {
            if (this == credential) {
                return true;
            }
            if (credential is UserPasswordCredential) {
                UserPasswordCredential other = (UserPasswordCredential)credential;
                return other.ivPassword.Equals(ivPassword) && other.ivUserName.Equals(ivUserName);
            }
            return false;
        }

        /// <summary>Returns the hashcode of the UserPasswordCredential object.
        /// </summary>
        /// <returns>return the hash code of this object</returns>
        public override int GetHashCode() {
            int ret = ivUserName.GetHashCode() + ivPassword.GetHashCode();
            return ret;
        }

        /// <summary>this.Object as a string
        /// </summary>
        /// <returns>return the string presentation of the UserPasswordCredential object.</returns>
        public override String ToString() {
            return typeof(UserPasswordCredential).FullName + "[" + ivUserName + ",xxxxxx]";
        }
        
    }}

Programming .NET client authentication

ICredential interface

ICredentialGenerator interface


18.6.2. Example: Implementing a user credential generator for .NET applications

Use this example to write your own implementation of the ICredentialGenerator interface. The interface takes a user ID and a password. The UserPasswordCredential object contains the user ID and password, which is obtained from the read-only Credential property.


UserPasswordCredentialGenerator.cs

// Module  :  UserPasswordCredientialGenerator.cs
//
// Source File Description: Reference Documentation
//
using System;
using System.Security.Authentication;
using IBM.WebSphere.Caching.Security;
using com.ibm.websphere.objectgrid.security.plugins.builtins;

namespace IBM.WebSphere.Caching.Security
{
    public class UserPasswordCredentialGenerator : ICredentialGenerator
    {

        private String ivUser;

        private String ivPwd;

        public ICredential Credential { get { return _getCredential(); } }

        public string Properties { set {_setProperties(value);} }

        public UserPasswordCredentialGenerator()
        {
            ivUser = null;
            ivPwd = null;
        }

        
        public UserPasswordCredentialGenerator(String user=null, String pwd=null)
        {
            ivUser = user;
            ivPwd = pwd;
        }

        /// <summary>Creates a new UserPasswordCredential object using this object's user name and password.
        /// </summary>
        /// <returns>new UserPasswordCredential instance</returns>
        private ICredential _getCredential()
        {
            try
            {
                ICredential MyCredential = new UserPasswordCredential(ivUser, ivPwd) as ICredential;
                return (ICredential) MyCredential;
            }
            catch (Exception e)
            {
                AuthenticationException CannotGenerateCredentialException = new AuthenticationException(e.ToString());
                throw CannotGenerateCredentialException;
            }
        }

        /// <summary>Gets the password for this credential generator.
        /// </summary>
        /// <returns>the password argument that was passed to the constructor</returns>
        public String getPassword()
        {
            return ivPwd;
        }

        /// <summary>Gets the user name for this credential.
        /// </summary>
        /// <returns>the user argument that was passed to the constructor of this class</returns>
        public String getUserName()
        {
            return ivUser;
        }

        /// <summary>Sets additional properties namely a user name and password.
        ///Throws ArgumentException if the format is not valid
        /// </summary>
        /// <param name="properties">properties a properties string  with a user name and a password separated by a blank.</param>
        private void _setProperties(string properties)
        {
            String token = properties;
            char[] Seperator = { ' ' };
            String[] StringProperty = properties.Split(Seperator);
            if (StringProperty.Length != 2)
            {
                throw new ArgumentException(
                    "The properties should have a user name and password and separated by a space.");
            }

            ivUser = StringProperty[0];
            ivPwd = StringProperty[1];
        }

        /// <summary>Checks two UserPasswordCredentialGenerator objects for equality.
        ///<p>
        ///Two UserPasswordCredentialGenerator objects are equal if and only if 
        ///their user names and passwords are equal.
        /// </summary>
        /// <param name="obj">the object we are testing for equality with this object.</param>
        /// <returns><code>true</code> if both UserPasswordCredentialGenerator objects are equivalent</returns>
        public override bool Equals(Object obj)
        {
            if (obj == this)
            {
                return true;
            }

            if (obj != null && obj is UserPasswordCredentialGenerator)
            {
                UserPasswordCredentialGenerator other = (UserPasswordCredentialGenerator)obj;

                Boolean bothUserNull = false;
                Boolean bothPwdNull = false;

                if (ivUser == null)
                {
                    if (other.ivUser == null)
                    {
                        bothUserNull = true;
                    }
                    else
                    {
                        return false;
                    }
                }

                if (ivPwd == null)
                {
                    if (other.ivPwd == null)
                    {
                        bothPwdNull = true;
                    }
                    else
                    {
                        return false;
                    }
                }
                return (bothUserNull || ivUser.Equals(other.ivUser)) && (bothPwdNull || ivPwd.Equals(other.ivPwd));
            }
            return false;
        }

        /// <summary>Returns the hashcode of the UserPasswordCredentialGenerator object.
        /// </summary>
        /// <returns>the hash code of this object</returns>
        public override int GetHashCode()
        {
           return ivUser.GetHashCode() + ivPwd.GetHashCode();
        }

    }}