Skip to content
59 changes: 56 additions & 3 deletions zookeeper-docs/src/main/resources/markdown/zookeeperAdmin.md
Original file line number Diff line number Diff line change
Expand Up @@ -2274,8 +2274,17 @@ options are used to configure the [AdminServer](#sc_adminserver).
**New in 3.8.0:** Prometheus.io exporter will start a Jetty server and listen this address, default is "0.0.0.0"

* *metricsProvider.httpPort* :
Prometheus.io exporter will start a Jetty server and bind to this port, it defaults to 7000.
Prometheus end point will be http://hostname:httPort/metrics.
Prometheus.io exporter will start a Jetty server and bind to this port.
Prometheus end point will be `http://hostname:httpPort/metrics`.
If omitted no HTTP port will be opened.
* Note: Either HTTP or HTTPS port has to be specified or both.

* *metricsProvider.httpsPort* :
**New in 3.10.0:**
Prometheus.io exporter will start a Jetty server and bind to this port.
Prometheus end point will be `https://hostname:httpsPort/metrics`.
If omitted no HTTPS port will be opened.
* Note: Either HTTP or HTTPS port has to be specified or both.

* *metricsProvider.exportJvmInfo* :
If this property is set to **true** Prometheus.io will export useful metrics about the JVM.
Expand All @@ -2296,7 +2305,51 @@ options are used to configure the [AdminServer](#sc_adminserver).
**New in 3.7.1:**
The timeout in ms for Prometheus worker threads shutdown.
Default value is 1000ms.


* *metricsProvider.ssl.keyStore.location* and *metricsProvider.ssl.keyStore.password*:
**New in 3.10.0:**
Specifies the file path to a Java keystore containing the local
credentials to be used for PrometheusMetricsProvider TLS connections and the
password to unlock the file.

* *metricsProvider.ssl.keyStore.type*:
**New in 3.10.0:**
Specifies the file format of the PrometheusMetricsProvider keystore. Values: JKS, PEM, PKCS12 or null (detect by filename).
Default: null.

* *metricsProvider.ssl.trustStore.location* and *metricsProvider.ssl.trustStore.password*:
**New in 3.10.0:**
Specifies the file path to a Java truststore containing the remote
credentials to be used for PrometheusMetricsProvider TLS connections and the
password to unlock the file.

* *metricsProvider.ssl.trustStore.type*:
**New in 3.10.0:**
Specifies the file format of the PrometheusMetricsProvider truststore. Values: JKS, PEM, PKCS12 or null (detect by filename).
Default: null.

* *metricsProvider.ssl.need.client.auth*:
**New in 3.10.0:**
Specifies options to authenticate SSL connections from clients.
When set to true, PrometheusMetricsProvider will "require" client authentication.
Default: true

* *metricsProvider.ssl.want.client.auth*:
**New in 3.10.0:**
Specifies options to authenticate SSL connections from clients.
When set to true, PrometheusMetricsProvider will "request" client authentication.
Default: true

* *metricsProvider.ssl.ciphersuites* :
**New in 3.10.0:**
The enabled cipher suites to be used in TLS negotiation for PrometheusMetricsProvider.
Default value is Jetty default.

* *metricsProvider.ssl.enabledProtocols* :
**New in 3.10.0:**
The enabled protocols to be used in TLS negotiation for PrometheusMetricsProvider.
Default value is Jetty default.

<a name="Communication+using+the+Netty+framework"></a>

### Communication using the Netty framework
Expand Down
18 changes: 18 additions & 0 deletions zookeeper-docs/src/main/resources/markdown/zookeeperMonitor.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,24 @@ ZooKeeper also supports SSL for Prometheus metrics, which provides secure data t
metricsProvider.httpPort=7000
metricsProvider.httpsPort=4443
```

#### Configure TLS protocols and cipher suites for SSL/TLS negotiation in Prometheus Metrics:

It is also possible to restrict TLS versions and cipher suites for PrometheusMetricsProvider.
Add the following configuration settings to the `zoo.cfg` config file:

```
metricsProvider.ssl.enabledProtocols=TLSv1.2,TLSv1.3
metricsProvider.ssl.ciphersuites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
```

To verify raise the log level of PrometheusMetricsProvider to DEBUG and check that the following entries can be seen in the logs:

```
2026-03-11 15:46:36,997 [myid:] - INFO [main:o.a.z.m.p.PrometheusMetricsProvider@245] - Setting enabled protocols: 'TLSv1.2,TLSv1.3'
2026-03-11 15:46:36,997 [myid:] - INFO [main:o.a.z.m.p.PrometheusMetricsProvider@251] - Setting enabled cipherSuites: 'TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'
```

### Prometheus
- Running a [Prometheus](https://prometheus.io/) monitoring service is the easiest way to ingest and record ZooKeeper's metrics.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import org.apache.zookeeper.metrics.Gauge;
import org.apache.zookeeper.metrics.GaugeSet;
import org.apache.zookeeper.metrics.MetricsContext;
import org.apache.zookeeper.metrics.MetricsContext.DetailLevel;
import org.apache.zookeeper.metrics.MetricsProvider;
import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException;
import org.apache.zookeeper.metrics.Summary;
Expand Down Expand Up @@ -83,8 +82,14 @@ public class PrometheusMetricsProvider implements MetricsProvider {
private String trustStoreType;
private boolean needClientAuth = true; // Secure default
private boolean wantClientAuth = true; // Secure default
private String enabledProtocols;
private String cipherSuites;

// Constants for configuration
public static final String HTTP_HOST = "httpHost";
public static final String HTTP_PORT = "httpPort";
public static final String EXPORT_JVM_INFO = "exportJvmInfo";
public static final String HTTPS_PORT = "httpsPort";
public static final String NUM_WORKER_THREADS = "numWorkerThreads";
public static final String SSL_KEYSTORE_LOCATION = "ssl.keyStore.location";
public static final String SSL_KEYSTORE_PASSWORD = "ssl.keyStore.password";
Expand All @@ -94,6 +99,8 @@ public class PrometheusMetricsProvider implements MetricsProvider {
public static final String SSL_TRUSTSTORE_TYPE = "ssl.trustStore.type";
public static final String SSL_NEED_CLIENT_AUTH = "ssl.need.client.auth";
public static final String SSL_WANT_CLIENT_AUTH = "ssl.want.client.auth";
public static final String SSL_ENABLED_PROTOCOLS = "ssl.enabledProtocols";
public static final String SSL_ENABLED_CIPHERS = "ssl.ciphersuites";
public static final int SCAN_INTERVAL = 60 * 10; // 10 minutes

/**
Expand All @@ -114,10 +121,10 @@ protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws
public void configure(Properties configuration) throws MetricsProviderLifeCycleException {
LOG.info("Initializing Prometheus metrics with Jetty, configuration: {}", configuration);

this.host = configuration.getProperty("httpHost", "0.0.0.0");
this.httpPort = Integer.parseInt(configuration.getProperty("httpPort", "-1"));
this.httpsPort = Integer.parseInt(configuration.getProperty("httpsPort", "-1"));
this.exportJvmInfo = Boolean.parseBoolean(configuration.getProperty("exportJvmInfo", "true"));
this.host = configuration.getProperty(HTTP_HOST, "0.0.0.0");
this.httpPort = Integer.parseInt(configuration.getProperty(HTTP_PORT, "-1"));
this.httpsPort = Integer.parseInt(configuration.getProperty(HTTPS_PORT, "-1"));
this.exportJvmInfo = Boolean.parseBoolean(configuration.getProperty(EXPORT_JVM_INFO, "true"));
this.numWorkerThreads = Integer.parseInt(configuration.getProperty(NUM_WORKER_THREADS, "10"));

// If httpsPort is specified, parse all SSL properties
Expand All @@ -130,6 +137,8 @@ public void configure(Properties configuration) throws MetricsProviderLifeCycleE
this.trustStoreType = configuration.getProperty(SSL_TRUSTSTORE_TYPE, "PKCS12");
this.needClientAuth = Boolean.parseBoolean(configuration.getProperty(SSL_NEED_CLIENT_AUTH, "true"));
this.wantClientAuth = Boolean.parseBoolean(configuration.getProperty(SSL_WANT_CLIENT_AUTH, "true"));
this.enabledProtocols = configuration.getProperty(SSL_ENABLED_PROTOCOLS);
this.cipherSuites = configuration.getProperty(SSL_ENABLED_CIPHERS);
}

// Validate that at least one port is configured.
Expand Down Expand Up @@ -232,6 +241,18 @@ private SslContextFactory.Server createSslContextFactory() {
sslContextFactory.setNeedClientAuth(this.needClientAuth);
sslContextFactory.setWantClientAuth(this.wantClientAuth);

if (enabledProtocols != null) {
LOG.debug("Setting enabled protocols: '{}'", enabledProtocols);
String[] enabledProtocolsArray = enabledProtocols.split(",");
sslContextFactory.setIncludeProtocols(enabledProtocolsArray);
}

if (cipherSuites != null) {
LOG.debug("Setting enabled cipherSuites: '{}'", cipherSuites);
String[] cipherSuitesArray = cipherSuites.split(",");
sslContextFactory.setIncludeCipherSuites(cipherSuitesArray);
}

return sslContextFactory;
}

Expand Down
Loading
Loading