22 Simple Security
The JMX API existing security protocols to secure your connections. This example
provides a simple security implementation. The source code contained in this section is
used to create corresponding files in the examples/
directory specified
in the appropriate setup procedure and includes:
-
README file
-
Server
-
Client
-
ClientListener
-
access.properties
-
keystore
-
password.properties
-
truststore
-
SimpleStandard
-
SimpleStandardMBean
examples/Security/simple/README
# ==============================================================================
#
# Example of a secure RMI connector.
#
# This example uses:
#
# - the RMI SSL socket factories for encryption,
# - the password authenticator based on the JMXAuthenticator interface for
# user authentication,
# - the file access controller based on the MBeanServerForwarder interface
# for user access level authorization.
#
# ==============================================================================
#
# In order to compile and run the example, make a copy of this README file, and
# then simply cut and paste all the commands as needed into a terminal window.
#
# This README makes the assumption that you are running under Java SE 6 on Unix,
# you are familiar with the JMX technology, and with the bourne shell or korn
# shell syntax.
#
# All the commands below are defined using Unix korn shell syntax.
#
# If you are not running Unix and korn shell you are expected to be able to
# adapt these commands to your favorite OS and shell environment.
#
# Compile Java classes
#
# * Server.java: creates an MBeanServer and creates and starts a secure RMI
# connector server (JRMP).
#
# * Client.java: creates a secure RMI connector (JRMP), creates and registers
# a Simple standard MBean and performs operations on it.
#
# * ClientListener.java: implements a generic notification listener.
#
# * SimpleStandard.java: implements the Simple standard MBean.
#
# * SimpleStandardMBean.java: the management interface exposed by the Simple
# standard MBean.
#
javac mbeans/SimpleStandard.java \
mbeans/SimpleStandardMBean.java \
server/Server.java \
client/Client.java \
client/ClientListener.java
# Start the RMI registry:
#
export CLASSPATH=server ; rmiregistry 9999 &
# Start the Server:
#
java -classpath server:mbeans \
-Djavax.net.ssl.keyStore=config/keystore \
-Djavax.net.ssl.keyStorePassword=password \
Server &
# Start the Client:
#
java -classpath client:server:mbeans \
-Djavax.net.ssl.trustStore=config/truststore \
-Djavax.net.ssl.trustStorePassword=trustword \
Client
# ==============================================================================
examples/Security/simple/server/Server.java
import java.io.File;
import java.util.HashMap;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;
public class Server {
public static void main(String[] args) {
try {
// Instantiate the MBean server
//
System.out.println("\nCreate the MBean server");
MBeanServer mbs = MBeanServerFactory.createMBeanServer();
// Environment map
//
System.out.println("\nInitialize the environment map");
HashMap env = new HashMap();
// Provide SSL-based RMI socket factories.
//
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);
// Provide the password file used by the connector server to
// perform user authentication. The password file is a properties
// based text file specifying username/password pairs. This
// properties based password authenticator has been implemented
// using the JMXAuthenticator interface and is passed to the
// connector through the "jmx.remote.authenticator" property
// in the map.
//
// This property is implementation-dependent and might not be
// supported by all implementations of the JMX Remote API.
//
env.put("jmx.remote.x.password.file",
"config" + File.separator + "password.properties");
// Provide the access level file used by the connector server to
// perform user authorization. The access level file is a properties
// based text file specifying username/access level pairs where
// access level is either "readonly" or "readwrite" access to the
// MBeanServer operations. This properties based access control
// checker has been implemented using the MBeanServerForwarder
// interface which wraps the real MBean server inside an access
// controller MBean server which performs the access control checks
// before forwarding the requests to the real MBean server.
//
// This property is implementation-dependent and might not be
// supported by all implementations of the JMX Remote API.
//
env.put("jmx.remote.x.access.file",
"config" + File.separator + "access.properties");
// Create an RMI connector server
//
System.out.println("\nCreate an RMI connector server");
JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://localhost:9999/server");
JMXConnectorServer cs =
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
// Start the RMI connector server
//
System.out.println("\nStart the RMI connector server");
cs.start();
System.out.println("\nRMI connector server successfully started");
System.out.println("\nWaiting for incoming connections...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
examples/Security/simple/client/Client.java
import java.util.HashMap;
import javax.management.Attribute;
import javax.management.JMX;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
public class Client {
public static void main(String[] args) {
try {
// Environment map
//
System.out.println("\nInitialize the environment map");
HashMap env = new HashMap();
// Provide the credentials required by the server to successfully
// perform user authentication
//
String[] credentials = new String[] { "username" , "password" };
env.put("jmx.remote.credentials", credentials);
// Create an RMI connector client and
// connect it to the RMI connector server
//
System.out.println("\nCreate an RMI connector client and " +
"connect it to the RMI connector server");
JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://localhost:9999/server");
JMXConnector jmxc = JMXConnectorFactory.connect(url, env);
// Get an MBeanServerConnection
//
System.out.println("\nGet an MBeanServerConnection");
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
// Get domains from MBeanServer
//
System.out.println("\nDomains:");
String domains[] = mbsc.getDomains();
for (int i = 0; i < domains.length; i++) {
System.out.println("\tDomain[" + i + "] = " + domains[i]);
}
// Create SimpleStandard MBean
//
ObjectName mbeanName = new ObjectName("MBeans:type=SimpleStandard");
System.out.println("\nCreate SimpleStandard MBean...");
mbsc.createMBean("SimpleStandard", mbeanName, null, null);
// Get MBean count
//
System.out.println("\nMBean count = " + mbsc.getMBeanCount());
// Get State attribute
//
System.out.println("\nState = " +
mbsc.getAttribute(mbeanName, "State"));
// Set State attribute
//
mbsc.setAttribute(mbeanName,
new Attribute("State", "changed state"));
// Get State attribute
//
// Another way of interacting with a given MBean is through a
// dedicated proxy instead of going directly through the MBean
// server connection
//
SimpleStandardMBean proxy = JMX.newMBeanProxy(
mbsc, mbeanName, SimpleStandardMBean.class);
System.out.println("\nState = " + proxy.getState());
// Add notification listener on SimpleStandard MBean
//
ClientListener listener = new ClientListener();
System.out.println("\nAdd notification listener...");
mbsc.addNotificationListener(mbeanName, listener, null, null);
// Invoke "reset" in SimpleStandard MBean
//
// Calling "reset" makes the SimpleStandard MBean emit a
// notification that will be received by the registered
// ClientListener.
//
System.out.println("\nInvoke reset() in SimpleStandard MBean...");
mbsc.invoke(mbeanName, "reset", null, null);
// Sleep for 2 seconds in order to have time to receive the
// notification before removing the notification listener.
//
System.out.println("\nWaiting for notification...");
Thread.sleep(2000);
// Remove notification listener on SimpleStandard MBean
//
System.out.println("\nRemove notification listener...");
mbsc.removeNotificationListener(mbeanName, listener);
// Unregister SimpleStandard MBean
//
System.out.println("\nUnregister SimpleStandard MBean...");
mbsc.unregisterMBean(mbeanName);
// Close MBeanServer connection
//
System.out.println("\nClose the connection to the server");
jmxc.close();
System.out.println("\nBye! Bye!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
examples/Security/simple/client/ClientListener.java
import javax.management.Notification;
import javax.management.NotificationListener;
public class ClientListener implements NotificationListener {
public void handleNotification(Notification notification, Object handback) {
System.out.println("\nReceived notification: " + notification);
}
}
examples/Security/simple/config/access.properties
# access.properties
# Access control file for Remote JMX API access to MBeanServer resources.
# This file defines the allowed access for different roles.
# The file format for the access file is syntactically the same as the
# Properties file format. The syntax is described in the Javadoc for
# java.util.Properties.load.
# A typical access file has multiple lines, where each line is blank,
# a comment (like this one), or an access control entry.
# An access control entry consists of a role name, and an associated access
# level. The role name is any string that does not itself contain spaces or
# tabs. It corresponds to an entry in the password file. The access level
# is one of the following:
#
# "readonly" grants access to read attributes of MBeans.
# For monitoring, this means that a remote client in this
# role can read measurements but cannot perform any action
# that changes the environment of the running program.
#
# "readwrite" grants access to read and write attributes of MBeans, to
# invoke operations on them, and to create or remove them.
# This access should be only granted to trusted clients,
# since they can potentially interfere with the smooth
# operation of a running program.
# A given role should have at most one entry in this file. If a role has no
# entry, it has no access.
# If multiple entries are found for the same role name, then the last access
# entry is used.
# Access rights granted to the authenticated identity by the RMI connector
# in this example.
#
username readwrite
examples/Security/simple/config/password.properties
# password.properties
# Password file for Remote JMX API authentication. This file defines
# the different roles and their passwords.
# The file format for the password file is syntactically the same as
# the Properties file format. The syntax is described in the Javadoc
# for java.util.Properties.load.
# A typical password file has multiple lines, where each line is blank,
# a comment (like this one), or a password entry.
# A password entry consists of a role name and an associated password.
# The role name is any string that does not itself contain spaces or
# tabs. The password is again any string that does not contain spaces
# or tabs. Note that passwords appear in the clear in this file, so it
# is a good idea not to use valuable passwords.
# A given role should have at most one entry in this file. If a role
# has no entry, it has no access.
# If multiple entries are found for the same role name, then the last
# one is used.
# In a typical installation, this file can be read by anybody on the
# local machine, and possibly by people on other machines.
# For security, you should either restrict the access to this file,
# or specify another, less accessible file in the management config
# file as described above.
# Role and password used for authentication by the RMI connector in
# this example.
#
username password
examples/Security/simple/mbeans/SimpleStandard.java
/**
* Simple definition of a standard MBean, named "SimpleStandard".
*
* The "SimpleStandard" standard MBean shows how to expose attributes
* and operations for management by implementing its corresponding
* "SimpleStandardMBean" management interface.
*
* This MBean has two attributes and one operation exposed
* for management by a JMX agent:
* - the read/write "State" attribute,
* - the read only "NbChanges" attribute,
* - the "reset()" operation.
*
* This object also has one property and one method not exposed
* for management by a JMX agent:
* - the "NbResets" property,
* - the "getNbResets()" method.
*/
import javax.management.AttributeChangeNotification;
import javax.management.MBeanNotificationInfo;
import javax.management.NotificationBroadcasterSupport;
public class SimpleStandard
extends NotificationBroadcasterSupport
implements SimpleStandardMBean {
/*
* -----------------------------------------------------
* CONSTRUCTORS
* -----------------------------------------------------
*/
/* "SimpleStandard" does not provide any specific constructors.
* However, "SimpleStandard" is JMX compliant with regards to
* contructors because the default contructor SimpleStandard()
* provided by the Java compiler is public.
*/
/*
* -----------------------------------------------------
* IMPLEMENTATION OF THE SimpleStandardMBean INTERFACE
* -----------------------------------------------------
*/
/**
* Getter: get the "State" attribute of the "SimpleStandard" standard MBean.
*
* @return the current value of the "State" attribute.
*/
public String getState() {
return state;
}
/**
* Setter: set the "State" attribute of the "SimpleStandard" standard MBean.
*
* @param <VAR>s</VAR> the new value of the "State" attribute.
*/
public void setState(String s) {
state = s;
nbChanges++;
}
/**
* Getter: get the "NbChanges" attribute of the "SimpleStandard" standard
* MBean.
*
* @return the current value of the "NbChanges" attribute.
*/
public int getNbChanges() {
return nbChanges;
}
/**
* Operation: reset to their initial values the "State" and "NbChanges"
* attributes of the "SimpleStandard" standard MBean.
*/
public void reset() {
AttributeChangeNotification acn =
new AttributeChangeNotification(this,
0,
0,
"NbChanges reset",
"NbChanges",
"Integer",
new Integer(nbChanges),
new Integer(0));
state = "initial state";
nbChanges = 0;
nbResets++;
sendNotification(acn);
}
/*
* -----------------------------------------------------
* METHOD NOT EXPOSED FOR MANAGEMENT BY A JMX AGENT
* -----------------------------------------------------
*/
/**
* Return the "NbResets" property.
* This method is not a Getter in the JMX sense because it
* is not exposed in the "SimpleStandardMBean" interface.
*
* @return the current value of the "NbResets" property.
*/
public int getNbResets() {
return nbResets;
}
/**
* Returns an array indicating, for each notification this MBean
* may send, the name of the Java class of the notification and
* the notification type.</p>
*
* @return the array of possible notifications.
*/
public MBeanNotificationInfo[] getNotificationInfo() {
return new MBeanNotificationInfo[] {
new MBeanNotificationInfo(
new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE },
AttributeChangeNotification.class.getName(),
"This notification is emitted when the reset() method is called.")
};
}
/*
* -----------------------------------------------------
* ATTRIBUTES ACCESSIBLE FOR MANAGEMENT BY A JMX AGENT
* -----------------------------------------------------
*/
private String state = "initial state";
private int nbChanges = 0;
/*
* -----------------------------------------------------
* PROPERTY NOT ACCESSIBLE FOR MANAGEMENT BY A JMX AGENT
* -----------------------------------------------------
*/
private int nbResets = 0;
}
examples/Security/simple/mbeans/SimpleStandardMBean.java
/**
* This is the management interface explicitly defined for the
* "SimpleStandard" standard MBean.
*
* The "SimpleStandard" standard MBean implements this interface
* in order to be manageable through a JMX agent.
*
* The "SimpleStandardMBean" interface shows how to expose for management:
* - a read/write attribute (named "State") through its getter and setter
* methods,
* - a read-only attribute (named "NbChanges") through its getter method,
* - an operation (named "reset").
*/
public interface SimpleStandardMBean {
/**
* Getter: set the "State" attribute of the "SimpleStandard" standard
* MBean.
*
* @return the current value of the "State" attribute.
*/
public String getState();
/**
* Setter: set the "State" attribute of the "SimpleStandard" standard
* MBean.
*
* @param <VAR>s</VAR> the new value of the "State" attribute.
*/
public void setState(String s);
/**
* Getter: get the "NbChanges" attribute of the "SimpleStandard" standard
* MBean.
*
* @return the current value of the "NbChanges" attribute.
*/
public int getNbChanges();
/**
* Operation: reset to their initial values the "State" and "NbChanges"
* attributes of the "SimpleStandard" standard MBean.
*/
public void reset();
}