Created
June 14, 2011 21:27
-
-
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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //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