Contents | Previous | Next | Java Management Extensions (JMX) Technology Tutorial |
This chapter gives examples of how to set up the JMX technology security features, as described in the following sections:
Caution: Applications should prompt the user to enter passwords rather than expecting the user to provide them on the command line. Use secure authentication mechanisms in production systems.
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.
You can find an example of an RMI connector with
simple security in the directory work_dir/jmx_examples/Security/simple
.
/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:/mbeans
, containing the following
files:/client
, containing the following
files:*.java
and *.properties
files in a text editor
These files will be analyzed in the following sections.
The Server.java
class
is shown in CODE EXAMPLE 5-1.
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
.
The SimpleStandardMBean
class defines the same straightforward MBean interface as was used
in Chapter 3, "JMX
Connectors".
The SimpleStandard
class defines the same straightforward MBean as was used in
Chapter 3, "JMX
Connectors".
The ClientListener
class defines the same straightforward notification listener as was
used in Chapter 3, "JMX
Connectors".
The Client.java
class
is shown in CODE EXAMPLE 5-1.
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.
To run the RMI connector example with simple security, perform the following steps:
$ javac mbeans/SimpleStandard.java \ mbeans/SimpleStandardMBean.java \ server/Server.java \ client/Client.java \ client/ClientListener.java
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.
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.
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.
You can find an example of a secure RMI connector
that implements subject delegation in the directory work_dir/jmx_examples/Security/subject_delegation
.
/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:/mbeans
, containing the following
files:/client
, containing the following
files:*.java
and *.properties
files in a text editor
These files will be analyzed in the following sections.
The Server.java
class
is shown in CODE EXAMPLE 5-1:
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.
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.
The SimpleStandardMBean
class defines the same straightforward MBean interface as was used
in the previous examples.
The SimpleStandard
class defines the same straightforward MBean as was used in the
previous examples.
The ClientListener
class defines the same straightforward notification listener as was
used in the previous examples.
The Client.java
class
is shown in CODE EXAMPLE 5-1:
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.
To run the secure RMI connector example with subject delegation, perform the following steps:
$ javac mbeans/SimpleStandard.java \ mbeans/SimpleStandardMBean.java \ server/Server.java \ client/Client.java \ client/ClientListener.java
$
export CLASSPATH=server ; rmiregistry 9999
&
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.
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.
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.
You can find an example of an RMI connector with
fine-grained security in the directory work_dir/jmx_examples/Security/fine_grained
.
/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:/mbeans
, containing the following
files:/client
, containing the following
files:*.java
and *.properties
files in a text editor.
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.
The java.policy
file
grants the following permissions:
server
code
base, so that the connector server can create the connectors, and
then perform the operations requested by remote user callsMBeanTrustPermission
to the
mbeans
code base, allowing trusted
MBeans to register in the MBean serverJMXPrincipal
named username.
The SimpleStandardMBean
class defines the same straightforward MBean interface as was used
in the previous examples.
The SimpleStandard
class defines the same straightforward MBean as was used in the
previous examples.
The ClientListener
class defines the same straightforward notification listener as as
was used in the previous examples.
The Client.java
class
is exactly the same as the one used in the RMI connector example
with simple security.
To run the RMI connector example with fine-grained security, perform the following steps:
$ javac mbeans/SimpleStandard.java \ mbeans/SimpleStandardMBean.java \ server/Server.java \ client/Client.java \ client/ClientListener.java
$
export CLASSPATH=server ; rmiregistry 9999
&
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.
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.