This is an automated email from the ASF dual-hosted git repository.

liyuheng pushed a commit to branch Working/prometheus-add-auth
in repository https://gitbox.apache.org/repos/asf/iotdb.git

commit 18db34aeb7254c029545e4137ae2eb2da860cc0e
Author: liyuheng <[email protected]>
AuthorDate: Thu Apr 17 17:43:35 2025 +0800

    init
---
 .../apache/iotdb/metrics/config/MetricConfig.java  | 26 ++++++++++
 .../metrics/config/MetricConfigDescriptor.java     |  4 ++
 .../reporter/prometheus/PrometheusReporter.java    | 57 +++++++++++++++++++---
 .../conf/iotdb-system.properties.template          |  7 +++
 4 files changed, 86 insertions(+), 8 deletions(-)

diff --git 
a/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java
 
b/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java
index 00f9cd6af9d..fcd1d8b3f14 100644
--- 
a/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java
+++ 
b/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java
@@ -49,6 +49,10 @@ public class MetricConfig {
   /** The export port for prometheus to get metrics. */
   private Integer prometheusReporterPort = 9091;
 
+  private String prometheusReporterUsername = "";
+
+  private String prometheusReporterPassword = "";
+
   /** The iotdb config for iotdb reporter to push metric data. */
   private final IoTDBReporterConfig iotdbReporterConfig = new 
IoTDBReporterConfig();
 
@@ -127,6 +131,26 @@ public class MetricConfig {
     this.prometheusReporterPort = prometheusReporterPort;
   }
 
+  public boolean prometheusNeedAuth() {
+    return !Objects.equals(prometheusReporterUsername, "") || 
!Objects.equals(prometheusReporterPassword, "");
+  }
+
+  public String getPrometheusReporterUsername() {
+    return prometheusReporterUsername;
+  }
+
+  public void setPrometheusReporterUsername(String prometheusReporterUsername) 
{
+    this.prometheusReporterUsername = prometheusReporterUsername;
+  }
+
+  public String getPrometheusReporterPassword() {
+    return prometheusReporterPassword;
+  }
+
+  public void setPrometheusReporterPassword(String prometheusReporterPassword) 
{
+    this.prometheusReporterPassword = prometheusReporterPassword;
+  }
+
   public IoTDBReporterConfig getIoTDBReporterConfig() {
     return iotdbReporterConfig;
   }
@@ -181,6 +205,8 @@ public class MetricConfig {
     metricLevel = newMetricConfig.getMetricLevel();
     asyncCollectPeriodInSecond = 
newMetricConfig.getAsyncCollectPeriodInSecond();
     prometheusReporterPort = newMetricConfig.getPrometheusReporterPort();
+    prometheusReporterUsername = 
newMetricConfig.getPrometheusReporterUsername();
+    prometheusReporterPassword = 
newMetricConfig.getPrometheusReporterPassword();
     internalReporterType = newMetricConfig.getInternalReportType();
 
     iotdbReporterConfig.copy(newMetricConfig.getIoTDBReporterConfig());
diff --git 
a/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfigDescriptor.java
 
b/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfigDescriptor.java
index 11decb83369..b881ef31bd4 100644
--- 
a/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfigDescriptor.java
+++ 
b/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfigDescriptor.java
@@ -112,6 +112,10 @@ public class MetricConfigDescriptor {
                 properties,
                 isConfigNode)));
 
+    
loadConfig.setPrometheusReporterUsername(properties.getProperty("metric_prometheus_reporter_username",
 loadConfig.getPrometheusReporterUsername()));
+
+    
loadConfig.setPrometheusReporterPassword(properties.getProperty("metric_prometheus_reporter_password",
 loadConfig.getPrometheusReporterPassword()));
+
     IoTDBReporterConfig reporterConfig = loadConfig.getIoTDBReporterConfig();
     reporterConfig.setHost(
         getProperty(
diff --git 
a/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/reporter/prometheus/PrometheusReporter.java
 
b/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/reporter/prometheus/PrometheusReporter.java
index 612c3e074f9..07ffc147600 100644
--- 
a/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/reporter/prometheus/PrometheusReporter.java
+++ 
b/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/reporter/prometheus/PrometheusReporter.java
@@ -19,6 +19,8 @@
 
 package org.apache.iotdb.metrics.reporter.prometheus;
 
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpResponseStatus;
 import org.apache.iotdb.metrics.AbstractMetricManager;
 import org.apache.iotdb.metrics.config.MetricConfig;
 import org.apache.iotdb.metrics.config.MetricConfigDescriptor;
@@ -43,11 +45,15 @@ import org.slf4j.LoggerFactory;
 import reactor.core.publisher.Mono;
 import reactor.netty.DisposableServer;
 import reactor.netty.http.server.HttpServer;
+import reactor.netty.http.server.HttpServerRequest;
+import reactor.netty.http.server.HttpServerResponse;
 
 import java.io.IOException;
 import java.io.StringWriter;
 import java.io.Writer;
+import java.nio.charset.StandardCharsets;
 import java.time.Duration;
+import java.util.Base64;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -59,6 +65,8 @@ public class PrometheusReporter implements Reporter {
   private final AbstractMetricManager metricManager;
   private DisposableServer httpServer;
 
+  private static final String REALM = "metrics";
+
   public PrometheusReporter(AbstractMetricManager metricManager) {
     this.metricManager = metricManager;
   }
@@ -76,14 +84,13 @@ public class PrometheusReporter implements Reporter {
               .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
               .channelGroup(new 
DefaultChannelGroup(GlobalEventExecutor.INSTANCE))
               .port(METRIC_CONFIG.getPrometheusReporterPort())
-              .route(
-                  routes ->
-                      routes.get(
-                          "/metrics",
-                          (request, response) ->
-                              response
-                                  .addHeader("Content-Type", "text/plain")
-                                  .sendString(Mono.just(scrape()))))
+                  .route(routes -> routes.get("/metrics", (req, res) -> {
+                    if (!authenticate(req, res)) {
+                      return Mono.empty();    // authenticate not pass
+                    }
+                    return res.header(HttpHeaderNames.CONTENT_TYPE, 
"text/plain")
+                            .sendString(Mono.just(scrape()));
+                  }))
               .bindNow();
     } catch (Throwable e) {
       // catch Throwable rather than Exception here because the code above 
might cause a
@@ -97,6 +104,40 @@ public class PrometheusReporter implements Reporter {
     return true;
   }
 
+  private boolean authenticate(HttpServerRequest req, HttpServerResponse res) {
+    if (!METRIC_CONFIG.prometheusNeedAuth()) {
+      return true;
+    }
+
+    String header = req.requestHeaders().get(HttpHeaderNames.AUTHORIZATION);
+    if (header == null || !header.startsWith("Basic ")) {
+      return authenticateFailed(res);
+    }
+
+    // base64String is expected like "Basic dXNlcjpwYXNzd29yZA=="
+    String base64String = header.substring("Basic ".length());
+    // decodedString is expected like "user:123456"
+    String decodedString = new 
String(Base64.getDecoder().decode(base64String), StandardCharsets.UTF_8);
+    int idx = decodedString.indexOf(':');
+    if (idx < 0) {
+      LOGGER.info("Unexpected auth string: {}", decodedString);
+      return authenticateFailed(res);
+    }
+
+    String username = decodedString.substring(0, idx);
+    String password  = decodedString.substring(idx + 1);
+    if (!METRIC_CONFIG.getPrometheusReporterUsername().equals(username) || 
!METRIC_CONFIG.getPrometheusReporterPassword().equals(password)) {
+      return authenticateFailed(res);
+    }
+    return true;
+  }
+
+  private boolean authenticateFailed(HttpServerResponse response) {
+    response.status(HttpResponseStatus.UNAUTHORIZED)
+            .addHeader(HttpHeaderNames.WWW_AUTHENTICATE, "Basic realm=\"" + 
REALM + "\"");
+    return false;
+  }
+
   private String scrape() {
     Writer writer = new StringWriter();
     PrometheusTextWriter prometheusTextWriter = new 
PrometheusTextWriter(writer);
diff --git 
a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
 
b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
index a7f00234c06..e85d431595e 100644
--- 
a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
+++ 
b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
@@ -394,6 +394,13 @@ cn_metric_async_collect_period=5
 # Datatype: int
 cn_metric_prometheus_reporter_port=9091
 
+# The username for the Prometheus port of both the ConfigNode and the DataNode.
+# If left unset, the Prometheus port can be accessed without authentication.
+metric_prometheus_reporter_username=
+
+# The password for the Prometheus port of both the ConfigNode and the DataNode.
+metric_prometheus_reporter_password=
+
 # The reporters of metric module to report metrics
 # If there are more than one reporter, please separate them by commas ",".
 # Options: [JMX, PROMETHEUS]

Reply via email to