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]