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

tanxinyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new a17ec6771be Add authorization for metric prometheus report (#15363)
a17ec6771be is described below

commit a17ec6771beeb663487b3be1972046181eef24f0
Author: Li Yu Heng <[email protected]>
AuthorDate: Fri Apr 25 12:00:58 2025 +0800

    Add authorization for metric prometheus report (#15363)
    
    * init
    
    * add test
    
    * Tan review
    
    * add base64 & optimize test
---
 .../it/env/cluster/config/MppConfigNodeConfig.java | 12 +++
 .../it/env/cluster/config/MppDataNodeConfig.java   | 12 +++
 .../iotdb/it/env/cluster/env/AbstractEnv.java      | 13 ++-
 .../env/remote/config/RemoteConfigNodeConfig.java  | 10 +++
 .../it/env/remote/config/RemoteDataNodeConfig.java | 10 +++
 .../iotdb/it/env/remote/env/RemoteServerEnv.java   | 13 ++-
 .../java/org/apache/iotdb/itbase/env/BaseEnv.java  | 11 ++-
 .../apache/iotdb/itbase/env/ConfigNodeConfig.java  |  5 ++
 .../apache/iotdb/itbase/env/DataNodeConfig.java    |  4 +
 .../apache/iotdb/db/it/metric/IoTDBMetricIT.java   | 92 +++++++++++++++++++---
 iotdb-core/metrics/interface/pom.xml               |  5 ++
 .../apache/iotdb/metrics/config/MetricConfig.java  | 38 +++++++++
 .../metrics/config/MetricConfigDescriptor.java     | 19 +++++
 .../reporter/prometheus/PrometheusReporter.java    | 62 ++++++++++++++-
 .../conf/iotdb-system.properties.template          | 11 +++
 15 files changed, 296 insertions(+), 21 deletions(-)

diff --git 
a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppConfigNodeConfig.java
 
b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppConfigNodeConfig.java
index 62ccbb0aa4f..8e4a6def365 100644
--- 
a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppConfigNodeConfig.java
+++ 
b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppConfigNodeConfig.java
@@ -55,4 +55,16 @@ public class MppConfigNodeConfig extends MppBaseConfig 
implements ConfigNodeConf
     properties.setProperty("cn_metric_reporter_list", String.join(",", 
metricReporterTypes));
     return this;
   }
+
+  @Override
+  public ConfigNodeConfig setMetricPrometheusReporterUsername(String username) 
{
+    properties.setProperty("metric_prometheus_reporter_username", username);
+    return this;
+  }
+
+  @Override
+  public ConfigNodeConfig setMetricPrometheusReporterPassword(String password) 
{
+    properties.setProperty("metric_prometheus_reporter_password", password);
+    return this;
+  }
 }
diff --git 
a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppDataNodeConfig.java
 
b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppDataNodeConfig.java
index 19dada3d06a..5f51e486dd8 100644
--- 
a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppDataNodeConfig.java
+++ 
b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppDataNodeConfig.java
@@ -56,6 +56,18 @@ public class MppDataNodeConfig extends MppBaseConfig 
implements DataNodeConfig {
     return this;
   }
 
+  @Override
+  public DataNodeConfig setMetricPrometheusReporterUsername(String username) {
+    properties.setProperty("metric_prometheus_reporter_username", username);
+    return this;
+  }
+
+  @Override
+  public DataNodeConfig setMetricPrometheusReporterPassword(String password) {
+    properties.setProperty("metric_prometheus_reporter_password", password);
+    return this;
+  }
+
   @Override
   public DataNodeConfig setEnableRestService(boolean enableRestService) {
     properties.setProperty("enable_rest_service", 
String.valueOf(enableRestService));
diff --git 
a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
 
b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
index a255dde2291..c64159f3169 100644
--- 
a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
+++ 
b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
@@ -118,7 +118,7 @@ public abstract class AbstractEnv implements BaseEnv {
   }
 
   @Override
-  public List<String> getMetricPrometheusReporterContents() {
+  public List<String> getMetricPrometheusReporterContents(String authHeader) {
     final List<String> result = new ArrayList<>();
     // get all report content of confignodes
     for (final ConfigNodeWrapper configNode : this.configNodeWrapperList) {
@@ -128,7 +128,8 @@ public abstract class AbstractEnv implements BaseEnv {
                   + configNode.getIp()
                   + ":"
                   + configNode.getMetricPort()
-                  + "/metrics");
+                  + "/metrics",
+              authHeader);
       result.add(configNodeMetricContent);
     }
     // get all report content of datanodes
@@ -139,7 +140,8 @@ public abstract class AbstractEnv implements BaseEnv {
                   + dataNode.getIp()
                   + ":"
                   + dataNode.getMetricPort()
-                  + "/metrics");
+                  + "/metrics",
+              authHeader);
       result.add(dataNodeMetricContent);
     }
     return result;
@@ -1047,6 +1049,11 @@ public abstract class AbstractEnv implements BaseEnv {
     configNodeWrapperList.forEach(AbstractNodeWrapper::stop);
   }
 
+  @Override
+  public void shutdownForciblyAllConfigNodes() {
+    configNodeWrapperList.forEach(AbstractNodeWrapper::stopForcibly);
+  }
+
   @Override
   public ConfigNodeWrapper getConfigNodeWrapper(final int index) {
     return configNodeWrapperList.get(index);
diff --git 
a/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteConfigNodeConfig.java
 
b/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteConfigNodeConfig.java
index 33a6bc48afd..ae8645eff52 100644
--- 
a/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteConfigNodeConfig.java
+++ 
b/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteConfigNodeConfig.java
@@ -28,4 +28,14 @@ public class RemoteConfigNodeConfig implements 
ConfigNodeConfig {
   public ConfigNodeConfig setMetricReporterType(List<String> 
metricReporterTypes) {
     return this;
   }
+
+  @Override
+  public ConfigNodeConfig setMetricPrometheusReporterUsername(String username) 
{
+    return this;
+  }
+
+  @Override
+  public ConfigNodeConfig setMetricPrometheusReporterPassword(String password) 
{
+    return this;
+  }
 }
diff --git 
a/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteDataNodeConfig.java
 
b/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteDataNodeConfig.java
index b109baa8203..c273daba49e 100644
--- 
a/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteDataNodeConfig.java
+++ 
b/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteDataNodeConfig.java
@@ -28,6 +28,16 @@ public class RemoteDataNodeConfig implements DataNodeConfig {
     return this;
   }
 
+  @Override
+  public DataNodeConfig setMetricPrometheusReporterUsername(String username) {
+    return this;
+  }
+
+  @Override
+  public DataNodeConfig setMetricPrometheusReporterPassword(String password) {
+    return this;
+  }
+
   @Override
   public DataNodeConfig setEnableRestService(boolean enableRestService) {
     return this;
diff --git 
a/integration-test/src/main/java/org/apache/iotdb/it/env/remote/env/RemoteServerEnv.java
 
b/integration-test/src/main/java/org/apache/iotdb/it/env/remote/env/RemoteServerEnv.java
index f0daa951e82..c2308cfe103 100644
--- 
a/integration-test/src/main/java/org/apache/iotdb/it/env/remote/env/RemoteServerEnv.java
+++ 
b/integration-test/src/main/java/org/apache/iotdb/it/env/remote/env/RemoteServerEnv.java
@@ -112,14 +112,16 @@ public class RemoteServerEnv implements BaseEnv {
   }
 
   @Override
-  public List<String> getMetricPrometheusReporterContents() {
+  public List<String> getMetricPrometheusReporterContents(String authHeader) {
     List<String> result = new ArrayList<>();
     result.add(
         getUrlContent(
-            Config.IOTDB_HTTP_URL_PREFIX + ip_addr + ":" + 
configNodeMetricPort + "/metrics"));
+            Config.IOTDB_HTTP_URL_PREFIX + ip_addr + ":" + 
configNodeMetricPort + "/metrics",
+            authHeader));
     result.add(
         getUrlContent(
-            Config.IOTDB_HTTP_URL_PREFIX + ip_addr + ":" + dataNodeMetricPort 
+ "/metrics"));
+            Config.IOTDB_HTTP_URL_PREFIX + ip_addr + ":" + dataNodeMetricPort 
+ "/metrics",
+            authHeader));
     return result;
   }
 
@@ -392,6 +394,11 @@ public class RemoteServerEnv implements BaseEnv {
     throw new UnsupportedOperationException();
   }
 
+  @Override
+  public void shutdownForciblyAllConfigNodes() {
+    throw new UnsupportedOperationException();
+  }
+
   @Override
   public void ensureNodeStatus(List<BaseNodeWrapper> nodes, List<NodeStatus> 
targetStatus) {
     throw new UnsupportedOperationException();
diff --git 
a/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseEnv.java 
b/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseEnv.java
index 0cae6d1fa25..19dcc77dfde 100644
--- a/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseEnv.java
+++ b/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseEnv.java
@@ -34,6 +34,8 @@ import org.apache.iotdb.jdbc.Config;
 import org.apache.iotdb.jdbc.Constant;
 import org.apache.iotdb.rpc.IoTDBConnectionException;
 
+import reactor.util.annotation.Nullable;
+
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
@@ -79,11 +81,14 @@ public interface BaseEnv {
   /** Return the {@link ClusterConfig} for developers to set values before 
test. */
   ClusterConfig getConfig();
 
-  default String getUrlContent(String urlStr) {
+  default String getUrlContent(String urlStr, @Nullable String authHeader) {
     StringBuilder sb = new StringBuilder();
     try {
       URL url = new URL(urlStr);
       HttpURLConnection httpConnection = (HttpURLConnection) 
url.openConnection();
+      if (authHeader != null) {
+        httpConnection.setRequestProperty("Authorization", authHeader);
+      }
       if (httpConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
         InputStream in = httpConnection.getInputStream();
         InputStreamReader isr = new InputStreamReader(in);
@@ -105,7 +110,7 @@ public interface BaseEnv {
   }
 
   /** Return the content of prometheus */
-  List<String> getMetricPrometheusReporterContents();
+  List<String> getMetricPrometheusReporterContents(String authHeader);
 
   default Connection getConnection() throws SQLException {
     return getConnection(
@@ -243,6 +248,8 @@ public interface BaseEnv {
   /** Shutdown all existed ConfigNodes. */
   void shutdownAllConfigNodes();
 
+  void shutdownForciblyAllConfigNodes();
+
   /**
    * Ensure all the nodes being in the corresponding status.
    *
diff --git 
a/integration-test/src/main/java/org/apache/iotdb/itbase/env/ConfigNodeConfig.java
 
b/integration-test/src/main/java/org/apache/iotdb/itbase/env/ConfigNodeConfig.java
index bf7179ef702..65a5a3271fc 100644
--- 
a/integration-test/src/main/java/org/apache/iotdb/itbase/env/ConfigNodeConfig.java
+++ 
b/integration-test/src/main/java/org/apache/iotdb/itbase/env/ConfigNodeConfig.java
@@ -23,5 +23,10 @@ import java.util.List;
 
 /** This interface is used to handle properties in 
iotdb-confignode.properties. */
 public interface ConfigNodeConfig {
+
   ConfigNodeConfig setMetricReporterType(List<String> metricReporterTypes);
+
+  ConfigNodeConfig setMetricPrometheusReporterUsername(String username);
+
+  ConfigNodeConfig setMetricPrometheusReporterPassword(String password);
 }
diff --git 
a/integration-test/src/main/java/org/apache/iotdb/itbase/env/DataNodeConfig.java
 
b/integration-test/src/main/java/org/apache/iotdb/itbase/env/DataNodeConfig.java
index b8ad5c2f15b..b8c44423bf8 100644
--- 
a/integration-test/src/main/java/org/apache/iotdb/itbase/env/DataNodeConfig.java
+++ 
b/integration-test/src/main/java/org/apache/iotdb/itbase/env/DataNodeConfig.java
@@ -25,6 +25,10 @@ import java.util.List;
 public interface DataNodeConfig {
   DataNodeConfig setMetricReporterType(List<String> metricReporterTypes);
 
+  DataNodeConfig setMetricPrometheusReporterUsername(String username);
+
+  DataNodeConfig setMetricPrometheusReporterPassword(String password);
+
   DataNodeConfig setEnableRestService(boolean enableRestService);
 
   DataNodeConfig setConnectionTimeoutInMS(int connectionTimeoutInMS);
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/metric/IoTDBMetricIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/db/it/metric/IoTDBMetricIT.java
index 76e6ddce817..3e6f660d4f2 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/db/it/metric/IoTDBMetricIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/db/it/metric/IoTDBMetricIT.java
@@ -23,16 +23,19 @@ import org.apache.iotdb.it.env.EnvFactory;
 import org.apache.iotdb.it.framework.IoTDBTestRunner;
 import org.apache.iotdb.itbase.category.ClusterIT;
 import org.apache.iotdb.itbase.category.LocalStandaloneIT;
+import org.apache.iotdb.metrics.reporter.prometheus.PrometheusReporter;
 
-import org.junit.AfterClass;
+import org.junit.After;
 import org.junit.Assert;
-import org.junit.BeforeClass;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
 import java.util.Collections;
 import java.util.List;
 import java.util.regex.Pattern;
@@ -65,7 +68,13 @@ public class IoTDBMetricIT {
   private static final String VALID_LOG_STRING =
       "This line {} is invalid in prometheus line protocol";
 
-  public static boolean isValidPrometheusTextFormat(String metrics) {
+  private static final String TEST_USERNAME = "good";
+  private static final String TEST_PASSWORD = "??";
+
+  private static final String WRONG_USERNAME = "bad";
+  private static final String WRONG_PASSWORD = "!!";
+
+  private static boolean isValidPrometheusTextFormat(String metrics) {
     String[] lines = metrics.split("\\n");
     boolean valid = true;
 
@@ -107,8 +116,8 @@ public class IoTDBMetricIT {
     return Pattern.matches(TYPE_REGEX, line.trim());
   }
 
-  @BeforeClass
-  public static void setUp() throws Exception {
+  @Before
+  public void setUp() throws Exception {
     // Start ConfigNode with Prometheus reporter up
     EnvFactory.getEnv()
         .getConfig()
@@ -119,21 +128,86 @@ public class IoTDBMetricIT {
         .getConfig()
         .getDataNodeConfig()
         .setMetricReporterType(Collections.singletonList("PROMETHEUS"));
-    EnvFactory.getEnv().initClusterEnvironment();
   }
 
-  @AfterClass
-  public static void tearDown() throws Exception {
+  @After
+  public void tearDown() throws Exception {
     EnvFactory.getEnv().cleanClusterEnvironment();
   }
 
+  @Test
+  public void testPrometheusReporterWithoutAuth() {
+    EnvFactory.getEnv().initClusterEnvironment();
+
+    List<String> metricContents = 
EnvFactory.getEnv().getMetricPrometheusReporterContents(null);
+    for (String metricContent : metricContents) {
+      Assert.assertNotNull(metricContent);
+      Assert.assertNotEquals(0, metricContent.length());
+      Assert.assertTrue(isValidPrometheusTextFormat(metricContent));
+    }
+  }
+
   @Test
   public void testPrometheusReporter() {
-    List<String> metricContents = 
EnvFactory.getEnv().getMetricPrometheusReporterContents();
+    EnvFactory.getEnv()
+        .getConfig()
+        .getConfigNodeConfig()
+        .setMetricPrometheusReporterUsername(base64Encode(TEST_USERNAME))
+        .setMetricPrometheusReporterPassword(base64Encode(TEST_PASSWORD));
+    EnvFactory.getEnv()
+        .getConfig()
+        .getDataNodeConfig()
+        .setMetricPrometheusReporterUsername(base64Encode(TEST_USERNAME))
+        .setMetricPrometheusReporterPassword(base64Encode(TEST_PASSWORD));
+    EnvFactory.getEnv().initClusterEnvironment();
+
+    wrongUsernameTest();
+    wrongPasswordTest();
+    correctUsernameAndPasswordTest();
+  }
+
+  private void wrongUsernameTest() {
+    List<String> metricContents =
+        EnvFactory.getEnv()
+            .getMetricPrometheusReporterContents(
+                buildPrometheusReporterAuthHeader(WRONG_USERNAME, 
TEST_PASSWORD));
+    for (String metricContent : metricContents) {
+      Assert.assertNull(metricContent);
+    }
+  }
+
+  private void wrongPasswordTest() {
+    List<String> metricContents =
+        EnvFactory.getEnv()
+            .getMetricPrometheusReporterContents(
+                buildPrometheusReporterAuthHeader(TEST_USERNAME, 
WRONG_PASSWORD));
+    for (String metricContent : metricContents) {
+      Assert.assertNull(metricContent);
+    }
+  }
+
+  private void correctUsernameAndPasswordTest() {
+    List<String> metricContents =
+        EnvFactory.getEnv()
+            .getMetricPrometheusReporterContents(
+                buildPrometheusReporterAuthHeader(TEST_USERNAME, 
TEST_PASSWORD));
     for (String metricContent : metricContents) {
       Assert.assertNotNull(metricContent);
       Assert.assertNotEquals(0, metricContent.length());
       Assert.assertTrue(isValidPrometheusTextFormat(metricContent));
     }
   }
+
+  private String buildPrometheusReporterAuthHeader(String username, String 
password) {
+    if (username == null || username.isEmpty()) {
+      return null;
+    }
+    String raw = username + 
PrometheusReporter.DIVIDER_BETWEEN_USERNAME_AND_DIVIDER + password;
+    String base64 = 
Base64.getEncoder().encodeToString(raw.getBytes(StandardCharsets.UTF_8));
+    return PrometheusReporter.BASIC_AUTH_PREFIX + base64;
+  }
+
+  private static String base64Encode(String raw) {
+    return 
Base64.getEncoder().encodeToString(raw.getBytes(StandardCharsets.UTF_8));
+  }
 }
diff --git a/iotdb-core/metrics/interface/pom.xml 
b/iotdb-core/metrics/interface/pom.xml
index adaab00e0a1..af56f7e3722 100644
--- a/iotdb-core/metrics/interface/pom.xml
+++ b/iotdb-core/metrics/interface/pom.xml
@@ -79,6 +79,11 @@
             <groupId>io.netty</groupId>
             <artifactId>netty-transport</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-codec-http</artifactId>
+            <version>4.1.119.Final</version>
+        </dependency>
         <dependency>
             <groupId>org.reactivestreams</groupId>
             <artifactId>reactive-streams</artifactId>
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..090ef3eae2c 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
@@ -29,7 +29,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.lang.management.ManagementFactory;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Base64;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -49,6 +51,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 +133,36 @@ public class MetricConfig {
     this.prometheusReporterPort = prometheusReporterPort;
   }
 
+  public boolean prometheusNeedAuth() {
+    return prometheusReporterUsername != null && 
!prometheusReporterUsername.isEmpty();
+  }
+
+  public String getPrometheusReporterUsername() {
+    return prometheusReporterUsername;
+  }
+
+  public String getDecodedPrometheusReporterUsername() {
+    return new String(
+        Base64.getDecoder().decode(prometheusReporterUsername), 
StandardCharsets.UTF_8);
+  }
+
+  public void setPrometheusReporterUsername(String prometheusReporterUsername) 
{
+    this.prometheusReporterUsername = prometheusReporterUsername;
+  }
+
+  public String getPrometheusReporterPassword() {
+    return prometheusReporterPassword;
+  }
+
+  public String getDecodedPrometheusReporterPassword() {
+    return new String(
+        Base64.getDecoder().decode(prometheusReporterPassword), 
StandardCharsets.UTF_8);
+  }
+
+  public void setPrometheusReporterPassword(String prometheusReporterPassword) 
{
+    this.prometheusReporterPassword = prometheusReporterPassword;
+  }
+
   public IoTDBReporterConfig getIoTDBReporterConfig() {
     return iotdbReporterConfig;
   }
@@ -181,6 +217,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..2cc5c2a986d 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,18 @@ public class MetricConfigDescriptor {
                 properties,
                 isConfigNode)));
 
+    loadConfig.setPrometheusReporterUsername(
+        getPropertyWithoutPrefix(
+            "metric_prometheus_reporter_username",
+            loadConfig.getPrometheusReporterUsername(),
+            properties));
+
+    loadConfig.setPrometheusReporterPassword(
+        getPropertyWithoutPrefix(
+            "metric_prometheus_reporter_password",
+            loadConfig.getPrometheusReporterPassword(),
+            properties));
+
     IoTDBReporterConfig reporterConfig = loadConfig.getIoTDBReporterConfig();
     reporterConfig.setHost(
         getProperty(
@@ -181,6 +193,13 @@ public class MetricConfigDescriptor {
         .orElse(defaultValue);
   }
 
+  private String getPropertyWithoutPrefix(
+      String target, String defaultValue, Properties properties) {
+    return Optional.ofNullable(properties.getProperty(target, defaultValue))
+        .map(String::trim)
+        .orElse(defaultValue);
+  }
+
   private static class MetricConfigDescriptorHolder {
     private static final MetricConfigDescriptor INSTANCE = new 
MetricConfigDescriptor();
   }
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..9bc8f2d9cae 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
@@ -37,17 +37,23 @@ import org.apache.iotdb.metrics.utils.ReporterType;
 
 import io.netty.channel.ChannelOption;
 import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpResponseStatus;
 import io.netty.util.concurrent.GlobalEventExecutor;
 import org.slf4j.Logger;
 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,10 @@ public class PrometheusReporter implements Reporter {
   private final AbstractMetricManager metricManager;
   private DisposableServer httpServer;
 
+  private static final String REALM = "metrics";
+  public static final String BASIC_AUTH_PREFIX = "Basic ";
+  public static final char DIVIDER_BETWEEN_USERNAME_AND_DIVIDER = ':';
+
   public PrometheusReporter(AbstractMetricManager metricManager) {
     this.metricManager = metricManager;
   }
@@ -80,10 +90,14 @@ public class PrometheusReporter implements Reporter {
                   routes ->
                       routes.get(
                           "/metrics",
-                          (request, response) ->
-                              response
-                                  .addHeader("Content-Type", "text/plain")
-                                  .sendString(Mono.just(scrape()))))
+                          (req, res) -> {
+                            if (!authenticate(req, res)) {
+                              // authenticate not pass
+                              return Mono.empty();
+                            }
+                            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 +111,46 @@ 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_AUTH_PREFIX)) {
+      return authenticateFailed(res);
+    }
+
+    // base64 decoding
+    // base64String is expected as "Basic dXNlcjpwYXNzd29yZA=="
+    String base64String = header.substring(BASIC_AUTH_PREFIX.length());
+    // decodedString is expected as "username:password"
+    String decodedString =
+        new String(Base64.getDecoder().decode(base64String), 
StandardCharsets.UTF_8);
+    int dividerIndex = 
decodedString.indexOf(DIVIDER_BETWEEN_USERNAME_AND_DIVIDER);
+    if (dividerIndex < 0) {
+      LOGGER.warn("Unexpected auth string: {}", decodedString);
+      return authenticateFailed(res);
+    }
+
+    // check username and password
+    String username = decodedString.substring(0, dividerIndex);
+    String password = decodedString.substring(dividerIndex + 1);
+    if (!METRIC_CONFIG.getDecodedPrometheusReporterUsername().equals(username)
+        || 
!METRIC_CONFIG.getDecodedPrometheusReporterPassword().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..40b529d48d5 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
@@ -371,6 +371,17 @@ 
iot_consensus_v2_deletion_file_dir=data/datanode/system/pipe/consensus/deletion
 ### Metric Configuration
 ####################
 
+# The base64 encoded username for the prometheus port of both the ConfigNode 
and the DataNode.
+# If left unset, the prometheus port can be accessed without authentication.
+# effectiveMode: restart
+# Datatype: String
+metric_prometheus_reporter_username=
+
+# The base64 encoded password for the prometheus port of both the ConfigNode 
and the DataNode.
+# effectiveMode: restart
+# Datatype: String
+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