From 6c7cbc640642e64915ee50f6fbc4a35d59980267 Mon Sep 17 00:00:00 2001 From: Sivarajan Narayanan Date: Thu, 5 Feb 2026 09:55:28 +0530 Subject: [PATCH 1/3] enabling https for fe-fe communication --- .../java/org/apache/doris/catalog/Env.java | 8 +- .../apache/doris/common/util/HttpURLUtil.java | 22 ++++- .../doris/common/util/InternalHttpsUtils.java | 90 +++++++++++++++++++ .../doris/httpv2/rest/manager/HttpUtils.java | 11 ++- .../org/apache/doris/master/Checkpoint.java | 10 +-- .../doris/common/util/HttpURLUtilTest.java | 80 +++++++++++++++++ 6 files changed, 205 insertions(+), 16 deletions(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/common/util/InternalHttpsUtils.java create mode 100644 fe/fe-core/src/test/java/org/apache/doris/common/util/HttpURLUtilTest.java diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java index 77fa6a0f35544a..52da9a8ff69df4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java @@ -1503,9 +1503,8 @@ protected boolean getFeNodeTypeAndNameFromHelpers() { try { // For upgrade compatibility, the host parameter name remains the same // and the new hostname parameter is added - String url = "http://" + NetUtils.getHostPortInAccessibleFormat(helperNode.getHost(), Config.http_port) - + "/role?host=" + selfNode.getHost() - + "&port=" + selfNode.getPort(); + String queryParams = "host=" + selfNode.getHost() + "&port=" + selfNode.getPort(); + String url = HttpURLUtil.buildInternalFeUrl(helperNode.getHost(), "/role", queryParams); HttpURLConnection conn = HttpURLUtil.getConnectionWithNodeIdent(url); if (conn.getResponseCode() != 200) { LOG.warn("failed to get fe node type from helper node: {}. response code: {}", helperNode, @@ -2129,8 +2128,7 @@ private void checkBeExecVersion() { protected boolean getVersionFileFromHelper(HostInfo helperNode) throws IOException { try { - String url = "http://" + NetUtils.getHostPortInAccessibleFormat(helperNode.getHost(), Config.http_port) - + "/version"; + String url = HttpURLUtil.buildInternalFeUrl(helperNode.getHost(), "/version", null); File dir = new File(this.imageDir); MetaHelper.getRemoteFile(url, HTTP_TIMEOUT_SECOND * 1000, MetaHelper.getFile(Storage.VERSION_FILE, dir)); diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/util/HttpURLUtil.java b/fe/fe-core/src/main/java/org/apache/doris/common/util/HttpURLUtil.java index 70b3e68450aa45..d802fecd3c08b3 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/common/util/HttpURLUtil.java +++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/HttpURLUtil.java @@ -19,6 +19,7 @@ import org.apache.doris.catalog.Env; import org.apache.doris.cloud.security.SecurityChecker; +import org.apache.doris.common.Config; import org.apache.doris.system.SystemInfoService.HostInfo; import com.google.common.collect.Maps; @@ -34,9 +35,12 @@ public static HttpURLConnection getConnectionWithNodeIdent(String request) throw try { SecurityChecker.getInstance().startSSRFChecking(request); URL url = new URL(request); + + if (url.getProtocol().equalsIgnoreCase("https") && Config.enable_https) { + InternalHttpsUtils.installTrustManagerForUrlConnection(); + } + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - // Must use Env.getServingEnv() instead of getCurrentEnv(), - // because here we need to obtain selfNode through the official service catalog. HostInfo selfNode = Env.getServingEnv().getSelfNode(); conn.setRequestProperty(Env.CLIENT_NODE_HOST_KEY, selfNode.getHost()); conn.setRequestProperty(Env.CLIENT_NODE_PORT_KEY, selfNode.getPort() + ""); @@ -50,12 +54,22 @@ public static HttpURLConnection getConnectionWithNodeIdent(String request) throw public static Map getNodeIdentHeaders() throws IOException { Map headers = Maps.newHashMap(); - // Must use Env.getServingEnv() instead of getCurrentEnv(), - // because here we need to obtain selfNode through the official service catalog. HostInfo selfNode = Env.getServingEnv().getSelfNode(); headers.put(Env.CLIENT_NODE_HOST_KEY, selfNode.getHost()); headers.put(Env.CLIENT_NODE_PORT_KEY, selfNode.getPort() + ""); return headers; } + public static String buildInternalFeUrl(String host, String path, String queryParams) { + String protocol = Config.enable_https ? "https" : "http"; + int port = Config.enable_https ? Config.https_port : Config.http_port; + + String url = protocol + "://" + NetUtils.getHostPortInAccessibleFormat(host, port) + path; + if (queryParams != null && !queryParams.isEmpty()) { + url += "?" + queryParams; + } + + return url; + } + } diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/util/InternalHttpsUtils.java b/fe/fe-core/src/main/java/org/apache/doris/common/util/InternalHttpsUtils.java new file mode 100644 index 00000000000000..9690e7ffa806e8 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/InternalHttpsUtils.java @@ -0,0 +1,90 @@ +// 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.doris.common.util; + +import org.apache.doris.common.Config; + +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.KeyStore; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + +/** + * SSL-aware HTTP clients for internal FE communication using MySQL SSL truststore. + */ +public class InternalHttpsUtils { + private static final Logger LOG = LogManager.getLogger(InternalHttpsUtils.class); + + public static CloseableHttpClient createValidatedHttpClient() { + try { + KeyStore trustStore = KeyStore.getInstance(Config.ssl_trust_store_type); + try (InputStream stream = Files.newInputStream( + Paths.get(Config.mysql_ssl_default_ca_certificate))) { + trustStore.load(stream, Config.mysql_ssl_default_ca_certificate_password.toCharArray()); + } + + TrustManagerFactory tmf = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(trustStore); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, tmf.getTrustManagers(), null); + + SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(sslContext); + + return HttpClients.custom() + .setSSLSocketFactory(sslFactory) + .build(); + } catch (Exception e) { + LOG.error("Failed to create SSL-aware HTTP client using truststore: {}", + Config.mysql_ssl_default_ca_certificate, e); + throw new RuntimeException("Failed to create SSL-aware HTTP client", e); + } + } + + public static void installTrustManagerForUrlConnection() { + try { + KeyStore trustStore = KeyStore.getInstance(Config.ssl_trust_store_type); + try (InputStream stream = Files.newInputStream( + Paths.get(Config.mysql_ssl_default_ca_certificate))) { + trustStore.load(stream, Config.mysql_ssl_default_ca_certificate_password.toCharArray()); + } + + TrustManagerFactory tmf = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(trustStore); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, tmf.getTrustManagers(), null); + + javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); + } catch (Exception e) { + LOG.error("Failed to install trust manager for URLConnection using truststore: {}", + Config.mysql_ssl_default_ca_certificate, e); + throw new RuntimeException("Failed to install trust manager for URLConnection", e); + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/manager/HttpUtils.java b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/manager/HttpUtils.java index 694e55729526b7..4e3954ce317d75 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/manager/HttpUtils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/manager/HttpUtils.java @@ -20,7 +20,7 @@ import org.apache.doris.catalog.Env; import org.apache.doris.common.Config; import org.apache.doris.common.Pair; -import org.apache.doris.common.util.Util; +import org.apache.doris.common.util.InternalHttpsUtils; import org.apache.doris.httpv2.entity.ResponseBody; import org.apache.doris.persist.gson.GsonUtils; import org.apache.doris.system.Frontend; @@ -130,7 +130,14 @@ public static CloseableHttpClient getHttpClient() { } private static String executeRequest(HttpRequestBase request) throws IOException { - CloseableHttpClient client = getHttpClient(); + CloseableHttpClient client; + + if (request.getURI().getScheme().equalsIgnoreCase("https") && Config.enable_https) { + client = InternalHttpsUtils.createValidatedHttpClient(); + } else { + client = HttpClientBuilder.create().build(); + } + return client.execute(request, httpResponse -> EntityUtils.toString(httpResponse.getEntity())); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/master/Checkpoint.java b/fe/fe-core/src/main/java/org/apache/doris/master/Checkpoint.java index ec4126aa86d3b6..119741c291dbab 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/master/Checkpoint.java +++ b/fe/fe-core/src/main/java/org/apache/doris/master/Checkpoint.java @@ -207,10 +207,10 @@ public synchronized void doCheckpoint() throws CheckpointException { // skip master itself continue; } - int port = Config.http_port; + int port = Config.enable_https ? Config.https_port : Config.http_port; - String url = "http://" + NetUtils.getHostPortInAccessibleFormat(host, port) + "/put?version=" + replayedJournalId - + "&port=" + port; + String queryParams = "version=" + replayedJournalId + "&port=" + port; + String url = HttpURLUtil.buildInternalFeUrl(host, "/put", queryParams); LOG.info("Put image:{}", url); try { @@ -264,7 +264,6 @@ private void deleteOldJournalsAndImages(int successPushed, int otherNodesCount, // skip master itself continue; } - int port = Config.http_port; String idURL; HttpURLConnection conn = null; try { @@ -274,7 +273,7 @@ private void deleteOldJournalsAndImages(int successPushed, int otherNodesCount, * any non-master node's current replayed journal id. otherwise, * this lagging node can never get the deleted journal. */ - idURL = "http://" + NetUtils.getHostPortInAccessibleFormat(host, port) + "/journal_id"; + idURL = HttpURLUtil.buildInternalFeUrl(host, "/journal_id", null); conn = HttpURLUtil.getConnectionWithNodeIdent(idURL); conn.setConnectTimeout(CONNECT_TIMEOUT_SECOND * 1000); conn.setReadTimeout(READ_TIMEOUT_SECOND * 1000); @@ -284,6 +283,7 @@ private void deleteOldJournalsAndImages(int successPushed, int otherNodesCount, minOtherNodesJournalId = id; } } catch (Throwable e) { + int port = Config.enable_https ? Config.https_port : Config.http_port; throw new CheckpointException(String.format("Exception when getting current replayed" + " journal id. host=%s, port=%d", host, port), e); } finally { diff --git a/fe/fe-core/src/test/java/org/apache/doris/common/util/HttpURLUtilTest.java b/fe/fe-core/src/test/java/org/apache/doris/common/util/HttpURLUtilTest.java new file mode 100644 index 00000000000000..5bba968b49af41 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/common/util/HttpURLUtilTest.java @@ -0,0 +1,80 @@ +// 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.doris.common.util; + +import org.apache.doris.common.Config; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +public class HttpURLUtilTest { + + @After + public void tearDown() { + Config.enable_https = false; + Config.http_port = 8030; + Config.https_port = 8050; + } + + @Test + public void testBuildInternalFeUrlHttp() { + Config.enable_https = false; + Config.http_port = 8030; + + String url = HttpURLUtil.buildInternalFeUrl("192.168.1.10", "/put", "version=123&port=8030"); + Assert.assertEquals("http://192.168.1.10:8030/put?version=123&port=8030", url); + } + + @Test + public void testBuildInternalFeUrlHttps() { + Config.enable_https = true; + Config.https_port = 8050; + + String url = HttpURLUtil.buildInternalFeUrl("192.168.1.10", "/put", "version=123&port=8050"); + Assert.assertEquals("https://192.168.1.10:8050/put?version=123&port=8050", url); + } + + @Test + public void testBuildInternalFeUrlNoQueryParams() { + Config.enable_https = false; + Config.http_port = 8030; + + String url = HttpURLUtil.buildInternalFeUrl("192.168.1.10", "/journal_id", null); + Assert.assertEquals("http://192.168.1.10:8030/journal_id", url); + } + + @Test + public void testBuildInternalFeUrlEmptyQueryParams() { + Config.enable_https = false; + Config.http_port = 8030; + + String url = HttpURLUtil.buildInternalFeUrl("192.168.1.10", "/version", ""); + Assert.assertEquals("http://192.168.1.10:8030/version", url); + } + + @Test + public void testBuildInternalFeUrlHttpsWithIPv6() { + Config.enable_https = true; + Config.https_port = 8050; + + String url = HttpURLUtil.buildInternalFeUrl("fe80::1", "/role", "host=fe80::2&port=9010"); + Assert.assertTrue(url.startsWith("https://")); + Assert.assertTrue(url.contains("/role?host=fe80::2&port=9010")); + } +} From f0ddb7c10fd7f5bb26cfb60cc6dfcc9f1abdfce1 Mon Sep 17 00:00:00 2001 From: Sivarajan Narayanan Date: Thu, 5 Feb 2026 14:58:36 +0530 Subject: [PATCH 2/3] fix multiple endpoints fetching on http --- .../main/java/org/apache/doris/catalog/Env.java | 8 +++----- .../doris/common/util/InternalHttpsUtils.java | 17 ++++++++++++++++- .../apache/doris/httpv2/meta/MetaService.java | 5 ++--- .../org/apache/doris/master/Checkpoint.java | 1 - 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java index 52da9a8ff69df4..95b728e79eddd1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java @@ -1406,8 +1406,7 @@ protected void getClusterIdAndRole() throws IOException { getClusterIdFromStorage(storage); token = storage.getToken(); try { - String url = "http://" + NetUtils - .getHostPortInAccessibleFormat(rightHelperNode.getHost(), Config.http_port) + "/check"; + String url = HttpURLUtil.buildInternalFeUrl(rightHelperNode.getHost(), "/check", null); HttpURLConnection conn = HttpURLUtil.getConnectionWithNodeIdent(url); conn.setConnectTimeout(2 * 1000); conn.setReadTimeout(2 * 1000); @@ -2147,8 +2146,7 @@ protected void getNewImage(HostInfo helperNode) throws IOException { localImageVersion = storage.getLatestImageSeq(); try { - String hostPort = NetUtils.getHostPortInAccessibleFormat(helperNode.getHost(), Config.http_port); - String infoUrl = "http://" + hostPort + "/info"; + String infoUrl = HttpURLUtil.buildInternalFeUrl(helperNode.getHost(), "/info", null); ResponseBody responseBody = MetaHelper .doGet(infoUrl, HTTP_TIMEOUT_SECOND * 1000, StorageInfo.class); if (responseBody.getCode() != RestApiStatusCode.OK.code) { @@ -2158,7 +2156,7 @@ protected void getNewImage(HostInfo helperNode) throws IOException { StorageInfo info = responseBody.getData(); long version = info.getImageSeq(); if (version > localImageVersion) { - String url = "http://" + hostPort + "/image?version=" + version; + String url = HttpURLUtil.buildInternalFeUrl(helperNode.getHost(), "/image", "version=" + version); String filename = Storage.IMAGE + "." + version; File dir = new File(this.imageDir); MetaHelper.getRemoteFile(url, Config.sync_image_timeout_second * 1000, diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/util/InternalHttpsUtils.java b/fe/fe-core/src/main/java/org/apache/doris/common/util/InternalHttpsUtils.java index 9690e7ffa806e8..b7009f8afcf30f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/common/util/InternalHttpsUtils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/InternalHttpsUtils.java @@ -19,6 +19,7 @@ import org.apache.doris.common.Config; +import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -34,6 +35,17 @@ /** * SSL-aware HTTP clients for internal FE communication using MySQL SSL truststore. + * + * Security Model: + * - Validates certificates against configured CA truststore (mysql_ssl_default_ca_certificate) + * - Hostname verification is DISABLED to support IP-based FE communication + * - This is safe for internal cluster communication because: + * 1. All endpoints enforce checkFromValidFe() - only registered FE nodes can connect + * 2. FE cluster is assumed to be on trusted network + * 3. Traffic is encrypted and authenticated via certificate validation + * + * This approach is similar to other distributed systems (Kafka, Elasticsearch, Cassandra) + * where inter-node SSL communication disables hostname verification for operational flexibility. */ public class InternalHttpsUtils { private static final Logger LOG = LogManager.getLogger(InternalHttpsUtils.class); @@ -53,7 +65,9 @@ public static CloseableHttpClient createValidatedHttpClient() { SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); - SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(sslContext); + SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory( + sslContext, + NoopHostnameVerifier.INSTANCE); return HttpClients.custom() .setSSLSocketFactory(sslFactory) @@ -81,6 +95,7 @@ public static void installTrustManagerForUrlConnection() { sslContext.init(null, tmf.getTrustManagers(), null); javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); + javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); } catch (Exception e) { LOG.error("Failed to install trust manager for URLConnection using truststore: {}", Config.mysql_ssl_default_ca_certificate, e); diff --git a/fe/fe-core/src/main/java/org/apache/doris/httpv2/meta/MetaService.java b/fe/fe-core/src/main/java/org/apache/doris/httpv2/meta/MetaService.java index 6a8bd71facaa79..04968eda8bdbde 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/httpv2/meta/MetaService.java +++ b/fe/fe-core/src/main/java/org/apache/doris/httpv2/meta/MetaService.java @@ -20,7 +20,7 @@ import org.apache.doris.catalog.Env; import org.apache.doris.common.Config; import org.apache.doris.common.DdlException; -import org.apache.doris.common.util.NetUtils; +import org.apache.doris.common.util.HttpURLUtil; import org.apache.doris.ha.FrontendNodeType; import org.apache.doris.httpv2.entity.ResponseEntityBuilder; import org.apache.doris.httpv2.rest.RestBaseController; @@ -163,8 +163,7 @@ public Object put(HttpServletRequest request, HttpServletResponse response) thro clientHost, clientPort, machine, portStr); clientHost = Strings.isNullOrEmpty(clientHost) ? machine : clientHost; - String url = "http://" + NetUtils.getHostPortInAccessibleFormat(clientHost, Integer.valueOf(portStr)) - + "/image?version=" + versionStr; + String url = HttpURLUtil.buildInternalFeUrl(clientHost, "/image", "version=" + versionStr); String filename = Storage.IMAGE + "." + versionStr; File dir = new File(Env.getCurrentEnv().getImageDir()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/master/Checkpoint.java b/fe/fe-core/src/main/java/org/apache/doris/master/Checkpoint.java index 119741c291dbab..41386aae433743 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/master/Checkpoint.java +++ b/fe/fe-core/src/main/java/org/apache/doris/master/Checkpoint.java @@ -23,7 +23,6 @@ import org.apache.doris.common.FeConstants; import org.apache.doris.common.util.HttpURLUtil; import org.apache.doris.common.util.MasterDaemon; -import org.apache.doris.common.util.NetUtils; import org.apache.doris.httpv2.entity.ResponseBody; import org.apache.doris.httpv2.rest.RestApiStatusCode; import org.apache.doris.metric.MetricRepo; From 4fdf6dd701e344957b8dfddcb803fc9433d5bdfa Mon Sep 17 00:00:00 2001 From: Sivarajan Narayanan Date: Thu, 5 Feb 2026 20:07:03 +0530 Subject: [PATCH 3/3] fix sni blocker --- .../WebServerFactoryCustomizerConfig.java | 17 +++++++++++++++++ .../org/apache/doris/master/MetaHelper.java | 10 ++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/httpv2/config/WebServerFactoryCustomizerConfig.java b/fe/fe-core/src/main/java/org/apache/doris/httpv2/config/WebServerFactoryCustomizerConfig.java index fc24bf1bd27884..6d34335cb16590 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/httpv2/config/WebServerFactoryCustomizerConfig.java +++ b/fe/fe-core/src/main/java/org/apache/doris/httpv2/config/WebServerFactoryCustomizerConfig.java @@ -25,6 +25,7 @@ import org.eclipse.jetty.ee10.websocket.server.config.JettyWebSocketServletContainerInitializer; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.ServerConnector; import org.springframework.boot.web.embedded.jetty.ConfigurableJettyWebServerFactory; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; @@ -59,6 +60,10 @@ public void customize(ConfigurableJettyWebServerFactory factory) { factory.addServerCustomizers(server -> { if (server.getConnectors() != null && server.getConnectors().length > 0) { + if (!(server.getConnectors()[0] instanceof ServerConnector)) { + LOG.warn("First connector is not a ServerConnector, cannot configure SNI"); + return; + } ServerConnector existingConnector = (ServerConnector) server.getConnectors()[0]; HttpConnectionFactory httpFactory = existingConnector.getConnectionFactory(HttpConnectionFactory.class); @@ -66,6 +71,18 @@ public void customize(ConfigurableJettyWebServerFactory factory) { HttpConfiguration httpConfig = httpFactory.getHttpConfiguration(); httpConfig.setSecurePort(Config.https_port); httpConfig.setSecureScheme("https"); + + // Disable SNI host checking to allow IP-based connections + // Safe because checkFromValidFe() validates only registered FE nodes can connect + SecureRequestCustomizer secureCustomizer = + httpConfig.getCustomizer(SecureRequestCustomizer.class); + if (secureCustomizer == null) { + secureCustomizer = new SecureRequestCustomizer(); + httpConfig.addCustomizer(secureCustomizer); + } + secureCustomizer.setSniHostCheck(false); + secureCustomizer.setSniRequired(false); + LOG.info("Disabled SNI host checking for IP-based HTTPS connections"); } } }); diff --git a/fe/fe-core/src/main/java/org/apache/doris/master/MetaHelper.java b/fe/fe-core/src/main/java/org/apache/doris/master/MetaHelper.java index 96e7375679150d..cdb963610a4a9b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/master/MetaHelper.java +++ b/fe/fe-core/src/main/java/org/apache/doris/master/MetaHelper.java @@ -148,10 +148,12 @@ private static void checkFile(File file) throws IOException { } public static ResponseBody doGet(String url, int timeout, Class clazz) throws IOException { - Map headers = HttpURLUtil.getNodeIdentHeaders(); - LOG.info("meta helper, url: {}, timeout{}, headers: {}", url, timeout, headers); - String response = HttpUtils.doGet(url, headers, timeout); - return parseResponse(response, clazz); + String response = HttpUtils.doGet(url, HttpURLUtil.getNodeIdentHeaders(), timeout); + try { + return parseResponse(response, clazz); + } catch (Exception e) { + throw new IOException("Failed to parse response from " + url + ". Response: " + response, e); + } } // download file from remote node