Author: markt Date: Sat Mar 14 22:20:06 2015 New Revision: 1666757 URL: http://svn.apache.org/r1666757 Log: Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=57377 Remove the restriction that prevented the use of SSL when specifying a bind address. Enable SSL to be configured for the registry as well as the server.
Modified: tomcat/trunk/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java tomcat/trunk/java/org/apache/catalina/mbeans/LocalStrings.properties tomcat/trunk/webapps/docs/config/listeners.xml Modified: tomcat/trunk/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java?rev=1666757&r1=1666756&r2=1666757&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java (original) +++ tomcat/trunk/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java Sat Mar 14 22:20:06 2015 @@ -25,17 +25,25 @@ import java.net.MalformedURLException; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; +import java.rmi.AlreadyBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; import java.rmi.server.RMIClientSocketFactory; import java.rmi.server.RMIServerSocketFactory; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import java.util.Locale; -import javax.management.MBeanServer; import javax.management.remote.JMXConnectorServer; -import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; import javax.management.remote.rmi.RMIConnectorServer; +import javax.management.remote.rmi.RMIJRMPServerImpl; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; import javax.rmi.ssl.SslRMIClientSocketFactory; import javax.rmi.ssl.SslRMIServerSocketFactory; @@ -55,19 +63,16 @@ import org.apache.tomcat.util.res.String */ public class JmxRemoteLifecycleListener implements LifecycleListener { - private static final Log log = - LogFactory.getLog(JmxRemoteLifecycleListener.class); + private static final Log log = LogFactory.getLog(JmxRemoteLifecycleListener.class); - /** - * The string resources for this package. - */ protected static final StringManager sm = - StringManager.getManager(Constants.Package); + StringManager.getManager(JmxRemoteLifecycleListener.class); protected String rmiBindAddress = null; protected int rmiRegistryPortPlatform = -1; protected int rmiServerPortPlatform = -1; - protected boolean rmiSSL = true; + protected boolean rmiRegistrySSL = true; + protected boolean rmiServerSSL = true; protected String ciphers[] = null; protected String protocols[] = null; protected boolean clientAuth = true; @@ -154,9 +159,13 @@ public class JmxRemoteLifecycleListener // Get all the other parameters required from the standard system // properties. Only need to get the parameters that affect the creation // of the server port. - String rmiSSLValue = System.getProperty( + String rmiRegistrySSLValue = System.getProperty( + "com.sun.management.jmxremote.registry.ssl", "false"); + rmiRegistrySSL = Boolean.parseBoolean(rmiRegistrySSLValue); + + String rmiServerSSLValue = System.getProperty( "com.sun.management.jmxremote.ssl", "true"); - rmiSSL = Boolean.parseBoolean(rmiSSLValue); + rmiServerSSL = Boolean.parseBoolean(rmiServerSSLValue); String protocolsValue = System.getProperty( "com.sun.management.jmxremote.ssl.enabled.protocols"); @@ -171,7 +180,7 @@ public class JmxRemoteLifecycleListener } String clientAuthValue = System.getProperty( - "com.sun.management.jmxremote.ssl.need.client.auth", "true"); + "com.sun.management.jmxremote.ssl.need.client.auth", "true"); clientAuth = Boolean.parseBoolean(clientAuthValue); String authenticateValue = System.getProperty( @@ -204,47 +213,64 @@ public class JmxRemoteLifecycleListener // Create the environment HashMap<String,Object> env = new HashMap<>(); - RMIClientSocketFactory csf = null; - RMIServerSocketFactory ssf = null; + RMIClientSocketFactory registryCsf = null; + RMIServerSocketFactory registrySsf = null; - // Configure SSL for RMI connection if required - if (rmiSSL) { + RMIClientSocketFactory serverCsf = null; + RMIServerSocketFactory serverSsf = null; + + // Configure registry socket factories + if (rmiRegistrySSL) { + registryCsf = new SslRMIClientSocketFactory(); + if (rmiBindAddress == null) { + registrySsf = new SslRMIServerSocketFactory( + ciphers, protocols, clientAuth); + } else { + registrySsf = new SslRmiServerBindSocketFactory( + ciphers, protocols, clientAuth, rmiBindAddress); + } + } else { if (rmiBindAddress != null) { - throw new IllegalStateException(sm.getString( - "jmxRemoteLifecycleListener.sslRmiBindAddress")); + registrySsf = new RmiServerBindSocketFactory(rmiBindAddress); } + } - csf = new SslRMIClientSocketFactory(); - ssf = new SslRMIServerSocketFactory(ciphers, protocols, - clientAuth); + // Configure server socket factories + if (rmiServerSSL) { + serverCsf = new SslRMIClientSocketFactory(); + if (rmiBindAddress == null) { + serverSsf = new SslRMIServerSocketFactory( + ciphers, protocols, clientAuth); + } else { + serverSsf = new SslRmiServerBindSocketFactory( + ciphers, protocols, clientAuth, rmiBindAddress); + } + } else { + if (rmiBindAddress != null) { + serverSsf = new RmiServerBindSocketFactory(rmiBindAddress); + } } - // Force server bind address if required + // By default, the registry will pick an address to listen on. + // Setting this property overrides that and ensures it listens on + // the configured address. if (rmiBindAddress != null) { - try { - ssf = new RmiServerBindSocketFactory( - InetAddress.getByName(rmiBindAddress)); - } catch (UnknownHostException e) { - log.error(sm.getString( - "jmxRemoteLifecycleListener.invalidRmiBindAddress", - rmiBindAddress), e); - } + System.setProperty("java.rmi.server.hostname", rmiBindAddress); } // Force the use of local ports if required if (useLocalPorts) { - csf = new RmiClientLocalhostSocketFactory(csf); + registryCsf = new RmiClientLocalhostSocketFactory(registryCsf); + serverCsf = new RmiClientLocalhostSocketFactory(serverCsf); } // Populate the env properties used to create the server - if (csf != null) { - env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, - csf); - env.put("com.sun.jndi.rmi.factory.socket", csf); - } - if (ssf != null) { - env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, - ssf); + if (serverCsf != null) { + env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, serverCsf); + env.put("com.sun.jndi.rmi.factory.socket", registryCsf); + } + if (serverSsf != null) { + env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverSsf); } // Configure authentication @@ -254,25 +280,27 @@ public class JmxRemoteLifecycleListener env.put("jmx.remote.x.login.config", loginModuleName); } - // Create the Platform server csPlatform = createServer("Platform", rmiBindAddress, rmiRegistryPortPlatform, - rmiServerPortPlatform, env, csf, ssf, - ManagementFactory.getPlatformMBeanServer()); + rmiServerPortPlatform, env, registryCsf, registrySsf, serverCsf, serverSsf); } else if (Lifecycle.STOP_EVENT == event.getType()) { destroyServer("Platform", csPlatform); } } + private JMXConnectorServer createServer(String serverName, String bindAddress, int theRmiRegistryPort, int theRmiServerPort, - HashMap<String,Object> theEnv, RMIClientSocketFactory csf, - RMIServerSocketFactory ssf, MBeanServer theMBeanServer) { + HashMap<String,Object> theEnv, + RMIClientSocketFactory registryCsf, RMIServerSocketFactory registrySsf, + RMIClientSocketFactory serverCsf, RMIServerSocketFactory serverSsf) { // Create the RMI registry + Registry registry; try { - LocateRegistry.createRegistry(theRmiRegistryPort, csf, ssf); + registry = LocateRegistry.createRegistry( + theRmiRegistryPort, registryCsf, registrySsf); } catch (RemoteException e) { log.error(sm.getString( "jmxRemoteLifecycleListener.createRegistryFailed", @@ -284,37 +312,27 @@ public class JmxRemoteLifecycleListener bindAddress = "localhost"; } - // Build the connection string with fixed ports - StringBuilder url = new StringBuilder(); - url.append("service:jmx:rmi://"); - url.append(bindAddress); - url.append(":"); - url.append(theRmiServerPort); - url.append("/jndi/rmi://"); - url.append(bindAddress); - url.append(":"); - url.append(theRmiRegistryPort); - url.append("/jmxrmi"); + String url = "service:jmx:rmi://" + bindAddress; JMXServiceURL serviceUrl; try { serviceUrl = new JMXServiceURL(url.toString()); } catch (MalformedURLException e) { - log.error(sm.getString( - "jmxRemoteLifecycleListener.invalidURL", - serverName, url.toString()), e); + log.error(sm.getString("jmxRemoteLifecycleListener.invalidURL", serverName, url), e); return null; } - // Start the JMX server with the connection string - JMXConnectorServer cs = null; + RMIConnectorServer cs = null; try { - cs = JMXConnectorServerFactory.newJMXConnectorServer( - serviceUrl, theEnv, theMBeanServer); + RMIJRMPServerImpl server = new RMIJRMPServerImpl( + rmiServerPortPlatform, serverCsf, serverSsf, theEnv); + cs = new RMIConnectorServer(serviceUrl, theEnv, server, + ManagementFactory.getPlatformMBeanServer()); cs.start(); + registry.bind("jmxrmi", server); log.info(sm.getString("jmxRemoteLifecycleListener.start", Integer.toString(theRmiRegistryPort), Integer.toString(theRmiServerPort), serverName)); - } catch (IOException e) { + } catch (IOException | AlreadyBoundException e) { log.error(sm.getString( "jmxRemoteLifecycleListener.createServerFailed", serverName), e); @@ -322,6 +340,7 @@ public class JmxRemoteLifecycleListener return cs; } + private void destroyServer(String serverName, JMXConnectorServer theConnectorServer) { if (theConnectorServer != null) { @@ -335,6 +354,7 @@ public class JmxRemoteLifecycleListener } } + public static class RmiClientLocalhostSocketFactory implements RMIClientSocketFactory, Serializable { @@ -358,13 +378,22 @@ public class JmxRemoteLifecycleListener } } - public static class RmiServerBindSocketFactory - implements RMIServerSocketFactory { + + public static class RmiServerBindSocketFactory implements RMIServerSocketFactory { private final InetAddress bindAddress; - public RmiServerBindSocketFactory(InetAddress address) { - bindAddress = address; + public RmiServerBindSocketFactory(String address) { + InetAddress bindAddress = null; + try { + bindAddress = InetAddress.getByName(address); + } catch (UnknownHostException e) { + log.error(sm.getString( + "jmxRemoteLifecycleListener.invalidRmiBindAddress", address), e); + // bind address will be null which means any/all local addresses + // which should be safe + } + this.bindAddress = bindAddress; } @Override @@ -372,4 +401,64 @@ public class JmxRemoteLifecycleListener return new ServerSocket(port, 0, bindAddress); } } + + + public static class SslRmiServerBindSocketFactory extends SslRMIServerSocketFactory { + + private static final SSLServerSocketFactory sslServerSocketFactory; + private static final String[] defaultProtocols; + + static { + SSLContext sslContext; + try { + sslContext = SSLContext.getDefault(); + } catch (NoSuchAlgorithmException e) { + // Can't continue. Force a failure. + throw new IllegalStateException(e); + } + sslServerSocketFactory = sslContext.getServerSocketFactory(); + String[] protocols = sslContext.getDefaultSSLParameters().getProtocols(); + List<String> filteredProtocols = new ArrayList<>(protocols.length); + for (String protocol : protocols) { + if (protocol.toUpperCase(Locale.ENGLISH).contains("SSL")) { + continue; + } + filteredProtocols.add(protocol); + } + defaultProtocols = filteredProtocols.toArray(new String[filteredProtocols.size()]); + } + + private final InetAddress bindAddress; + + public SslRmiServerBindSocketFactory(String[] enabledCipherSuites, + String[] enabledProtocols, boolean needClientAuth, String address) { + super(enabledCipherSuites, enabledProtocols, needClientAuth); + InetAddress bindAddress = null; + try { + bindAddress = InetAddress.getByName(address); + } catch (UnknownHostException e) { + log.error(sm.getString( + "jmxRemoteLifecycleListener.invalidRmiBindAddress", address), e); + // bind address will be null which means any/all local addresses + // which should be safe + } + this.bindAddress = bindAddress; + } + + @Override + public ServerSocket createServerSocket(int port) throws IOException { + SSLServerSocket sslServerSocket = + (SSLServerSocket) sslServerSocketFactory.createServerSocket(port, 0, bindAddress); + if (getEnabledCipherSuites() != null) { + sslServerSocket.setEnabledCipherSuites(getEnabledCipherSuites()); + } + if (getEnabledProtocols() == null) { + sslServerSocket.setEnabledProtocols(defaultProtocols); + } else { + sslServerSocket.setEnabledProtocols(getEnabledProtocols()); + } + sslServerSocket.setNeedClientAuth(getNeedClientAuth()); + return sslServerSocket; + } + } } Modified: tomcat/trunk/java/org/apache/catalina/mbeans/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/mbeans/LocalStrings.properties?rev=1666757&r1=1666756&r2=1666757&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/mbeans/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/catalina/mbeans/LocalStrings.properties Sat Mar 14 22:20:06 2015 @@ -18,7 +18,6 @@ jmxRemoteLifecycleListener.createServerF jmxRemoteLifecycleListener.destroyServerFailed=The JMX connector server could not be stopped for the {0} server jmxRemoteLifecycleListener.invalidURL=The JMX Service URL requested for the {0} server, "{1}", was invalid jmxRemoteLifecycleListener.start=The JMX Remote Listener has configured the registry on port {0} and the server on port {1} for the {2} server -jmxRemoteLifecycleListener.sslRmiBindAddress=rmiBindAddress is incompatible with setting the system property com.sun.management.jmxremote.ssl to true jmxRemoteLifecycleListener.invalidRmiBindAddress=Invalid RMI bind address [{0}] mBeanFactory.managerContext=Manager components may only be added to Contexts. Modified: tomcat/trunk/webapps/docs/config/listeners.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/listeners.xml?rev=1666757&r1=1666756&r2=1666757&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/listeners.xml (original) +++ tomcat/trunk/webapps/docs/config/listeners.xml Sat Mar 14 22:20:06 2015 @@ -496,10 +496,7 @@ </attribute> <attribute name="rmiBindAddress" required="false"> - <p>The address of the interface to be used by JMX/RMI server. - This option is incompatible with setting the system - property <code>com.sun.management.jmxremote.ssl</code> to - <code>true</code>.</p> + <p>The address of the interface to be used by JMX/RMI server.</p> </attribute> <attribute name="useLocalPorts" required="false"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org