Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for scenarios where the RMI registry has SSL enabled #835

Merged
merged 3 commits into from
Apr 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions jmx-metrics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ otel.jmx.target.system = jvm,kafka
otel.jmx.interval.milliseconds = 5000
otel.jmx.username = my-username
otel.jmx.password = my-password

otel.jmx.remote.registry.ssl=false
otel.metrics.exporter = otlp
otel.exporter.otlp.endpoint = http://my-opentelemetry-collector:4317
```

As configured in this example, the metric gatherer will establish an MBean server connection using the
specified `otel.jmx.service.url` (required) and credentials and configure an OTLP gRPC metrics exporter reporting to
`otel.exporter.otlp.endpoint`. After loading the included JVM and Kafka metric-gathering scripts determined by
`otel.exporter.otlp.endpoint`. If SSL is enabled on the RMI registry for your server, the `otel.jmx.remote.registry.ssl` property must be set to `true`. After loading the included JVM and Kafka metric-gathering scripts determined by
the comma-separated list in `otel.jmx.target.system`, it will then run the scripts on the desired interval
length of `otel.jmx.interval.milliseconds` and export the resulting metrics.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class JmxClient {
Expand All @@ -31,6 +30,7 @@ public class JmxClient {
private final String password;
private final String realm;
private final String remoteProfile;
private final boolean registrySsl;
@Nullable private JMXConnector jmxConn;

JmxClient(final JmxConfig config) throws MalformedURLException {
Expand All @@ -39,6 +39,7 @@ public class JmxClient {
this.password = config.password;
this.realm = config.realm;
this.remoteProfile = config.remoteProfile;
this.registrySsl = config.registrySsl;
}

public MBeanServerConnection getConnection() {
Expand Down Expand Up @@ -68,7 +69,7 @@ public MBeanServerConnection getConnection() {
logger.warning("SASL unsupported in current environment: " + e.getMessage());
}

jmxConn = JMXConnectorFactory.connect(url, env);
jmxConn = JmxConnectorHelper.connect(url, env, registrySsl);
return jmxConn.getMBeanServerConnection();
} catch (IOException e) {
logger.log(Level.WARNING, "Could not connect to remote JMX server: ", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class JmxConfig {
static final String INTERVAL_MILLISECONDS = PREFIX + "jmx.interval.milliseconds";
static final String METRICS_EXPORTER_TYPE = PREFIX + "metrics.exporter";
static final String EXPORTER = PREFIX + "exporter.";

static final String REGISTRY_SSL = PREFIX + "jmx.remote.registry.ssl";
static final String EXPORTER_INTERVAL = PREFIX + "metric.export.interval";

static final String OTLP_ENDPOINT = EXPORTER + "otlp.endpoint";
Expand Down Expand Up @@ -74,7 +74,7 @@ class JmxConfig {
final String password;
final String realm;
final String remoteProfile;

final boolean registrySsl;
final Properties properties;

JmxConfig(final Properties props) {
Expand Down Expand Up @@ -111,6 +111,8 @@ class JmxConfig {
remoteProfile = properties.getProperty(JMX_REMOTE_PROFILE);
realm = properties.getProperty(JMX_REALM);

registrySsl = Boolean.valueOf(properties.getProperty(REGISTRY_SSL));

// For the list of System Properties, if they have been set in the properties file
// they need to be set in Java System Properties.
JAVA_SYSTEM_PROPERTIES.forEach(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.jmxmetrics;

import java.io.IOException;
import java.net.URI;
import java.rmi.NotBoundException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import javax.management.remote.rmi.RMIServer;
import javax.rmi.ssl.SslRMIClientSocketFactory;

public class JmxConnectorHelper {

private static final Logger logger = Logger.getLogger(JmxConnectorHelper.class.getName());

private static RMIServer stub = null;
private static final SslRMIClientSocketFactory sslRMIClientSocketFactory =
new SslRMIClientSocketFactory();

private JmxConnectorHelper() {}

/**
* To use SSL, the {@link RMIServer} stub used by the {@link RMIConnector} must be built
* separately. As a result, we have to unwind the {@link JMXConnectorFactory#connect} method and
* reimplement pieces.
*/
public static JMXConnector connect(
JMXServiceURL serviceURL, Map<String, Object> env, boolean registrySsl) throws IOException {

// Different connection logic is needed when SSL is enabled on the RMI registry
if (!registrySsl) {
return JMXConnectorFactory.connect(serviceURL, env);
}

logger.log(Level.INFO, "Attempting to connect to an SSL-protected RMI registry");

String hostName;
int port;

if (serviceURL.getURLPath().startsWith("/jndi/")) {
final String[] components = serviceURL.getURLPath().split("/", 3);
final URI uri = URI.create(components[2]);
hostName = uri.getHost();
port = uri.getPort();
} else {
hostName = serviceURL.getHost();
port = serviceURL.getPort();
}

if (stub == null) {
stub = getStub(hostName, port);
}
JMXConnector jmxConn = new RMIConnector(stub, null);
jmxConn.connect(env);
return jmxConn;
}

private static RMIServer getStub(String hostName, int port) throws IOException {
try {
Registry registry = LocateRegistry.getRegistry(hostName, port, sslRMIClientSocketFactory);
return (RMIServer) registry.lookup("jmxrmi");
} catch (NotBoundException nbe) {
throw new IOException(nbe);
}
}
}