Skip to content

Instantly share code, notes, and snippets.

@eegrok
Created June 14, 2011 21:27
Show Gist options
  • Select an option

  • Save eegrok/1025934 to your computer and use it in GitHub Desktop.

Select an option

Save eegrok/1025934 to your computer and use it in GitHub Desktop.
setup jconsole to work through firewall, or over vpn or tunneled ssh
//this method should mostly set up a java app to act as a jconsole server, with static ports so you can configure forwarding through a firewall or over a vpn
public static void setupJConsole(MBeanServer mbs, String hostname, Integer port1, Integer port2) {
// from http://download.oracle.com/javase/6/docs/technotes/guides/management/agent.html#gdfvv
// note you need to set the following startup parameters:
// -Dcom.sun.management.jmxremote.authenticate=true
// -Dcom.sun.management.jmxremote.ssl=false
// and this one, if you don't want the rmi server to tell people to connect to the default ip (as is the case if using a vpn ip)
// -Djava.rmi.server.hostname=vpnhostip
// Ensure cryptographically strong random number generator used
// to choose the object number - see java.rmi.server.ObjID
//
// can pass in null for the MBeanServer if you haven't already created one, or use something like this with Spring to get an existing server:
// <!-- The exporter bean must not be lazily initialized if the exporting is to happen. (comment from docs) -->
// <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
// <!-- indicate to first look for a server -->
// <property name="locateExistingServerIfPossible" value="true"/>
// </bean>
System.setProperty("java.rmi.server.randomIDs", "true");
// Start an RMI registry on port port1.
//
System.out.println("Create RMI registry on port " + port1);
try {
LocateRegistry.createRegistry(port1);
} catch (RemoteException except) {
except.printStackTrace();
System.out.println("jconsole failed to start" + except);
return;
}
// Retrieve the PlatformMBeanServer.
//
if (mbs == null) {
mbs = ManagementFactory.getPlatformMBeanServer();
}
// Environment map.
//
HashMap<String,Object> env = new HashMap<String,Object>();
// Provide SSL-based RMI socket factories.
//
// The protocol and cipher suites to be enabled will be the ones
// defined by the default JSSE implementation and only server
// authentication will be required.
//
//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 class used by the connector server to
// perform user authentication.
env.put(JMXConnectorServer.AUTHENTICATOR, new LocalJMXAuthenticator());
// Create an RMI connector server.
//
// As specified in the JMXServiceURL the RMIServer stub will be
// registered in the RMI registry running in the local host on
// port 'port1' with the name "jmxrmi". This is the same name the
// out-of-the-box management agent uses to register the RMIServer
// stub too.
//
JMXServiceURL jmxServiceURL;
try {
jmxServiceURL = new JMXServiceURL("service:jmx:rmi://" + hostname + ":" + port2 + "/jndi/rmi://" + hostname + ":" + port1 +"/jmxrmi");
jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, env, mbs);
// Provide the access class used by the connector server to
// perform user authorization.
MBeanServerForwarder mbsf = MBSFInvocationHandler.newProxyInstance();
jmxConnectorServer.setMBeanServerForwarder(mbsf);
// Start the RMI connector server.
//
jmxConnectorServer.start();
} catch (MalformedURLException except) {
except.printStackTrace();
System.out.println("jconsole failed to start" + except);
} catch (IOException except) {
except.printStackTrace();
System.out.println("jconsole failed to start" + except);
}
}
public static class LocalJMXAuthenticator implements JMXAuthenticator {
public Subject authenticate(Object credentials) {
// Verify that credentials is of type String[].
//
if (!(credentials instanceof String[])) {
// Special case for null so we get a more informative message
if (credentials == null) {
throw new SecurityException("Credentials required");
}
throw new SecurityException("Credentials should be String[]");
}
// Verify that the array contains three elements (username/password).
//
final String[] aCredentials = (String[]) credentials;
if (aCredentials.length != 2) {
throw new SecurityException("Credentials should have 2 elements");
}
// Perform authentication
//
String username = (String) aCredentials[0];
String password = (String) aCredentials[1];
if (username.equals("testadmin") && password.equals("testpass")) {
return new Subject(true,
Collections.singleton(new JMXPrincipal(username)),
Collections.EMPTY_SET,
Collections.EMPTY_SET);
} else {
throw new SecurityException("Invalid credentials");
}
}
}
public static class MBSFInvocationHandler implements InvocationHandler {
@SuppressWarnings("unchecked")
public static MBeanServerForwarder newProxyInstance() {
final InvocationHandler handler = new MBSFInvocationHandler();
final Class[] interfaces =
new Class[] {MBeanServerForwarder.class};
Object proxy = Proxy.newProxyInstance(
MBeanServerForwarder.class.getClassLoader(),
interfaces,
handler);
return MBeanServerForwarder.class.cast(proxy);
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
final String methodName = method.getName();
if (methodName.equals("getMBeanServer")) {
return mbs;
}
if (methodName.equals("setMBeanServer")) {
if (args[0] == null)
throw new IllegalArgumentException("Null MBeanServer");
if (mbs != null)
throw new IllegalArgumentException("MBeanServer object " +
"already initialized");
mbs = (MBeanServer) args[0];
return null;
}
// Retrieve Subject from current AccessControlContext
AccessControlContext acc = AccessController.getContext();
Subject subject = Subject.getSubject(acc);
// Allow operations performed locally on behalf of the connector server itself
if (subject == null) {
return method.invoke(mbs, args);
}
// Retrieve JMXPrincipal from Subject
Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class);
if (principals == null || principals.isEmpty()) {
throw new SecurityException("Access denied");
}
Principal principal = principals.iterator().next();
String identity = principal.getName();
// fullAdmin can do anything
if (identity.equals("testadmin")) {
return method.invoke(mbs, args);
}
// Restrict access to "createMBean" and "unregisterMBean" to any user
if (methodName.equals("createMBean") || methodName.equals("unregisterMBean")) {
throw new SecurityException("Access denied");
}
// this is from http://blogs.oracle.com/lmalventosa/entry/jmx_authentication_authorization
// leaving these example roles in for now, til we figure out what kind of roles / permissions we want
// "role1" can perform any operation other than "createMBean" and "unregisterMBean"
if (identity.equals("role1")) {
return method.invoke(mbs, args);
}
// "role2" can only call "getAttribute" on the MBeanServerDelegate MBean
if (identity.equals("role2") &&
methodName.equals("getAttribute") &&
MBeanServerDelegate.DELEGATE_NAME.equals(args[0])) {
return method.invoke(mbs, args);
}
throw new SecurityException("Access denied");
}
private MBeanServer mbs;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment