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:

- Section 5.1 "Simple Security" presents examples of connectors that implement straightforward security based on password authentication and file access control.
- Section 5.2 "Subject Delegation" presents examples of connectors that use the subject delegation model to perform operations on a given authenticated connection on behalf of several different identities.
- Section 5.3 "Fine-Grained Security" presents examples of connectors that implement more sophisticated security mechanisms, in which permission to perform individual operations is controlled.

## 5.1 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.

### 5.1.1 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 file: `Server.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.

#### 5.1.1.1 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`.

#### 5.1.1.2 SimpleStandardMBean.java

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

#### 5.1.1.3 SimpleStandard.java

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

#### 5.1.1.4 ClientListener.java

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

#### 5.1.1.5 Client.java

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

CODE EXAMPLE 5-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 CODE EXAMPLE 5-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 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.

### 5.1.2 Running the RMI Connector Example With Simple Security

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

1. Compile the example classes.

    $ 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.

## 5.2 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 5.1 "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.

### 5.2.1 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.

#### 5.2.1.1 Server.java

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

CODE EXAMPLE 5-3 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-3 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 password file contains a username and password combination that is used to authenticate users that try to make connections.
- The access file contains a user name and access level combination that is used to authorize access to the MBeans in the MBean server. The access level is either readwrite or readonly.

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

#### 5.2.1.2 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.

#### 5.2.1.3 SimpleStandardMBean.java

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

#### 5.2.1.4 SimpleStandard.java

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

#### 5.2.1.5 ClientListener.java

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

#### 5.2.1.6 Client.java

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

CODE EXAMPLE 5-4 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-4 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.

### 5.2.2 Running the Secure RMI Connector Example With Subject Delegation

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

1. Compile the example classes.

    $ 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.

## 5.3 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 2 platform Standard Edition (J2SE) Security Architecture. JAAS and J2SE 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 5.1 "Simple Security", with the difference being that the simple, file-based access control has been replaced by policy-based access control.

### 5.3.1 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 file `Server.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.

#### 5.3.1.1 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.

#### 5.3.1.2 java.policy

The `java.policy` file grants the following permissions:

- All permissions to the `server` codebase, so that the connector server can create the connectors, and then perform the operations requested by remote user calls
- `MBeanTrustPermission` to the `mbeans` codebase, 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.`

#### 5.3.1.3 SimpleStandardMBean.java

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

#### 5.3.1.4 SimpleStandard.java

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

#### 5.3.1.5 ClientListener.java

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

#### 5.3.1.6 Client.java

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

### 5.3.2 Running the RMI Connector Example With Fine-Grained Security

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

1. Compile the example classes.

    $ 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 | Java Management Extensions (JMX) Technology Tutorial Java Management Extensions (JMX), Java 2 Platform, Standard Edition 5.0

Copyright © 2004 Sun Microsystems, Inc. All rights reserved.