Gabriel Duchateau
Gabriel Duchateau

Reputation: 111

How to authenticate with user and password using Custom JMX server using TLS and JMXMP

I am using the following code to create a custom JMX server with TLS and JMXMP following the Oracle documentation. It works well and I can connect to the server with no problem, however I would like to add "USER" and "PASSWORD" to the authentication, however specifying the "password.properties" and the "access.properties" is not working, JMX seems to be ignoring these two options. Can somebody shed some light on the correct way to configure USER and PASSWORD and correct this problem? Thanks

     private JMXServiceURL url() {
        final String url = String.format( "service:jmx:jmxmp://%s:%s", host(), port() );
        try {

           return new JMXServiceURL( url );

        } catch( Throwable exception ) {
           throw new RuntimeException( String.format( "Failed to create JMX Service URL: %s", url ), exception );
        }
     }

     private Map<String, Object> env() {
        final Map<String, Object> env = new LinkedHashMap<String, Object>();


         try {

             String keystore = "jmx.keystore";

             char keystorepass[] = "12345678".toCharArray();
             char keypassword[] = "12345678".toCharArray();

             KeyStore ks = KeyStore.getInstance("JKS");
             ks.load(new FileInputStream(keystore), keystorepass);
             KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");

             kmf.init(ks, keypassword);
             SSLContext ctx = SSLContext.getInstance("TLSv1");
             ctx.init(kmf.getKeyManagers(), null, null);
             SSLSocketFactory ssf = ctx.getSocketFactory();

             env.put("jmx.remote.profiles", "TLS");
             env.put("jmx.remote.tls.socket.factory", ssf);
             env.put("jmx.remote.tls.enabled.protocols", "TLSv1");
             env.put("jmx.remote.tls.enabled.cipher.suites","SSL_RSA_WITH_NULL_MD5");


             env.put("jmx.remote.x.password.file", "password.properties");
             env.put("jmx.remote.x.access.file","access.properties");


         } catch (Exception e) {
             e.printStackTrace();
         }


         return env;
     }

     private MBeanServer server() {
        return ManagementFactory.getPlatformMBeanServer();
     }

     private JMXConnectorServer connector() {
        try {

           ServerProvider.class.getName();
           return JMXConnectorServerFactory.newJMXConnectorServer( url(), env(), server() );

        }catch( Throwable exception ) {
           throw new RuntimeException( "Failed to create JMX connector server factory", exception );
        }
     }

Upvotes: 1

Views: 1542

Answers (1)

Gabriel Duchateau
Gabriel Duchateau

Reputation: 111

I was finally able to configure the additional user and password for the JMXMP connection with the following code from the Oracle documentation

MBeanServer mbs = MBeanServerFactory.createMBeanServer(); 
Security.addProvider(new com.sun.jdmk.security.sasl.Provider()); 

HashMap env = new HashMap(); 
env.put("jmx.remote.profiles", "TLS SASL/PLAIN"); 
env.put("jmx.remote.sasl.callback.handler", 
    new PropertiesFileCallbackHandler("password.properties")); 
env.put("jmx.remote.x.access.file",access.properties"); 

JMXServiceURL url = new JMXServiceURL("jmxmp", null, 5555); 
JMXConnectorServer cs = 
   JMXConnectorServerFactory.newJMXConnectorServer(url, 
                                                   env, 
                                                   mbs); 
cs.start();

I implemented a simple callBackHandler for the password validation

public final class PropertiesFileCallbackHandler
    implements CallbackHandler {

    private Properties pwDb;

    /**
     * Contents of files are in the Properties file format.
     *
     * @param pwFile name of file containing name/password 
     */
    public PropertiesFileCallbackHandler(String pwFile) throws IOException {

        if (pwFile != null) {

            File file = new File(pwFile);

            if(file.exists()) {
                pwDb = new Properties();
                pwDb.load(new FileInputStream(file));
            } else {
                throw new IOException("File " + pwFile + " not found");
            }
        }
    }

    public void handle(Callback[] callbacks) 
        throws UnsupportedCallbackException {

        AuthorizeCallback acb = null;
        AuthenticateCallback   aucb = null;

        for (int i = 0; i < callbacks.length; i++) {    
            if (callbacks[i] instanceof AuthorizeCallback) {
                acb = (AuthorizeCallback) callbacks[i];
            } else if (callbacks[i] instanceof AuthenticateCallback) {
                aucb = (AuthenticateCallback)callbacks[i];
            } else {
                throw new UnsupportedCallbackException(callbacks[i]);
            }
        }

        // Process retrieval of password; can get password if
        // username is available
        if (aucb != null) {
            String username = aucb.getAuthenticationID();
            String password = new String(aucb.getPassword());
            String pw = pwDb.getProperty(username);

            if (pw != null) {
                if(pw.equals(password)){
                    aucb.setAuthenticated(true);
                }
            }
        }

        // Check for authorization
        if (acb != null) {
            String authid = acb.getAuthenticationID();
            String authzid = acb.getAuthorizationID();
            if (authid.equals(authzid)) {
                // Self is always authorized
                acb.setAuthorized(true);
            }
        }
    }
} 

Upvotes: 1

Related Questions