Skip navigation bar
Contents | Previous | Next Java Management Extensions (JMX) Technology Tutorial

Chapter   5

Security

This chapter gives examples of how to set up the JMX technology security features, as described in the following sections:

Caution:

Simple Security

The simplest type of security you can use with the JMX technology is based upon encryption, user name and password authentication, and file access control.

RMI Connectors With Simple Security

You can find an example of an RMI connector with simple security in the directory work_dir/jmx_examples/Security/simple.

  1. Open the work_dir/jmx_examples/Security/simple directory.

    Inside this directory you will find the following directories:

    • /server, containing the fileServer.java
    • /config, containing the security configuration files:
    • access.properties
    • keystore
    • password.properties
    • truststore
    • /mbeans, containing the following files:
    • SimpleStandardMBean.java
    • SimpleStandard.java
    • /client, containing the following files:
    • Client.java
    • ClientListener.java
  2. Open all the *.java and *.properties files in a text editor

    These files will be analyzed in the following sections.

Server.java

The Server.java class is shown in CODE EXAMPLE 5-1.

CODE EXAMPLE 5-1 RMI Connector Example (Simple Security) Class Server.java
 
public class Server { 
 
  public static void main(String[] args) { 
  try { 
       MBeanServer mbs = MBeanServerFactory.createMBeanServer(); 
 
       HashMap env = new HashMap(); 
 
       SslRMIClientSocketFactory csf =  
                  new SslRMIClientSocketFactory(); 
       SslRMIServerSocketFactory ssf =  
                  new SslRMIServerSocketFactory(); 
       env.put(RMIConnectorServer. 
                  RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,csf); 
       env.put(RMIConnectorServer. 
                  RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,ssf); 
 
       env.put("jmx.remote.x.password.file", 
                 "config" + File.separator + "password.properties"); 
       env.put("jmx.remote.x.access.file", 
                 "config" + File.separator + "access.properties"); 
 
       JMXServiceURL url = new JMXServiceURL( 
        "service:jmx:rmi:///jndi/rmi://localhost:9999/server"); 
         JMXConnectorServer cs = 
            JMXConnectorServerFactory.newJMXConnectorServer(url,  
                                                            env,  
                                                            mbs); 
       cs.start(); 
     } catch (Exception e) { 
       e.printStackTrace(); 
     } 
  } 
} 
 

The Server class shown in CODE EXAMPLE 5-1 creates an MBean server mbs, and populates an environment map env with a secure RMI client socket factory csf, a secure RMI server socket factory ssf, and the properties files password.properties and access.properties.

The properties file password.properties contains a username and password and is accessed using the JMX Remote API interface JMXAuthenticator. Using the property jmx.remote.x.password.file is the same as creating a password-based JMXAuthenticator and passing it into the environment map through the jmx.remote.authenticator property.

The properties file access.properties contains a username and a level of access permission that can be either readwrite or readonly. This represents the level of access this user can have to MBean server operations. This file-based access control is implemented using the JMX technology interface MBeanServerForwarder, which wraps the real MBean server inside an access controller MBean server. The access controller MBean server only forwards requests to the real MBean server after performing the appropriate checks.

Server creates a JMX service URL, named url, for an RMI connector that will operate over the default JRMP transport, and register an RMI connector stub in an RMI registry on port 9999 of the local host.

The MBean server mbs, the environment map env and the service URL url are all passed to JMXConnectorServer to create a new, secure JMX connector server named cs.

SimpleStandardMBean.java

The SimpleStandardMBean class defines the same straightforward MBean interface as was used in Chapter 3, "JMX Connectors".

SimpleStandard.java

The SimpleStandard class defines the same straightforward MBean as was used in Chapter 3, "JMX Connectors".

ClientListener.java

The ClientListener class defines the same straightforward notification listener as was used in Chapter 3, "JMX Connectors".

Client.java

The Client.java class is shown in CODE EXAMPLE 5-1.

CODE EXAMPLE 5-1 RMI Connector Example (Simple Security) Class Client.java
 
public class Client { 
 
  public static void main(String[] args) { 
  try { 
      HashMap env = new HashMap(); 
 
      String[] credentials = new String[] { "username" , "password" }; 
      env.put("jmx.remote.credentials", credentials); 
      JMXServiceURL url = new JMXServiceURL( 
         "service:jmx:rmi:///jndi/rmi://localhost:9999/server");       
      JMXConnector jmxc = JMXConnectorFactory.connect(url, env); 
      MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); 
      String domains[] = mbsc.getDomains(); 
      for (int i = 0; i < domains.length; i++) { 
         System.out.println("Domain[" + i + "] = " + domains[i]); 
      } 
       
      ObjectName mbeanName =  
          new ObjectName("MBeans:type=SimpleStandard"); 
      mbsc.createMBean("SimpleStandard", mbeanName, null, null); 
      // Perform MBean operations 
      [...] 
      
      mbsc.removeNotificationListener(mbeanName, listener); 
      mbsc.unregisterMBean(mbeanName); 
      jmxc.close(); 
    }  catch (Exception e) { 
      e.printStackTrace(); 
    } 
  } 
} 
 

The Client class shown in CODE EXAMPLE 5-1 populates an environment map env with a set of credentials, namely the username and password expected by the Server. These credentials are then given to an instance of JMXConnector named jmxc when the service URL of the connector stub and the environment map are passed to JMXConnectorFactory.connect(). Through jmxc, the Client connects to the MBean server started by Server, and performs MBean operations.

When the connection is established, the credentials supplied in the environment map env are sent to the server. The server then calls the authenticate() method of the JMXAuthenticator interface, passing the client credentials as parameters. The authenticate() method authenticates the client and returns a subject that contains the set of principals upon which the access control checks will be performed.

Running the RMI Connector Example With Simple Security

To run the RMI connector example with simple security, perform the following steps:

  1. Run the RMI connector example:

    $ javac 
          mbeans/SimpleStandard.java \ 
          mbeans/SimpleStandardMBean.java \ 
          server/Server.java \ 
          client/Client.java \ 
          client/ClientListener.java 
     
    
  2. Start an RMI registry on port 9999 of the local host.
     
    $ export CLASSPATH=server ; rmiregistry 9999 & 
     
    
  3. Start the Server.
     
    $ java -classpath server:mbeans \ 
         -Djavax.net.ssl.keyStore=config/keystore \ 
         -Djavax.net.ssl.keyStorePassword=password \ 
         Server & 
    

    You will see confirmation of the creation of the MBean server and of the RMI connector.

  4. Start the Client.
     
    $java -classpath client:server:mbeans \ 
         -Djavax.net.ssl.trustStore=config/truststore \ 
         -Djavax.net.ssl.trustStorePassword=trustword \ 
         Client 
     
    

    You will see confirmation of the creation of the connector client, the various MBean operations followed by the closure of the connection.

As you can see, all the above appears to proceed in exactly the same manner as the basic RMI connector example shown in Chapter 3, "JMX Connectors". However, if you were to open password.properties and change the password, you would see a java.lang.SecurityException when you launched the Client, and the connection would fail.

Subject Delegation

If your implementation requires the client end of the connection to perform different operations on behalf of multiple users or applications, using the security mechanisms demonstrated in Section  "Simple Security", each different user would require one secure connection for every operation it performs. If you expect your connector clients to interact with numerous users, you can reduce the load on your system by implementing subject delegation. Subject delegation establishes a single secure connection for a user, and this connection can be used to perform related operations on behalf of any number of users. The connection itself is made by an authenticated user. If the authenticated user has been granted a SubjectDelegationPermission that allows it to act on behalf of another user, then operations can be performed over the connection on behalf of that user.

Secure RMI Connectors With Subject Delegation

You can find an example of a secure RMI connector that implements subject delegation in the directory work_dir/jmx_examples/Security/subject_delegation.

  1. Open the work_dir/jmx_examples/Security/subject_delegation directory

    Inside this directory you will find the following directories:

    • /server, containing the file Server.java:
    • /config, containing the security configuration files:
    • access.properties
    • java.policy
    • password.properties
    • /mbeans, containing the following files:
    • SimpleStandardMBean.java
    • SimpleStandard.java
    • /client, containing the following files:
    • Client.java
    • ClientListener.java
  2. Open all the *.java and *.properties files in a text editor

    These files will be analyzed in the following sections.

Server.java

The Server.java class is shown in CODE EXAMPLE 5-1:

CODE EXAMPLE 5-1 Secure RMI Connector (Subject Delegation) Example Class Server.java
 
public class Server { 
 
   public static void main(String[] args) { 
     try { 
          MBeanServer mbs = MBeanServerFactory.createMBeanServer(); 
          HashMap env = new HashMap(); 
          env.put("jmx.remote.x.password.file", 
                  "config" + File.separator + "password.properties"); 
          env.put("jmx.remote.x.access.file", 
                  "config" + File.separator + "access.properties"); 
 
          JMXServiceURL url = new JMXServiceURL( 
             "service:jmx:rmi:///jndi/rmi://localhost:9999/server"); 
          JMXConnectorServer cs = 
              JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); 
          cs.start(); 
     } catch (Exception e) { 
          e.printStackTrace(); 
     } 
   } 
} 
 

CODE EXAMPLE 5-1 begins with the creation of an MBean server mbs, and the population of an environment map env with a password file and an access file, called password.properties and access.properties respectively:

The Server then creates a connector server named cs, and starts it in exactly the same way as in the previous RMI connector examples.

java.policy

The java.policy file grants to username a SubjectDelegationPermission so it can perform operations on behalf of the user delegate, an instance of JMXPrincipal created by the Client class. The java.policy file is required when launching the Server class.

SimpleStandardMBean.java

The SimpleStandardMBean class defines the same straightforward MBean interface as was used in the previous examples.

SimpleStandard.java

The SimpleStandard class defines the same straightforward MBean as was used in the previous examples.

ClientListener.java

The ClientListener class defines the same straightforward notification listener as was used in the previous examples.

Client.java

The Client.java class is shown in CODE EXAMPLE 5-1:

CODE EXAMPLE 5-1 Secure RMI Connector (Subject Delegation) Example Class Client.java
 
public class Client { 
 
  public static void main(String[] args) { 
    try { 
         HashMap env = new HashMap(); 
         String[] credentials = new String[] { "username" , "password" }; 
         env.put("jmx.remote.credentials", credentials); 
         JMXServiceURL url = new JMXServiceURL( 
            "service:jmx:rmi:///jndi/rmi://localhost:9999/server"); 
         JMXConnector jmxc = JMXConnectorFactory.connect(url, env); 
         Subject delegationSubject = 
            new Subject(true, 
                Collections.singleton(new JMXPrincipal("delegate")), 
                Collections.EMPTY_SET, 
                Collections.EMPTY_SET); 
 
         MBeanServerConnection mbsc = 
            jmxc.getMBeanServerConnection(delegationSubject); 
         String domains[] = mbsc.getDomains(); 
         ObjectName mbeanName = 
            new ObjectName("MBeans:type=SimpleStandard"); 
         mbsc.createMBean("SimpleStandard", mbeanName, null, null); 
         // Perform MBean operations 
         // 
 
[...] 
         mbsc.removeNotificationListener(mbeanName, listener); 
         mbsc.unregisterMBean(mbeanName); 
         jmxc.close(); 
     } catch (Exception e) { 
         e.printStackTrace(); 
     } 
  } 
} 
 

CODE EXAMPLE 5-1 begins with the creation of an environment map env that is populated with a user name username and a password password. These strings match the user name and password stored in the password.properties file that is held by the Server to authenticate users accessing the connector server.

A JMX technology connector client jmxc is created in the same way as in the previous RMI connector examples, with the user name and password passed into the environment map env.

The Client then creates an instance of Subject, called delegationSubject, with a Principal that is an instance of JMXPrincipal, named delegate.

An MBean server connection, named mbsc, is created by calling the getMBeanServerConnection() method of JMXConnector, with delegationSubject passed in as a parameter. This MBean server connection therefore allows operations to be performed on the remote MBean server on behalf of the principals stored in the delegationSubject, which in this example is the JMXPrincipal named delegate.

The example continues by creating and registering the SimpleStandard MBean in the MBean server, and performing operations on it, in exactly the same way as in the previous examples.

Running the Secure RMI Connector Example With Subject Delegation

To run the secure RMI connector example with subject delegation, perform the following steps:

  1. Run the secure RMI connector example:
    $ javac  mbeans/SimpleStandard.java \ 
      mbeans/SimpleStandardMBean.java \ 
      server/Server.java \ 
      client/Client.java \ 
      client/ClientListener.java 
     
    
  2. Start an RMI registry on port 9999 of the local host.
    $ export CLASSPATH=server ; rmiregistry 9999 &
  3. Start the Server.
    $ java -classpath server:mbeans \ 
      -Djava.security.policy=config/java.policy Server & 
     
    

    You will see confirmation of the creation of the MBean server, the initialization of the environment map, the creation of the RMI connector, and the registration of the connector in the MBean server.

  4. Start the Client.
    $java -classpath client:server:mbeans Client

    You will see confirmation of the creation of the connector client, the creation of the delegation subject, the connection to the MBean server and the various MBean operations followed by the closure of the connection.

Fine-Grained Security

You can implement a more fine-grained level of security in your connectors by managing user access through the Java Authentication and Authorization Service (JAAS) and Java platform Standard Edition (Java SE) Security Architecture. JAAS and Java SE security is based on the use of security managers and policy files to allocate different levels of access to different users. Consequently, you can decide more precisely which users are allowed to perform which operations.

The two examples in this section are very similar to those shown in Section  "Simple Security", with the difference being that the simple, file-based access control has been replaced by policy-based access control.

RMI Connectors With Fine-Grained Security

You can find an example of an RMI connector with fine-grained security in the directory work_dir/jmx_examples/Security/fine_grained.

  1. Open work_dir/jmx_examples/Security/fine_grained.

    Inside this directory you will find the following directories:

    • /server, containing the fileServer.java
    • /config, containing the security configuration files:
    • java.policy
    • keystore
    • password.properties
    • truststore
    • /mbeans, containing the following files:
    • SimpleStandard.java
    • SimpleStandardMBean.java
    • /client, containing the following files:
    • ClientListener.java
    • Client.java
  2. Open all the *.java and *.properties files in a text editor. The files will be analyzed in the following sections.

Server.java

The Server.java class used in this example is very similar to the one used in the RMI connector example with simple security. The only difference is that there is no access.properties file to map into the environment map in the fine-grained example. Otherwise, the two classes are identical.

java.policy

 

The java.policy file grants the following permissions:

SimpleStandardMBean.java

The SimpleStandardMBean class defines the same straightforward MBean interface as was used in the previous examples.

SimpleStandard.java

The SimpleStandard class defines the same straightforward MBean as was used in the previous examples.

ClientListener.java

The ClientListener class defines the same straightforward notification listener as as was used in the previous examples.

Client.java

The Client.java class is exactly the same as the one used in the RMI connector example with simple security.

Running the RMI Connector Example With Fine-Grained Security

To run the RMI connector example with fine-grained security, perform the following steps:

  1. Run the RMI connector example:
    $ javac 
          mbeans/SimpleStandard.java \ 
          mbeans/SimpleStandardMBean.java \ 
          server/Server.java \ 
          client/Client.java \ 
          client/ClientListener.java 
     
    
  2. Start an RMI registry on port 9999 of the local host.
    $ export CLASSPATH=server ; rmiregistry 9999 &
  3. Start the Server.
    $ java -classpath server:mbeans \ 
         -Djavax.net.ssl.keyStore=config/keystore \ 
         -Djavax.net.ssl.keyStorePassword=password \ 
         -Djava.security.manager \ 
         -Djava.security.policy=config/java.policy \ 
         Server & 
     
    

    You will see confirmation of the initialization of the environment map, the creation of the MBean server and of the RMI connector.

  4. Start the Client.
     
    $ java -classpath client:server:mbeans \ 
         -Djavax.net.ssl.trustStore=config/truststore \ 
         -Djavax.net.ssl.trustStorePassword=trustword \ 
         Client 
     
    

    You will see confirmation of the creation of the connector client, the connection to the RMI server and the various MBean operations followed by the closure of the connection.


Contents | Previous | Next


Copyright © 1993, 2018, Oracle and/or its affiliates. All rights reserved.