From 1c3e4dd1af6d85ae30360ebcd87390b7c33156c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Paksy?= Date: Thu, 19 Mar 2026 14:55:34 +0100 Subject: [PATCH 1/3] ZOOKEEPER-5029: Port unification for PrometheusMetricsProvider --- .../prometheus/PrometheusMetricsProvider.java | 66 +++++++++++++++---- .../PrometheusHttpsMetricsProviderTest.java | 8 +-- .../PrometheusMetricsProviderConfigTest.java | 16 +++++ 3 files changed, 73 insertions(+), 17 deletions(-) diff --git a/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java b/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java index 8ae6178e0f0..af18137760e 100644 --- a/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java +++ b/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java @@ -41,6 +41,11 @@ import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException; import org.apache.zookeeper.metrics.Summary; import org.apache.zookeeper.metrics.SummarySet; +import org.apache.zookeeper.server.admin.UnifiedConnectionFactory; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; @@ -84,6 +89,7 @@ public class PrometheusMetricsProvider implements MetricsProvider { private boolean wantClientAuth = true; // Secure default private String enabledProtocols; private String cipherSuites; + private int httpVersion; // Constants for configuration public static final String HTTP_HOST = "httpHost"; @@ -101,7 +107,10 @@ public class PrometheusMetricsProvider implements MetricsProvider { 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 String HTTP_VERSION = "httpVersion"; public static final int SCAN_INTERVAL = 60 * 10; // 10 minutes + public static final int DEFAULT_HTTP_VERSION = 11; // based on HttpVersion.java in jetty + public static final int DEFAULT_STS_MAX_AGE = 1 * 24 * 60 * 60; // seconds in a day /** * Custom servlet to disable the TRACE method for security reasons. @@ -139,6 +148,7 @@ public void configure(Properties configuration) throws MetricsProviderLifeCycleE 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); + this.httpVersion = Integer.getInteger(HTTP_VERSION, DEFAULT_HTTP_VERSION); } // Validate that at least one port is configured. @@ -171,23 +181,47 @@ public void start() throws MetricsProviderLifeCycleException { int acceptors = 1; int selectors = 1; - // Configure HTTP connector if enabled - if (this.httpPort != -1) { - ServerConnector httpConnector = new ServerConnector(server, acceptors, selectors); - httpConnector.setPort(this.httpPort); - httpConnector.setHost(this.host); - server.addConnector(httpConnector); - } + ServerConnector connector = null; + + if (this.httpPort != -1 && this.httpsPort != -1 && this.httpPort == this.httpsPort) { + SecureRequestCustomizer customizer = new SecureRequestCustomizer(); + customizer.setStsMaxAge(DEFAULT_STS_MAX_AGE); + customizer.setStsIncludeSubDomains(true); + + HttpConfiguration config = new HttpConfiguration(); + config.setSecureScheme("https"); + config.addCustomizer(customizer); - // Configure HTTPS connector if enabled - if (this.httpsPort != -1) { SslContextFactory.Server sslContextFactory = createSslContextFactory(); - KeyStoreScanner keystoreScanner = new KeyStoreScanner(sslContextFactory); - keystoreScanner.setScanInterval(SCAN_INTERVAL); - server.addBean(keystoreScanner); - server.addConnector(createSslConnector(server, acceptors, selectors, sslContextFactory)); + setKeyStoreScanner(sslContextFactory); + + String nextProtocol = HttpVersion.fromVersion(httpVersion).asString(); + connector = new ServerConnector(server, + new UnifiedConnectionFactory(sslContextFactory, nextProtocol), + new HttpConnectionFactory(config)); + connector.setPort(this.httpPort); + connector.setHost(this.host); + LOG.debug("Created unified ServerConnector for host: {}, httpPort: {}", host, httpPort); + } else { + // Configure HTTP connector if enabled + if (this.httpPort != -1) { + connector = new ServerConnector(server, acceptors, selectors); + connector.setPort(this.httpPort); + connector.setHost(this.host); + LOG.debug("Created ServerConnector for host: {}, httpPort: {}", host, httpPort); + } + + // Configure HTTPS connector if enabled + if (this.httpsPort != -1) { + SslContextFactory.Server sslContextFactory = createSslContextFactory(); + setKeyStoreScanner(sslContextFactory); + connector = createSslConnector(server, acceptors, selectors, sslContextFactory); + LOG.debug("Created HTTPS ServerConnector for host: {}, httpsPort: {}", host, httpsPort); + } } + server.addConnector(connector); + // Set up the servlet context handler ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/"); @@ -207,6 +241,12 @@ public void start() throws MetricsProviderLifeCycleException { } } + private void setKeyStoreScanner(SslContextFactory.Server sslContextFactory) { + KeyStoreScanner keystoreScanner = new KeyStoreScanner(sslContextFactory); + keystoreScanner.setScanInterval(SCAN_INTERVAL); + server.addBean(keystoreScanner); + } + /** * Creates and configures the SslContextFactory for the server. * diff --git a/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/prometheus/PrometheusHttpsMetricsProviderTest.java b/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/prometheus/PrometheusHttpsMetricsProviderTest.java index c091934709f..f730b4dbe00 100644 --- a/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/prometheus/PrometheusHttpsMetricsProviderTest.java +++ b/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/prometheus/PrometheusHttpsMetricsProviderTest.java @@ -43,10 +43,10 @@ public class PrometheusHttpsMetricsProviderTest extends PrometheusMetricsTestBase { private PrometheusMetricsProvider provider; - private String httpHost = "127.0.0.1"; - private int httpsPort = 4443; - private int httpPort = 4000; - private String testDataPath = System.getProperty("test.data.dir", "src/test/resources/data"); + private final String httpHost = "127.0.0.1"; + private final int httpsPort = 4443; + private final int httpPort = 4000; + private final String testDataPath = System.getProperty("test.data.dir", "src/test/resources/data"); public void initializeProviderWithCustomConfig(Properties inputConfiguration) throws Exception { provider = new PrometheusMetricsProvider(); diff --git a/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProviderConfigTest.java b/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProviderConfigTest.java index bb24ab75d29..66a01fb95e7 100644 --- a/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProviderConfigTest.java +++ b/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProviderConfigTest.java @@ -292,4 +292,20 @@ public void checkServerTrusted(X509Certificate[] certs, String authType) { } }; } + + @Test + public void testPortUnification() throws Exception { + int unifiedPort = 5400; + Properties configuration = new Properties(); + configuration.setProperty("httpsPort", String.valueOf(unifiedPort)); + configuration.setProperty("httpPort", String.valueOf(unifiedPort)); + String testDataPath = System.getProperty("test.data.dir", "src/test/resources/data"); + configuration.setProperty("ssl.keyStore.location", testDataPath + "/ssl/server_keystore.jks"); + configuration.setProperty("ssl.keyStore.password", "testpass"); + configuration.setProperty("ssl.trustStore.location", testDataPath + "/ssl/server_truststore.jks"); + configuration.setProperty("ssl.trustStore.password", "testpass"); + PrometheusMetricsProvider provider = new PrometheusMetricsProvider(); + provider.configure(configuration); + provider.start(); + } } From 45e069910051c127ea4d419433e08ffad7417e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Paksy?= Date: Thu, 16 Apr 2026 10:57:04 +0200 Subject: [PATCH 2/3] ZOOKEEPER-5029: Changed ServerConnector log messages to INFO Reason: We should see clearly which type of server has been initialized. --- .../metrics/prometheus/PrometheusMetricsProvider.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java b/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java index af18137760e..3b2e4b7809e 100644 --- a/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java +++ b/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java @@ -201,14 +201,14 @@ public void start() throws MetricsProviderLifeCycleException { new HttpConnectionFactory(config)); connector.setPort(this.httpPort); connector.setHost(this.host); - LOG.debug("Created unified ServerConnector for host: {}, httpPort: {}", host, httpPort); + LOG.info("Created unified ServerConnector for host: {}, httpPort: {}", host, httpPort); } else { // Configure HTTP connector if enabled if (this.httpPort != -1) { connector = new ServerConnector(server, acceptors, selectors); connector.setPort(this.httpPort); connector.setHost(this.host); - LOG.debug("Created ServerConnector for host: {}, httpPort: {}", host, httpPort); + LOG.info("Created HTTP ServerConnector for host: {}, httpPort: {}", host, httpPort); } // Configure HTTPS connector if enabled @@ -216,7 +216,7 @@ public void start() throws MetricsProviderLifeCycleException { SslContextFactory.Server sslContextFactory = createSslContextFactory(); setKeyStoreScanner(sslContextFactory); connector = createSslConnector(server, acceptors, selectors, sslContextFactory); - LOG.debug("Created HTTPS ServerConnector for host: {}, httpsPort: {}", host, httpsPort); + LOG.info("Created HTTPS ServerConnector for host: {}, httpsPort: {}", host, httpsPort); } } From 1de11a1c9b15044f60ab4ba7bda063a6e57c80c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Paksy?= Date: Thu, 16 Apr 2026 11:39:34 +0200 Subject: [PATCH 3/3] ZOOKEEPER-5029: Added comments for Strict-Transport-Security header in PrometheusMetricsProvider --- .../metrics/prometheus/PrometheusMetricsProvider.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java b/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java index 3b2e4b7809e..dc7828ddb35 100644 --- a/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java +++ b/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java @@ -110,7 +110,11 @@ public class PrometheusMetricsProvider implements MetricsProvider { public static final String HTTP_VERSION = "httpVersion"; public static final int SCAN_INTERVAL = 60 * 10; // 10 minutes public static final int DEFAULT_HTTP_VERSION = 11; // based on HttpVersion.java in jetty - public static final int DEFAULT_STS_MAX_AGE = 1 * 24 * 60 * 60; // seconds in a day + /** + * The time, in seconds, that the browser should remember that a host is only to be accessed using HTTPS. + * Seconds in a day. + */ + public static final int DEFAULT_STS_MAX_AGE = 1 * 24 * 60 * 60; /** * Custom servlet to disable the TRACE method for security reasons. @@ -184,8 +188,10 @@ public void start() throws MetricsProviderLifeCycleException { ServerConnector connector = null; if (this.httpPort != -1 && this.httpsPort != -1 && this.httpPort == this.httpsPort) { + // Set Strict-Transport-Security HTTP response header. SecureRequestCustomizer customizer = new SecureRequestCustomizer(); customizer.setStsMaxAge(DEFAULT_STS_MAX_AGE); + // Strict-Transport-Security HTTP header should apply to all subdomains of the host's domain as well. customizer.setStsIncludeSubDomains(true); HttpConfiguration config = new HttpConfiguration();