Java Dynamic Management Kit 5.1 Tutorial

11.1 Simple Security

The simplest type of connector security you can use with the Java DMK is based upon encryption, user name and password authentication, and file access control. The exact implementation of these security features depends on whether you are using an RMI or a JMXMP connector, as explained in the following sections. The examples make use of the secure sockets layer (SSL) RMI socket factories and the SASL classes that are provided with Java DMK.

11.1.1 RMI Connectors With Simple Security

You can find an example of an RMI connector with simple security in the directory examplesDir/current/Security/rmi/simple. The properties files access.properties and password.properties are found in the config directory inside examplesDir/current/Security/rmi/simple.

The RMI connector server is created by the Server class, shown below.


Example 11–1 Creating an RMI Connector Server with Simple Security

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 Example 11–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 JMXAuthenticator interface. 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 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.

Because the connector uses SSL, when you start the Server class, you will have to provide it with a pointer to the SSL keystore file and provide the password expected by the SSL RMI server socket factory ssf. This is shown in Example 11–2 below.

Server creates a 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 connector server named cs.


Example 11–2 Creating an RMI Connector Client with Simple Security

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 Example 11–2 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 operations on the MBean SimpleStandard.

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.

Because the connector uses SSL, when you start the Client class, you will have to provide it with a pointer to the SSL truststore file and provide the password, trustword, that is expected by the SSL RMI server socket factory ssf. This is shown in To Run the RMI Connector Example With Simple Security below.

To Run the RMI Connector Example With Simple Security

Run this example from within the examplesDir/current/Security/rmi/simple directory. Before running the example, make sure your classpath contains the runtime library for SSL RMI (see Directories and Classpath).

  1. Compile the example classes.


    $ javac -classpath classpath \
          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:classpath ; rmiregistry 9999 &
    
  3. Start the Server.

    You have to provide the SSL keystore and its password when you start the Server


    $ java -classpath server:mbeans:classpath \
         -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.

    The Client requires the SSL truststore and its password when it is launched.


    $ java -classpath client:server:mbeans:classpath \
         -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 and finally 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 9, Protocol 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.

11.1.2 JMXMP Connectors With Simple Security

You can find an example of a JMXMP connector with simple security in the directory examplesDir/current/Security/jmxmp/simple. JMXMP connectors can be secured using Java DMK's own implementation of the Simple Authentication and Security Layer (SASL), which provides a greater level of security than the SSL security implemented by the RMI connector.


Example 11–3 Creating a JMXMP Connector Server with Simple Security

public class Server { 
 
    public static void main(String[] args) { 
      try { 
           MBeanServer mbs = MBeanServerFactory.createMBeanServer(); 
           HashMap env = new HashMap(); 
          	     
           Security.addProvider(
                 new com.sun.jdmk.security.sasl.Provider()); 
           env.put("jmx.remote.profiles", "TLS SASL/PLAIN"); 
           env.put("jmx.remote.sasl.callback.handler", 
               new PropertiesFileCallbackHandler("config" +  
                                               File.separator +  
                                               "password.properties")); 
           env.put("jmx.remote.x.access.file", 
                 "config" + File.separator + "access.properties"); 
 
           JMXServiceURL url = new JMXServiceURL("jmxmp", null, 5555); 
           JMXConnectorServer cs = 
              JMXConnectorServerFactory.newJMXConnectorServer(url, 
                                                              env, 
                                                              mbs); 
           cs.start(); 
 
         } catch (Exception e) { 
           e.printStackTrace(); 
         } 
      } 
  } 
 

Example 11–3 shows the creation of an MBean server mbs, and an environment map env. A security provider is defined by a call to the addProvider() method of the Security class, implementing server support for the PLAIN SASL mechanism to Java DMK.

The environment map env is populated with the TLS and PLAIN SASL profiles. A callback handler, an instance of the PropertiesFileCallbackHandler class, is also passed to the environment map. The PropertiesFileCallbackHandler class is an implementation of the CallbackHandler interface, and is used by Server to check that the user name and password supplied by the client against the user name and password supplied in the password.properties file.

Finally, the access properties file, access.properties, is passed into the environment map to define the names of the users who will be authorized to access the connector server, and their level of access.

When the security provider and the environment map have been configured, a service URL, named url, is created. This URL is defined by the constructor JMXServiceURL, with its three arguments specifying JMXMP as the connector protocol, a null host name, and port number 5555 as the port upon which the connector server will listen for connections. The URL is then provided, along with the environment map env, to create an instance of JMXConnectorServer, named cs. The connector server cs is started by calling the start() method of JMXConnectorServer.


Example 11–4 Creating a JMXMP Connector Client with Simple Security

public class Client { 
 
   public static void main(String[] args) { 
     try { 
          HashMap env = new HashMap(); 
          Security.addProvider(new com.sun.security.sasl.Provider()); 
          env.put("jmx.remote.profiles", "TLS SASL/PLAIN"); 
          env.put("jmx.remote.sasl.callback.handler", 
             new UserPasswordCallbackHandler("username", "password")); 

          JMXServiceURL url = new JMXServiceURL("jmxmp", null, 5555); 
          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(); 
        } 
     } 
 } 

In Example 11–4, we see the creation of an environment map env, and a security provider is defined by a call to the addProvider() method of the Security class, implementing the client support for the PLAIN SASL mechanism.

A callback handler, an instance of the UserPasswordCallbackHandler class, is passed to the environment map for use by the PLAIN SASL client mechanism to retrieve the user name and password of the remote user connecting to the server.

Once the security provider and the environment map have been configured, a service URL named url is created. This URL defines JMXMP as the connector protocol, and port number 5555 as the port upon which the connector server will make connections. The URL is then provided, along with the environment map env, to create an instance of JMXConnector, named jmxc. An MBean server connection, mbsc, is made by calling the getMBeanServerConnection of the connector jmxc.

In code that is not shown here, when the secure connection to the MBean server has been established, the Client creates an MBean called SimpleStandard and performs various operations on it. Once these MBean operations have completed, the Client unregisters the MBean, and closes down the connection jmxc.

To Run the JMXMP Connector Example with Simple Security

Run this example from within the examplesDir/current/Security/jmxmp/simple directory. Before running the example, make sure your classpath contains the runtime library for SASL (see Directories and Classpath).

  1. Compile the example classes.


    $ javac -classpath classpath \
          mbeans/SimpleStandard.java \
          mbeans/SimpleStandardMBean.java \
          server/Server.java \
          server/PropertiesFileCallbackHandler.java \
          client/Client.java \
          client/ClientListener.java \
          client/UserPasswordCallbackHandler.java
    
  2. Start the Server:

    The Server requires the SSL keystore file and its password when you launch it.


    $ java -classpath server:mbeans:classpath \
         -Djavax.net.ssl.keyStore=config/keystore \
         -Djavax.net.ssl.keyStorePassword=password \
         Server &
    

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

  3. Start the Client:

    The Client requires the SSL truststore and its password when it is launched.


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

    You will see confirmation of the creation of the JMXMP connector client, the initialization of the environment map, the connection to the MBean server and the performance of 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 JMXMP connector example shown in Chapter 9, Protocol Connectors. However, if you were to open access.properties and change the access to readonly rather than readwrite, you would see a java.lang.SecurityException when you launched the Client, and the connection would fail.