This is an automated email from the ASF dual-hosted git repository.
nvazquez pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new 2a4d7d9036c Fix Prometheus exporter not recognizing ::1 as IPv6
loopback (#13245)
2a4d7d9036c is described below
commit 2a4d7d9036ca72374054cf83853923e4282e6297
Author: dahn <[email protected]>
AuthorDate: Thu Nov 5 15:38:29 2026 +0100
Fix Prometheus exporter not recognizing ::1 as IPv6 loopback (#13245)
* Fix Prometheus exporter not recognizing ::1 as IPv6 loopback
Normalize IPv6 addresses using InetAddress.getHostAddress() so that
compressed forms like ::1 and full forms like 0:0:0:0:0:0:0:1 are
treated as equivalent when checking the allowed IPs list.
Fixes #6714
* prometheus: skip blank/non-IP allowlist entries, avoid DNS, cache parsed
list
---
.../metrics/PrometheusExporterServerImpl.java | 50 +++++++++++++++++++-
.../metrics/PrometheusExporterServerImplTest.java | 53 ++++++++++++++++++++++
2 files changed, 101 insertions(+), 2 deletions(-)
diff --git
a/plugins/integrations/prometheus/src/main/java/org/apache/cloudstack/metrics/PrometheusExporterServerImpl.java
b/plugins/integrations/prometheus/src/main/java/org/apache/cloudstack/metrics/PrometheusExporterServerImpl.java
index d9f25d2f577..5c51994a0ce 100644
---
a/plugins/integrations/prometheus/src/main/java/org/apache/cloudstack/metrics/PrometheusExporterServerImpl.java
+++
b/plugins/integrations/prometheus/src/main/java/org/apache/cloudstack/metrics/PrometheusExporterServerImpl.java
@@ -17,6 +17,7 @@
package org.apache.cloudstack.metrics;
import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.net.NetUtils;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
@@ -26,9 +27,15 @@ import org.apache.cloudstack.framework.config.Configurable;
import javax.inject.Inject;
import java.io.IOException;
import java.io.OutputStream;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
public class PrometheusExporterServerImpl extends ManagerBase implements
PrometheusExporterServer, Configurable {
@@ -37,6 +44,9 @@ public class PrometheusExporterServerImpl extends ManagerBase
implements Prometh
@Inject
private PrometheusExporter prometheusExporter;
+ private String allowedAddressesConfig;
+ private Set<String> allowedAddresses = Collections.emptySet();
+
private final class ExporterHandler implements HttpHandler {
private PrometheusExporter prometheusExporter;
@@ -47,11 +57,11 @@ public class PrometheusExporterServerImpl extends
ManagerBase implements Prometh
@Override
public void handle(final HttpExchange httpExchange) throws IOException
{
- final String remoteClientAddress =
httpExchange.getRemoteAddress().getAddress().toString().replace("/", "");
+ final String remoteClientAddress =
httpExchange.getRemoteAddress().getAddress().getHostAddress();
logger.debug("Prometheus exporter received client request from: "
+ remoteClientAddress);
String response = "Forbidden";
int responseCode = 403;
- if
(Arrays.asList(PrometheusExporterAllowedAddresses.value().split(",")).contains(remoteClientAddress))
{
+ if (getAllowedAddresses().contains(remoteClientAddress)) {
prometheusExporter.updateMetrics();
response = prometheusExporter.getMetrics();
responseCode = 200;
@@ -74,6 +84,42 @@ public class PrometheusExporterServerImpl extends
ManagerBase implements Prometh
}
}
+ private synchronized Set<String> getAllowedAddresses() {
+ final String config = PrometheusExporterAllowedAddresses.value();
+ if (!config.equals(allowedAddressesConfig)) {
+ allowedAddresses = normalizeAddresses(config);
+ allowedAddressesConfig = config;
+ }
+ return allowedAddresses;
+ }
+
+ /**
+ * Normalizes a comma separated list of IP addresses to their canonical
text form, so that
+ * equivalent notations (e.g. ::1 and 0:0:0:0:0:0:0:1) match the remote
client address.
+ * Blank entries and anything that is not an IP literal are ignored, so no
DNS resolution takes place.
+ */
+ protected Set<String> normalizeAddresses(final String addresses) {
+ return Arrays.stream(addresses.split(","))
+ .map(String::trim)
+ .filter(addr -> !addr.isEmpty())
+ .map(this::normalizeAddress)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ }
+
+ private String normalizeAddress(final String address) {
+ if (!NetUtils.isValidIp4(address) && !NetUtils.isValidIp6(address)) {
+ logger.warn("Ignoring invalid IP address [{}] in {}", address,
PrometheusExporterAllowedAddresses.key());
+ return null;
+ }
+ try {
+ return InetAddress.getByName(address).getHostAddress();
+ } catch (UnknownHostException e) {
+ logger.warn("Ignoring IP address [{}] in {} that could not be
parsed", address, PrometheusExporterAllowedAddresses.key(), e);
+ return null;
+ }
+ }
+
@Override
public boolean start() {
if (EnablePrometheusExporter.value()) {
diff --git
a/plugins/integrations/prometheus/src/test/java/org/apache/cloudstack/metrics/PrometheusExporterServerImplTest.java
b/plugins/integrations/prometheus/src/test/java/org/apache/cloudstack/metrics/PrometheusExporterServerImplTest.java
new file mode 100644
index 00000000000..835389f227f
--- /dev/null
+++
b/plugins/integrations/prometheus/src/test/java/org/apache/cloudstack/metrics/PrometheusExporterServerImplTest.java
@@ -0,0 +1,53 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.metrics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Set;
+
+import org.junit.Test;
+
+public class PrometheusExporterServerImplTest {
+
+ private final PrometheusExporterServerImpl server = new
PrometheusExporterServerImpl();
+
+ @Test
+ public void testNormalizeAddressesIpv6LoopbackForms() {
+ Set<String> addresses = server.normalizeAddresses("::1");
+ assertEquals(server.normalizeAddresses("0:0:0:0:0:0:0:1"), addresses);
+ assertTrue(addresses.contains("0:0:0:0:0:0:0:1"));
+ }
+
+ @Test
+ public void testNormalizeAddressesTrimsEntries() {
+ Set<String> addresses = server.normalizeAddresses(" 127.0.0.1 ,
10.1.1.1");
+ assertEquals(Set.of("127.0.0.1", "10.1.1.1"), addresses);
+ }
+
+ @Test
+ public void testNormalizeAddressesIgnoresBlankEntries() {
+ assertTrue(server.normalizeAddresses("").isEmpty());
+ assertEquals(Set.of("10.1.1.1"),
server.normalizeAddresses(",10.1.1.1,, ,"));
+ }
+
+ @Test
+ public void testNormalizeAddressesIgnoresNonIpEntries() {
+ assertEquals(Set.of("10.1.1.1"),
server.normalizeAddresses("localhost,10.1.1.1,not-an-ip"));
+ }
+}