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.
- 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.
- Fine-Grained Security presents examples of connectors that implement more sophisticated security mechanisms, in which permission to perform individual operations is controlled.
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
-
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
-
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:
-
Run the RMI connector example:
$ javac mbeans/SimpleStandard.java \ mbeans/SimpleStandardMBean.java \ server/Server.java \ client/Client.java \ client/ClientListener.java
- Start an RMI registry on port 9999 of the local host.
$ export CLASSPATH=server ; rmiregistry 9999 &
- 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.
- 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.
Subject Delegation
If your implementation requires the client end of the connection to perform different operations on behalf of multiple users or applications, and if you use the security mechanisms demonstrated in Simple Security , then each different user would requires one secure connection for every operation it performs. If you expect your connector clients to interact with numerous users, then you can reduce the load on your system by implementing subject delegation. Subject delegation establishes a single secure connection for a user. 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 granted a SubjectDelegationPermission
that allows it to act on behalf of other users, then operations can be performed over the connection on behalf of that user.
Analyzing the Secure RMI Connectors With Subject Delegation Example Classes
-
Copy the source code contained in the Security with Subject Delegation section and create the following
work_dir/jmx_examples/Security/subject_delegation
subdirectories and corresponding files:/server/Server.java
:/config/access.properties
/config/java.policy
/config/password.properties
/mbeans/SimpleStandardMBean.java
/mbeans/SimpleStandard.java
/client/Client.java
/client/ClientListener.java
-
Open all the
*.java
and*.properties
files in your IDE or text editor.
The following sections contain the analysis of these files.
Server.java in the Subject Delegation Example
The Server.java
class is shown in the following code example.
CODE EXAMPLE 13-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();
}
}
}
This code example 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.
java.policy in the Subject Delegation Example
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 in the Subject Delegation Example
The SimpleStandardMBean
class defines the same straightforward MBean interface used in previous examples.
SimpleStandard.java in the Subject Delegation Example
The SimpleStandard
class defines the same, straightforward MBean used in previous examples.
ClientListener.java in the Subject Delegation Example
The ClientListener
class defines the same, straightforward notification listener used in previous examples.
Client.java in the Subject Delegation Example
The Client.java
class is shown in the following code example.
CODE EXAMPLE 13-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();
}
}
}
This code example 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 code 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:
- Run the secure RMI connector example:
$ javac mbeans/SimpleStandard.java \ mbeans/SimpleStandardMBean.java \ server/Server.java \ client/Client.java \ client/ClientListener.java
- Start an RMI registry on port 9999 of the local host.
$ export CLASSPATH=server ; rmiregistry 9999 &
- 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.
- 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
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
-
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
- 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 Server.java in the Subject Delegation Example. 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 thembeans
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
namedusername.
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 Client.java in the Subject Delegation Example .
Running the RMI Connector Example With Fine-Grained Security
To run the RMI connector example with fine-grained security, perform the following steps:
- Run the RMI connector example:
$ javac mbeans/SimpleStandard.java \ mbeans/SimpleStandardMBean.java \ server/Server.java \ client/Client.java \ client/ClientListener.java
- Start an RMI registry on port 9999 of the local host.
$ export CLASSPATH=server ; rmiregistry 9999 &
- 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.
- 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.