Author: remm
Date: Tue Jan 29 14:34:19 2019
New Revision: 1852458
URL: http://svn.apache.org/viewvc?rev=1852458&view=rev
Log:
Add full SSL configuration options to the JMX remote listener using the
SSLHostConfig framework. Better configuration can be useful for cloud
deployments, although raw JMX is often not the prefered option.
Modified:
tomcat/trunk/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
tomcat/trunk/webapps/docs/changelog.xml
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=1852458&r1=1852457&r2=1852458&view=diff
==============================================================================
---
tomcat/trunk/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
(original)
+++
tomcat/trunk/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
Tue Jan 29 14:34:19 2019
@@ -31,11 +31,7 @@ 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 java.util.Map;
import javax.management.remote.JMXConnectorServer;
@@ -45,6 +41,7 @@ import javax.management.remote.rmi.RMIJR
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSessionContext;
import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;
@@ -53,6 +50,9 @@ import org.apache.catalina.LifecycleEven
import org.apache.catalina.LifecycleListener;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+import org.apache.tomcat.util.net.jsse.JSSEUtil;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -62,7 +62,9 @@ import org.apache.tomcat.util.res.String
* the listener. The remainder of the configuration is via the standard system
* properties for configuring JMX.
*/
-public class JmxRemoteLifecycleListener implements LifecycleListener {
+public class JmxRemoteLifecycleListener extends SSLHostConfig implements
LifecycleListener {
+
+ private static final long serialVersionUID = 1L;
private static final Log log =
LogFactory.getLog(JmxRemoteLifecycleListener.class);
@@ -74,9 +76,6 @@ public class JmxRemoteLifecycleListener
protected int rmiServerPortPlatform = -1;
protected boolean rmiRegistrySSL = true;
protected boolean rmiServerSSL = true;
- protected String ciphers[] = null;
- protected String protocols[] = null;
- protected boolean clientAuth = true;
protected boolean authenticate = true;
protected String passwordFile = null;
protected String loginModuleName = null;
@@ -156,48 +155,138 @@ public class JmxRemoteLifecycleListener
this.useLocalPorts = useLocalPorts;
}
- private void init() {
+ /**
+ * @return the rmiRegistrySSL
+ */
+ public boolean isRmiRegistrySSL() {
+ return rmiRegistrySSL;
+ }
+
+ /**
+ * @param rmiRegistrySSL the rmiRegistrySSL to set
+ */
+ public void setRmiRegistrySSL(boolean rmiRegistrySSL) {
+ this.rmiRegistrySSL = rmiRegistrySSL;
+ }
+
+ /**
+ * @return the rmiServerSSL
+ */
+ public boolean isRmiServerSSL() {
+ return rmiServerSSL;
+ }
+
+ /**
+ * @param rmiServerSSL the rmiServerSSL to set
+ */
+ public void setRmiServerSSL(boolean rmiServerSSL) {
+ this.rmiServerSSL = rmiServerSSL;
+ }
+
+ /**
+ * @return the authenticate
+ */
+ public boolean isAuthenticate() {
+ return authenticate;
+ }
+
+ /**
+ * @param authenticate the authenticate to set
+ */
+ public void setAuthenticate(boolean authenticate) {
+ this.authenticate = authenticate;
+ }
+
+ /**
+ * @return the passwordFile
+ */
+ public String getPasswordFile() {
+ return passwordFile;
+ }
+
+ /**
+ * @param passwordFile the passwordFile to set
+ */
+ public void setPasswordFile(String passwordFile) {
+ this.passwordFile = passwordFile;
+ }
+
+ /**
+ * @return the loginModuleName
+ */
+ public String getLoginModuleName() {
+ return loginModuleName;
+ }
+
+ /**
+ * @param loginModuleName the loginModuleName to set
+ */
+ public void setLoginModuleName(String loginModuleName) {
+ this.loginModuleName = loginModuleName;
+ }
+
+ /**
+ * @return the accessFile
+ */
+ public String getAccessFile() {
+ return accessFile;
+ }
+
+ /**
+ * @param accessFile the accessFile to set
+ */
+ public void setAccessFile(String accessFile) {
+ this.accessFile = accessFile;
+ }
+
+ protected void init() {
// 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 rmiRegistrySSLValue = System.getProperty(
- "com.sun.management.jmxremote.registry.ssl", "false");
- rmiRegistrySSL = Boolean.parseBoolean(rmiRegistrySSLValue);
-
- String rmiServerSSLValue = System.getProperty(
- "com.sun.management.jmxremote.ssl", "true");
- rmiServerSSL = Boolean.parseBoolean(rmiServerSSLValue);
+ String rmiRegistrySSLValue =
System.getProperty("com.sun.management.jmxremote.registry.ssl");
+ if (rmiRegistrySSLValue != null) {
+ setRmiRegistrySSL(Boolean.parseBoolean(rmiRegistrySSLValue));
+ }
+
+ String rmiServerSSLValue =
System.getProperty("com.sun.management.jmxremote.ssl");
+ if (rmiServerSSLValue != null) {
+ setRmiServerSSL(Boolean.parseBoolean(rmiServerSSLValue));
+ }
- String protocolsValue = System.getProperty(
- "com.sun.management.jmxremote.ssl.enabled.protocols");
+ String protocolsValue =
System.getProperty("com.sun.management.jmxremote.ssl.enabled.protocols");
if (protocolsValue != null) {
- protocols = protocolsValue.split(",");
+ setEnabledProtocols(protocolsValue.split(","));
}
- String ciphersValue = System.getProperty(
- "com.sun.management.jmxremote.ssl.enabled.cipher.suites");
+ String ciphersValue =
System.getProperty("com.sun.management.jmxremote.ssl.enabled.cipher.suites");
if (ciphersValue != null) {
- ciphers = ciphersValue.split(",");
+ setCiphers(ciphersValue);
}
- String clientAuthValue = System.getProperty(
- "com.sun.management.jmxremote.ssl.need.client.auth", "true");
- clientAuth = Boolean.parseBoolean(clientAuthValue);
+ String clientAuthValue =
System.getProperty("com.sun.management.jmxremote.ssl.need.client.auth");
+ if (clientAuthValue != null) {
+ setCertificateVerification(clientAuthValue);
+ }
- String authenticateValue = System.getProperty(
- "com.sun.management.jmxremote.authenticate", "true");
- authenticate = Boolean.parseBoolean(authenticateValue);
+ String authenticateValue =
System.getProperty("com.sun.management.jmxremote.authenticate");
+ if (authenticateValue != null) {
+ setAuthenticate(Boolean.parseBoolean(authenticateValue));
+ }
- passwordFile = System.getProperty(
- "com.sun.management.jmxremote.password.file",
- "jmxremote.password");
+ String passwordFileValue =
System.getProperty("com.sun.management.jmxremote.password.file");
+ if (passwordFileValue != null) {
+ setPasswordFile(passwordFileValue);
+ }
- accessFile = System.getProperty(
- "com.sun.management.jmxremote.access.file",
- "jmxremote.access");
+ String accessFileValue =
System.getProperty("com.sun.management.jmxremote.access.file");
+ if (accessFileValue != null) {
+ setAccessFile(accessFileValue);
+ }
- loginModuleName = System.getProperty(
- "com.sun.management.jmxremote.login.config");
+ String loginModuleNameValue =
System.getProperty("com.sun.management.jmxremote.login.config");
+ if (loginModuleNameValue != null) {
+ setLoginModuleName(loginModuleNameValue);
+ }
}
@@ -205,9 +294,30 @@ public class JmxRemoteLifecycleListener
public void lifecycleEvent(LifecycleEvent event) {
// When the server starts, configure JMX/RMI
if (Lifecycle.START_EVENT.equals(event.getType())) {
- // Configure using standard jmx system properties
+
+ // Configure using standard JMX system properties
init();
+ SSLContext sslContext = null;
+ // Create SSL context if properties were set to define a
certificate
+ if (getCertificates().size() > 0) {
+ SSLHostConfigCertificate certificate =
getCertificates().iterator().next();
+ // This can only support JSSE
+ JSSEUtil sslUtil = new JSSEUtil(certificate);
+ try {
+ sslContext =
javax.net.ssl.SSLContext.getInstance(getSslProtocol());
+ setEnabledProtocols(sslUtil.getEnabledProtocols());
+ setEnabledCiphers(sslUtil.getEnabledCiphers());
+ sslContext.init(sslUtil.getKeyManagers(),
sslUtil.getTrustManagers(), null);
+ SSLSessionContext sessionContext =
sslContext.getServerSessionContext();
+ if (sessionContext != null) {
+ sslUtil.configureSessionContext(sessionContext);
+ }
+ } catch (Exception e) {
+
log.error(sm.getString("jmxRemoteLifecycleListener.invalidSSLConfiguration"),
e);
+ }
+ }
+
// Prevent an attacker guessing the RMI object ID
System.setProperty("java.rmi.server.randomIDs", "true");
@@ -224,11 +334,14 @@ public class JmxRemoteLifecycleListener
if (rmiRegistrySSL) {
registryCsf = new SslRMIClientSocketFactory();
if (rmiBindAddress == null) {
- registrySsf = new SslRMIServerSocketFactory(
- ciphers, protocols, clientAuth);
+ registrySsf = new SslRMIServerSocketFactory(sslContext,
+ getEnabledCiphers(), getEnabledProtocols(),
+ getCertificateVerification() ==
CertificateVerification.REQUIRED);
} else {
- registrySsf = new SslRmiServerBindSocketFactory(
- ciphers, protocols, clientAuth, rmiBindAddress);
+ registrySsf = new SslRmiServerBindSocketFactory(sslContext,
+ getEnabledCiphers(), getEnabledProtocols(),
+ getCertificateVerification() ==
CertificateVerification.REQUIRED,
+ rmiBindAddress);
}
} else {
if (rmiBindAddress != null) {
@@ -240,11 +353,14 @@ public class JmxRemoteLifecycleListener
if (rmiServerSSL) {
serverCsf = new SslRMIClientSocketFactory();
if (rmiBindAddress == null) {
- serverSsf = new SslRMIServerSocketFactory(
- ciphers, protocols, clientAuth);
+ serverSsf = new SslRMIServerSocketFactory(sslContext,
+ getEnabledCiphers(), getEnabledProtocols(),
+ getCertificateVerification() ==
CertificateVerification.REQUIRED);
} else {
- serverSsf = new SslRmiServerBindSocketFactory(
- ciphers, protocols, clientAuth, rmiBindAddress);
+ serverSsf = new SslRmiServerBindSocketFactory(sslContext,
+ getEnabledCiphers(), getEnabledProtocols(),
+ getCertificateVerification() ==
CertificateVerification.REQUIRED,
+ rmiBindAddress);
}
} else {
if (rmiBindAddress != null) {
@@ -410,34 +526,13 @@ public class JmxRemoteLifecycleListener
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;
+ private final SSLContext sslContext;
- public SslRmiServerBindSocketFactory(String[] enabledCipherSuites,
+ public SslRmiServerBindSocketFactory(SSLContext sslContext, String[]
enabledCipherSuites,
String[] enabledProtocols, boolean needClientAuth, String
address) {
- super(enabledCipherSuites, enabledProtocols, needClientAuth);
+ super(sslContext, enabledCipherSuites, enabledProtocols,
needClientAuth);
+ this.sslContext = sslContext;
InetAddress bindAddress = null;
try {
bindAddress = InetAddress.getByName(address);
@@ -451,15 +546,16 @@ public class JmxRemoteLifecycleListener
}
@Override
- public ServerSocket createServerSocket(int port) throws IOException {
+ public ServerSocket createServerSocket(int port) throws IOException {
+ SSLServerSocketFactory sslServerSocketFactory = (sslContext ==
null)
+ ? (SSLServerSocketFactory)
SSLServerSocketFactory.getDefault()
+ : sslContext.getServerSocketFactory();
SSLServerSocket sslServerSocket =
(SSLServerSocket)
sslServerSocketFactory.createServerSocket(port, 0, bindAddress);
if (getEnabledCipherSuites() != null) {
sslServerSocket.setEnabledCipherSuites(getEnabledCipherSuites());
}
- if (getEnabledProtocols() == null) {
- sslServerSocket.setEnabledProtocols(defaultProtocols);
- } else {
+ if (getEnabledProtocols() != null) {
sslServerSocket.setEnabledProtocols(getEnabledProtocols());
}
sslServerSocket.setNeedClientAuth(getNeedClientAuth());
Modified: tomcat/trunk/webapps/docs/changelog.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1852458&r1=1852457&r2=1852458&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Tue Jan 29 14:34:19 2019
@@ -130,6 +130,10 @@
application class loading when running under a
<code>SecurityManager</code>. (markt)
</fix>
+ <update>
+ Add SSL configuration options to the JMX remote listener using the
+ <code>SSLHostConfig</code> framework. (remm)
+ </update>
</changelog>
</subsection>
<subsection name="Coyote">
Modified: tomcat/trunk/webapps/docs/config/listeners.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/listeners.xml?rev=1852458&r1=1852457&r2=1852458&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/listeners.xml (original)
+++ tomcat/trunk/webapps/docs/config/listeners.xml Tue Jan 29 14:34:19 2019
@@ -509,8 +509,16 @@
<p>The <strong>JMX Remote Lifecycle Listener</strong> fixes the ports used
by
the JMX/RMI Server making things much simpler if you need to connect
jconsole or a similar tool to a remote Tomcat instance that is running
- behind a firewall. Only these ports are configured via the listener. The
- remainder of the configuration is via the standard system properties for
+ behind a firewall.</p>
+
+ <p>Note: The SSL configuration can be done with attributes identical to
those
+ of <a href="http.html#SSL_Support_-_SSLHostConfig">SSLHostConfig</a> and
the
+ properties of the default certificate as defined for
+ <a href="http.html#SSL_Support_-_Certificate">SSLHostConfigCertificate</a>.
+ This will create a JSSE SSLContext which will be given to the JMX/RMI
registry
+ when creating the server socket.</p>
+
+ <p>The remainder of the configuration is via the standard system
properties for
configuring JMX. For further information on configuring JMX see
<a
href="http://docs.oracle.com/javase/6/docs/technotes/guides/management/agent.html">
Monitoring and Management Using JMX</a> included with the Java SDK
@@ -520,7 +528,7 @@
element.</p>
<p>The following additional attributes are supported by the <strong>JMX
Remote
- Lifecycle Listener</strong>:</p>
+ Lifecycle Listener</strong>, in addition to the SSL related attributes:</p>
<attributes>
@@ -569,6 +577,13 @@
<code>letmein</code>.
</p>
+ <h3>SSL configurations</h3>
+
+ <p>A certificate can be defined for the server using a keystore:</p>
+ <source><![CDATA[ <Listener
className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener"
+ rmiRegistryPortPlatform="10001" rmiServerPortPlatform="10002"
+ certificateKeystoreFile="${catalina.home}/conf/mykeystore.jks"
/>]]></source>
+
<h3>Using JAAS</h3>
<p>If we use the following system properties instead:</p>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]