1

I am new to JMX and am trying out JMX for remote monitoring and followed the oracle documentation on Monitoring and Management Using JMX Technology and am trying out the samples from the post Authentication and Authorization in JMX RMI connectors. I could create the Hello World MBean example with password and access files and now I want to implement the same with a custom Forwarder for authorization (use case 2 in 2).

I wrote the java code as shown but when I log into jConsole with monitorRole (readOnly) credentials it gives a connection failed error but works perfect for the controlRole (readWrite).

Following is the piece code for authorization from custom forwarder. Please let me know what changes needs to be done in the code for monitorRole authorization.

// "role1" can perform any operation other than "createMBean" and "unregisterMBean"
            if (identity.equals("controlRole")) {
                return method.invoke(mbs, args);
            }

// "role2" can only call "getAttribute" on the MBeanServerDelegate MBean
            if (identity.equals("monitorRole") &&
                    methodName.equals("getAttribute") &&
                    MBeanServerDelegate.DELEGATE_NAME.equals(args[0])) {
                return method.invoke(mbs, args);
            }

The complete code is is as follows:

public class Agent {

public static class MBSFInvocationHandler implements InvocationHandler {

    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);
        }

        // Restrict access to "createMBean" and "unregisterMBean" to any user
        if (methodName.equals("createMBean") || methodName.equals("unregisterMBean")) {
            throw new SecurityException("Access denied");
        }

        // 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();

        // "role1" can perform any operation other than "createMBean" and "unregisterMBean"
        if (identity.equals("controlRole")) {
            return method.invoke(mbs, args);
        }

        // "role2" can only call "getAttribute" on the MBeanServerDelegate MBean
        if (identity.equals("monitorRole") &&
                methodName.equals("getAttribute") &&
                MBeanServerDelegate.DELEGATE_NAME.equals(args[0])) {
            return method.invoke(mbs, args);
        }

        throw new SecurityException("Access denied");
    }

    private MBeanServer mbs;
}


public static void main(String[] args) throws Exception {

    // Ensure cryptographically strong random number generator used
    // to choose the object number - see java.rmi.server.ObjID
    //
    System.setProperty("java.rmi.server.randomIDs", "true");

    // Start an RMI registry on port 3000.
    //
    System.out.println("Create RMI registry on port 3000");
    LocateRegistry.createRegistry(3000);

    // Retrieve the PlatformMBeanServer.
    //
    System.out.println("Get the platform's MBean server");
    MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

//   --------------------------------------------------------------------------
    // Construct the ObjectName for the Hello MBean we will register
    ObjectName mbeanName = new ObjectName("com.example:type=Hello");

    // Create the Hello World MBean
    Hello mbean = new Hello();

    // Register the Hello World MBean
    mbs.registerMBean(mbean, mbeanName);


 //------------------------------------------------------------------------------


// Environment map.

    System.out.println("Initialize the 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 file used by the connector server to
    // perform user authentication. The password file is a properties
    // based text file specifying username/password pairs.
    //
//        env.put(JMXConnectorServer.AUTHENTICATOR, new RealmJMXAuthenticator());

    // 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.
    //
    env.put("jmx.remote.x.password.file", "jmxremote.password");

    // 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 3000 with the name "jmxrmi". This is the same name the
    // out-of-the-box management agent uses to register the RMIServer
    // stub too.
    //
    System.out.println("Create an RMI connector server");
    JMXServiceURL url =
            new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:3000/jmxrmi");
    JMXConnectorServer cs =
            JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);

    MBeanServerForwarder mbsf = MBSFInvocationHandler.newProxyInstance();
    cs.setMBeanServerForwarder(mbsf);

    // Start the RMI connector server.
    //
    System.out.println("Start the RMI connector server");
    cs.start();
}

}

Asma Zinneera Jabir
  • 801
  • 3
  • 13
  • 31
  • Can you paste the "Connection refused" stack trace? – Raffaele Apr 16 '17 at 08:57
  • Can I know where I can find the stack trace? It doesn't log in the terminal and the command I use to run jconsole is: jconsole -J-Djavax.net.ssl.trustStore=truststore -J-Djavax.net.ssl.trustStorePassword=admin123. I only get the connection refused error in a message box in jConsole. – Asma Zinneera Jabir Apr 16 '17 at 09:34
  • That link has been removed from the Oracle website :( – Adam Jan 11 '18 at 14:18
  • Run jconsole with the `-debug` option. Did you resolve this successfully? – Adam Jan 11 '18 at 14:44

0 Answers0