13 Security

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

  • Simple Security presents examples of connectors that implement straightforward security that is based on password authentication and file access control.
  • Fine-Grained Security presents examples of connectors that implement more sophisticated security mechanisms, in which permission to perform individual operations is controlled.

Note:

The Subject Delegation feature has been removed. If your client application needs to perform operations as, or on behalf of, multiple identities, it will need to make multiple calls to JMXConnectorFactory.connect() and to the getMBeanServerConnection() method on the returned JMXConnector.

WARNING:

The Security Manager and APIs related to it have been deprecated and are subject to removal in a future release. There is no replacement for the Security Manager. See JEP 411 for discussion and alternatives.

Caution:

  • Applications should prompt the user to enter passwords rather than expecting the user to provide them at the command line.
  • Use secure authentication mechanisms in production systems. In particular, use both SSL client certificates to authenticate the client host, and password authentication for user management. See Using SSL and Using LDAP Authentication in the Java Platform, Standard Edition Monitoring and Management Guide.

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.

Analyzing the RMI Connectors with Simple Security Example Classes

  1. Copy the source code contained in the Simple Security section and create the following work_dir/jmx_examples/Security/simple subdirectories and corresponding files:

    • /server/Server.java
    • /config/access.properties
    • /config/keystore
    • /config/password.properties
    • /config/truststore
    • /mbeans/SimpleStandardMBean.java
    • /mbeans/SimpleStandard.java
    • /client/Client.java
    • /client/ClientListener.java
  2. Open the *.java and *.properties files, in your IDE or text editor.

The following sections analyze these files and explain how they perform the security operations described above.

Server.java in the Simple Security Example

The Server.java class is shown in the following code example.

CODE EXAMPLE 13-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 this code example 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 in the Simple Security Example

The SimpleStandardMBean class defines the same straightforward MBean interface used in SimpleStandardMBean.java in the MBean Example.

SimpleStandard.java in the Simple Security Example

The SimpleStandard class defines the same straightforward MBean used in SimpleStandard.java in the MBean Example.

ClientListener.java in the Simple Security Example

The ClientListener class defines the same straightforward notification listener used in ClientListener.java in the MBean Example.

Client.java in the Simple Security Example

The Client.java class is shown in the following code example.

CODE EXAMPLE 13-2 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 this code example 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 described in 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.

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. You can specify which users are allowed to perform which operations.

WARNING:

The Security Manager and APIs related to it have been deprecated and are subject to removal in a future release. There is no replacement for the Security Manager. See JEP 411 for discussion and alternatives.

The two examples in this section are very similar to those shown in Simple Security, with the difference that policy-based access control replaces the simple, file-based access control.

Analyzing the Secure RMI Connectors With Fine-Grained Security Example Classes

  1. Copy the source code contained in the Fine-Grained Security section and create the following work_dir/jmx_examples/Security/fine_grained subdirectories and corresponding files:
    • /server/Server.java
    • /config/java.policy
    • /config/keystore
    • /config/password.properties
    • /config/truststore
    • /mbeans/SimpleStandard.java
    • /mbeans/SimpleStandardMBean.java
    • /client/ClientListener.java
    • /client/Client.java
  2. Open all of the *.java and *.properties files in your IDE or text editor.

The following sections contain the analysis of these files.

Server.java in the Fine-Grained Security Example

The Server.java class in this example is very similar to the one used in unresolvable-reference.html. 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 in the Fine-Grained Security Example

The java.policy file grants the following permissions:

  • All permissions to the server code base, so that the connector server can create the connectors, and then perform the operations requested by remote user calls
  • MBeanTrustPermission to the mbeans code base, allowing trusted MBeans to register in the MBean server
  • Permission to perform the various MBean and MBean server operations for the user represented by a JMXPrincipal named username.

SimpleStandardMBean.java in the Fine-Grained Security Example

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

SimpleStandard.java in the Fine-Grained Security Example

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

ClientListener.java in the Fine-Grained Security Example

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

Client.java in the Fine-Grained Security Example

The Client.java class is exactly the same as the one used in unresolvable-reference.html .

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.