< prev index next >
src/java.management/share/classes/sun/management/jmxremote/ConnectorBootstrap.java
Print this page
@ rev 13068 : 6425769: jmx remote bind address
|\ Summary: Allow for binding to a specific address via custom socket factories.
| | Reviewed-by: kvn
| \
| |\
| | \
| | |\
+-+---o rev 11054 : Merge
| | |/
+---o rev 11053 : 8049367: Modular Run-Time Images
| | Reviewed-by: chegar, dfuchs, ihse, joehw, mullan, psandoz, wetmore
| | Contributed-by: alan.bateman@oracle.com, alex.buckley@oracle.com, bradford.wetmore@oracle.com, chris.hegarty@oracle.com, erik.joelsson@oracle.com, james.laskey@oracle.com, jonathan.gibbons@oracle.com, karen.kinnear@oracle.com, magnus.ihse.bursie@oracle.com, mandy.chung@oracle.com, mark.reinhold@oracle.com, paul.sandoz@oracle.com, sundararajan.athijegannathan@oracle.com
| o rev 11007 : 8048050: Agent NullPointerException when rmi.port in use
|/ Reviewed-by: jbachorik, dfuchs
o rev 10469 : 8054834: Modular Source Code
| Reviewed-by: alanb, chegar, ihse, mduigou
| Contributed-by: alan.bateman@oracle.com, alex.buckley@oracle.com, chris.hegarty@oracle.com, erik.joelsson@oracle.com, jonathan.gibbons@oracle.com, karen.kinnear@oracle.com, magnus.ihse.bursie@oracle.com, mandy.chung@oracle.com, mark.reinhold@oracle.com, paul.sandoz@oracle.com
@@ -28,20 +28,24 @@
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.MalformedURLException;
+import java.net.Socket;
+import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.rmi.NoSuchObjectException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
+import java.rmi.server.RMISocketFactory;
import java.rmi.server.RemoteObject;
import java.rmi.server.UnicastRemoteObject;
import java.security.KeyStore;
import java.security.Principal;
import java.util.HashMap;
@@ -58,10 +62,12 @@
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;
import javax.security.auth.Subject;
@@ -105,10 +111,12 @@
**/
public static interface PropertyNames {
public static final String PORT =
"com.sun.management.jmxremote.port";
+ public static final String HOST =
+ "com.sun.management.jmxremote.host";
public static final String RMI_PORT =
"com.sun.management.jmxremote.rmi.port";
public static final String CONFIG_FILE_NAME =
"com.sun.management.config.file";
public static final String USE_LOCAL_ONLY =
@@ -422,14 +430,18 @@
accessFileName = props.getProperty(PropertyNames.ACCESS_FILE_NAME,
getDefaultFileName(DefaultValues.ACCESS_FILE_NAME));
checkAccessFile(accessFileName);
}
+ final String bindAddress =
+ props.getProperty(PropertyNames.HOST);
+
if (log.debugOn()) {
log.debug("startRemoteConnectorServer",
Agent.getText("jmxremote.ConnectorBootstrap.starting") +
"\n\t" + PropertyNames.PORT + "=" + port +
+ (bindAddress == null ? "" : "\n\t" + PropertyNames.HOST + "=" + bindAddress) +
"\n\t" + PropertyNames.RMI_PORT + "=" + rmiPort +
"\n\t" + PropertyNames.USE_SSL + "=" + useSsl +
"\n\t" + PropertyNames.USE_REGISTRY_SSL + "=" + useRegistrySsl +
"\n\t" + PropertyNames.SSL_CONFIG_FILE_NAME + "=" + sslConfigFileName +
"\n\t" + PropertyNames.SSL_ENABLED_CIPHER_SUITES + "=" +
@@ -456,11 +468,11 @@
final JMXConnectorServerData data = exportMBeanServer(
mbs, port, rmiPort, useSsl, useRegistrySsl,
sslConfigFileName, enabledCipherSuitesList,
enabledProtocolsList, sslNeedClientAuth,
useAuthentication, loginConfigName,
- passwordFileName, accessFileName);
+ passwordFileName, accessFileName, bindAddress);
cs = data.jmxConnectorServer;
url = data.jmxRemoteURL;
log.config("startRemoteConnectorServer",
Agent.getText("jmxremote.ConnectorBootstrap.ready",
url.toString()));
@@ -626,16 +638,17 @@
private static SslRMIServerSocketFactory createSslRMIServerSocketFactory(
String sslConfigFileName,
String[] enabledCipherSuites,
String[] enabledProtocols,
- boolean sslNeedClientAuth) {
+ boolean sslNeedClientAuth,
+ String bindAddress) {
if (sslConfigFileName == null) {
- return new SslRMIServerSocketFactory(
+ return new HostAwareSslSocketFactory(
enabledCipherSuites,
enabledProtocols,
- sslNeedClientAuth);
+ sslNeedClientAuth, bindAddress);
} else {
checkRestrictedFile(sslConfigFileName);
try {
// Load the SSL keystore properties from the config file
Properties p = new Properties();
@@ -685,15 +698,15 @@
tmf.init(ts);
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
- return new SslRMIServerSocketFactory(
+ return new HostAwareSslSocketFactory(
ctx,
enabledCipherSuites,
enabledProtocols,
- sslNeedClientAuth);
+ sslNeedClientAuth, bindAddress);
} catch (Exception e) {
throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.toString());
}
}
}
@@ -709,26 +722,29 @@
String[] enabledProtocols,
boolean sslNeedClientAuth,
boolean useAuthentication,
String loginConfigName,
String passwordFileName,
- String accessFileName)
+ String accessFileName,
+ String bindAddress)
throws IOException, MalformedURLException {
/* Make sure we use non-guessable RMI object IDs. Otherwise
* attackers could hijack open connections by guessing their
* IDs. */
System.setProperty("java.rmi.server.randomIDs", "true");
- JMXServiceURL url = new JMXServiceURL("rmi", null, rmiPort);
+ JMXServiceURL url = new JMXServiceURL("rmi", bindAddress, rmiPort);
Map<String, Object> env = new HashMap<>();
PermanentExporter exporter = new PermanentExporter();
env.put(RMIExporter.EXPORTER_ATTRIBUTE, exporter);
+ boolean useSocketFactory = bindAddress != null && !useSsl;
+
if (useAuthentication) {
if (loginConfigName != null) {
env.put("jmx.remote.x.login.config", loginConfigName);
}
if (passwordFileName != null) {
@@ -749,20 +765,26 @@
if (useSsl || useRegistrySsl) {
csf = new SslRMIClientSocketFactory();
ssf = createSslRMIServerSocketFactory(
sslConfigFileName, enabledCipherSuites,
- enabledProtocols, sslNeedClientAuth);
+ enabledProtocols, sslNeedClientAuth, bindAddress);
}
if (useSsl) {
env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
csf);
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
ssf);
}
+ if (useSocketFactory) {
+ ssf = new HostAwareSocketFactory(bindAddress);
+ env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
+ ssf);
+ }
+
JMXConnectorServer connServer = null;
try {
connServer =
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
connServer.start();
@@ -778,10 +800,14 @@
if (useRegistrySsl) {
registry =
new SingleEntryRegistry(port, csf, ssf,
"jmxrmi", exporter.firstExported);
+ } else if (useSocketFactory) {
+ registry =
+ new SingleEntryRegistry(port, csf, ssf,
+ "jmxrmi", exporter.firstExported);
} else {
registry =
new SingleEntryRegistry(port,
"jmxrmi", exporter.firstExported);
}
@@ -811,6 +837,174 @@
}
private static final ClassLogger log =
new ClassLogger(ConnectorBootstrap.class.getPackage().getName(),
"ConnectorBootstrap");
+
+ private static class HostAwareSocketFactory implements RMIServerSocketFactory {
+
+ private final String bindAddress;
+
+ private HostAwareSocketFactory(String bindAddress) {
+ this.bindAddress = bindAddress;
+ }
+
+ @Override
+ public ServerSocket createServerSocket(int port) throws IOException {
+ if (bindAddress == null) {
+ return new ServerSocket(port);
+ } else {
+ try {
+ InetAddress addr = InetAddress.getByName(bindAddress);
+ return new ServerSocket(port, 0, addr);
+ } catch (UnknownHostException e) {
+ return new ServerSocket(port);
+ }
+ }
+ }
+ }
+
+ private static class HostAwareSslSocketFactory extends SslRMIServerSocketFactory {
+
+ private final String bindAddress;
+ private final String[] enabledCipherSuites;
+ private final String[] enabledProtocols;
+ private final boolean needClientAuth;
+ private final SSLContext context;
+
+ private HostAwareSslSocketFactory(String[] enabledCipherSuites,
+ String[] enabledProtocols,
+ boolean sslNeedClientAuth,
+ String bindAddress) throws IllegalArgumentException {
+ this(null, enabledCipherSuites, enabledProtocols, sslNeedClientAuth, bindAddress);
+ }
+
+ private HostAwareSslSocketFactory(SSLContext ctx,
+ String[] enabledCipherSuites,
+ String[] enabledProtocols,
+ boolean sslNeedClientAuth,
+ String bindAddress) throws IllegalArgumentException {
+ this.context = ctx;
+ this.bindAddress = bindAddress;
+ this.enabledProtocols = enabledProtocols;
+ this.enabledCipherSuites = enabledCipherSuites;
+ this.needClientAuth = sslNeedClientAuth;
+ checkValues(ctx, enabledCipherSuites, enabledProtocols);
+ }
+
+ @Override
+ public ServerSocket createServerSocket(int port) throws IOException {
+ if (bindAddress != null) {
+ try {
+ InetAddress addr = InetAddress.getByName(bindAddress);
+ return new SslServerSocket(port, 0, addr, context,
+ enabledCipherSuites, enabledProtocols, needClientAuth);
+ } catch (UnknownHostException e) {
+ return new SslServerSocket(port, context,
+ enabledCipherSuites, enabledProtocols, needClientAuth);
+ }
+ } else {
+ return new SslServerSocket(port, context,
+ enabledCipherSuites, enabledProtocols, needClientAuth);
+ }
+ }
+
+ private static void checkValues(SSLContext context,
+ String[] enabledCipherSuites,
+ String[] enabledProtocols) throws IllegalArgumentException {
+ // Force the initialization of the default at construction time,
+ // rather than delaying it to the first time createServerSocket()
+ // is called.
+ //
+ final SSLSocketFactory sslSocketFactory =
+ context == null ?
+ (SSLSocketFactory)SSLSocketFactory.getDefault() : context.getSocketFactory();
+ SSLSocket sslSocket = null;
+ if (enabledCipherSuites != null || enabledProtocols != null) {
+ try {
+ sslSocket = (SSLSocket) sslSocketFactory.createSocket();
+ } catch (Exception e) {
+ final String msg = "Unable to check if the cipher suites " +
+ "and protocols to enable are supported";
+ throw (IllegalArgumentException)
+ new IllegalArgumentException(msg).initCause(e);
+ }
+ }
+
+ // Check if all the cipher suites and protocol versions to enable
+ // are supported by the underlying SSL/TLS implementation and if
+ // true create lists from arrays.
+ //
+ if (enabledCipherSuites != null) {
+ sslSocket.setEnabledCipherSuites(enabledCipherSuites);
+ }
+ if (enabledProtocols != null) {
+ sslSocket.setEnabledProtocols(enabledProtocols);
+ }
+ }
+ }
+
+ private static class SslServerSocket extends ServerSocket {
+
+ private static SSLSocketFactory defaultSSLSocketFactory;
+ private final String[] enabledCipherSuites;
+ private final String[] enabledProtocols;
+ private final boolean needClientAuth;
+ private final SSLContext context;
+
+ private SslServerSocket(int port,
+ SSLContext ctx,
+ String[] enabledCipherSuites,
+ String[] enabledProtocols,
+ boolean needClientAuth) throws IOException {
+ super(port);
+ this.enabledProtocols = enabledProtocols;
+ this.enabledCipherSuites = enabledCipherSuites;
+ this.needClientAuth = needClientAuth;
+ this.context = ctx;
+ }
+
+ private SslServerSocket(int port,
+ int backlog,
+ InetAddress bindAddr,
+ SSLContext ctx,
+ String[] enabledCipherSuites,
+ String[] enabledProtocols,
+ boolean needClientAuth) throws IOException {
+ super(port, backlog, bindAddr);
+ this.enabledProtocols = enabledProtocols;
+ this.enabledCipherSuites = enabledCipherSuites;
+ this.needClientAuth = needClientAuth;
+ this.context = ctx;
+ }
+
+ @Override
+ public Socket accept() throws IOException {
+ final SSLSocketFactory sslSocketFactory =
+ context == null ?
+ getDefaultSSLSocketFactory() : context.getSocketFactory();
+ Socket socket = super.accept();
+ SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
+ socket, socket.getInetAddress().getHostName(),
+ socket.getPort(), true);
+ sslSocket.setUseClientMode(false);
+ if (enabledCipherSuites != null) {
+ sslSocket.setEnabledCipherSuites(enabledCipherSuites);
+ }
+ if (enabledProtocols != null) {
+ sslSocket.setEnabledProtocols(enabledProtocols);
+ }
+ sslSocket.setNeedClientAuth(needClientAuth);
+ return sslSocket;
+ }
+
+ private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
+ if (defaultSSLSocketFactory == null) {
+ defaultSSLSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
+ return defaultSSLSocketFactory;
+ } else {
+ return defaultSSLSocketFactory;
+ }
+ }
+
+ }
}
< prev index next >