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

gaborgsomogyi pushed a commit to branch release-2.3
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/release-2.3 by this push:
     new bc5c03c9fbf [FLINK-40727][security] Support comma-separated 
security.ssl.protocol and fix internal SSL hostname verification regression
bc5c03c9fbf is described below

commit bc5c03c9fbfb5fbca889c8de874bc66013fc2bf5
Author: Gabor Somogyi <[email protected]>
AuthorDate: Thu Sep 24 13:21:40 2026 +0200

    [FLINK-40727][security] Support comma-separated security.ssl.protocol and 
fix internal SSL hostname verification regression
---
 .../generated/security_configuration.html          |   2 +-
 .../shortcodes/generated/security_ssl_section.html |   2 +-
 .../flink/configuration/SecurityOptions.java       |  15 +-
 flink-end-to-end-tests/test-scripts/common_ssl.sh  |   5 +
 .../test-scripts/test_file_sink.sh                 |  85 ++++--
 .../runtime/rpc/pekko/CustomSSLEngineProvider.java |  61 +++-
 .../apache/flink/runtime/rpc/pekko/PekkoUtils.java |   2 +-
 .../rpc/pekko/CustomSSLEngineProviderTest.java     | 181 ++++++++++++
 .../flink-rpc-akka/src/test/resources/rpc.keystore | Bin 0 -> 2576 bytes
 .../src/test/resources/rpc.truststore              | Bin 0 -> 1110 bytes
 .../org/apache/flink/runtime/net/SSLUtils.java     |   7 +
 .../org/apache/flink/runtime/net/SSLUtilsTest.java | 311 +++++++++++++++++++++
 tools/azure-pipelines/e2e-template.yml             |  17 +-
 13 files changed, 651 insertions(+), 37 deletions(-)

diff --git a/docs/layouts/shortcodes/generated/security_configuration.html 
b/docs/layouts/shortcodes/generated/security_configuration.html
index 9bd0e39823b..c86660b6051 100644
--- a/docs/layouts/shortcodes/generated/security_configuration.html
+++ b/docs/layouts/shortcodes/generated/security_configuration.html
@@ -174,7 +174,7 @@
             <td><h5>security.ssl.protocol</h5></td>
             <td style="word-wrap: break-word;">"TLSv1.2"</td>
             <td>String</td>
-            <td>The SSL protocol version to be supported for the ssl 
transport. Note that it doesn’t support comma separated list.</td>
+            <td>The comma separated list of SSL protocol versions to be 
supported for the ssl transport, e.g. <code 
class="highlighter-rouge">TLSv1.2,TLSv1.3</code>. The highest protocol version 
supported by both communication endpoints and for which a matching cipher suite 
is available (see <code 
class="highlighter-rouge">security.ssl.algorithms</code>) is negotiated; the 
connection fails only if none of the listed protocols has a matching cipher 
suite.</td>
         </tr>
         <tr>
             <td><h5>security.ssl.provider</h5></td>
diff --git a/docs/layouts/shortcodes/generated/security_ssl_section.html 
b/docs/layouts/shortcodes/generated/security_ssl_section.html
index ab9f337500f..7df25d3fd63 100644
--- a/docs/layouts/shortcodes/generated/security_ssl_section.html
+++ b/docs/layouts/shortcodes/generated/security_ssl_section.html
@@ -72,7 +72,7 @@
             <td><h5>security.ssl.protocol</h5></td>
             <td style="word-wrap: break-word;">"TLSv1.2"</td>
             <td>String</td>
-            <td>The SSL protocol version to be supported for the ssl 
transport. Note that it doesn’t support comma separated list.</td>
+            <td>The comma separated list of SSL protocol versions to be 
supported for the ssl transport, e.g. <code 
class="highlighter-rouge">TLSv1.2,TLSv1.3</code>. The highest protocol version 
supported by both communication endpoints and for which a matching cipher suite 
is available (see <code 
class="highlighter-rouge">security.ssl.algorithms</code>) is negotiated; the 
connection fails only if none of the listed protocols has a matching cipher 
suite.</td>
         </tr>
         <tr>
             <td><h5>security.ssl.rest.authentication-enabled</h5></td>
diff --git 
a/flink-core/src/main/java/org/apache/flink/configuration/SecurityOptions.java 
b/flink-core/src/main/java/org/apache/flink/configuration/SecurityOptions.java
index 7ec79f0c80a..d00bb43711b 100644
--- 
a/flink-core/src/main/java/org/apache/flink/configuration/SecurityOptions.java
+++ 
b/flink-core/src/main/java/org/apache/flink/configuration/SecurityOptions.java
@@ -493,15 +493,24 @@ public class SecurityOptions {
 
     // ------------------------ ssl parameters --------------------------------
 
-    /** SSL protocol version to be supported. */
+    /** SSL protocol version(s) to be supported. */
     @Documentation.Section(Documentation.Sections.SECURITY_SSL)
     public static final ConfigOption<String> SSL_PROTOCOL =
             key("security.ssl.protocol")
                     .stringType()
                     .defaultValue("TLSv1.2")
                     .withDescription(
-                            "The SSL protocol version to be supported for the 
ssl transport. Note that it doesn’t"
-                                    + " support comma separated list.");
+                            Description.builder()
+                                    .text(
+                                            "The comma separated list of SSL 
protocol versions to be supported for"
+                                                    + " the ssl transport, 
e.g. %s. The highest protocol version"
+                                                    + " supported by both 
communication endpoints and for which a"
+                                                    + " matching cipher suite 
is available (see %s) is negotiated;"
+                                                    + " the connection fails 
only if none of the listed protocols"
+                                                    + " has a matching cipher 
suite.",
+                                            code("TLSv1.2,TLSv1.3"),
+                                            code("security.ssl.algorithms"))
+                                    .build());
 
     /**
      * The standard SSL algorithms to be supported.
diff --git a/flink-end-to-end-tests/test-scripts/common_ssl.sh 
b/flink-end-to-end-tests/test-scripts/common_ssl.sh
index 87219c68352..b04aea6d6fa 100644
--- a/flink-end-to-end-tests/test-scripts/common_ssl.sh
+++ b/flink-end-to-end-tests/test-scripts/common_ssl.sh
@@ -79,6 +79,11 @@ function _set_conf_ssl_helper {
 
     if [ "${provider}" = "OPENSSL" -a "${provider_lib}" = "dynamic" ]; then
         cp $FLINK_DIR/opt/flink-shaded-netty-tcnative-dynamic-*.jar 
$FLINK_DIR/lib/
+        # if the CI prepared a newer OpenSSL than the system one (see 
e2e-template.yml), prefer it;
+        # local/developer runs without that variable set keep using the system 
OpenSSL as before
+        if [ -n "${FLINK_E2E_OPENSSL32_LIB:-}" ]; then
+            export 
LD_LIBRARY_PATH="${FLINK_E2E_OPENSSL32_LIB}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
+        fi
     elif [ "${provider}" = "OPENSSL" -a "${provider_lib}" = "static" ]; then
         # Flink is not providing the statically-linked library because of 
potential licensing issues
         # -> we need to build it ourselves
diff --git a/flink-end-to-end-tests/test-scripts/test_file_sink.sh 
b/flink-end-to-end-tests/test-scripts/test_file_sink.sh
index 47fba1d0c90..1d6715752e5 100755
--- a/flink-end-to-end-tests/test-scripts/test_file_sink.sh
+++ b/flink-end-to-end-tests/test-scripts/test_file_sink.sh
@@ -22,15 +22,6 @@ SINK_TO_TEST="${2:-"StreamingFileSink"}"
 
 source "$(dirname "$0")"/common.sh
 
-# LOCAL_JOB_OUTPUT_PATH is a local folder that can be used as a download 
folder for remote data
-# the helper functions will access this folder
-RANDOM_PREFIX="temp/test_file_sink-$(uuidgen)"
-LOCAL_JOB_OUTPUT_PATH="$TEST_DATA_DIR/${RANDOM_PREFIX}"
-mkdir -p "${LOCAL_JOB_OUTPUT_PATH}"
-
-# JOB_OUTPUT_PATH is the location where the job writes its data to
-JOB_OUTPUT_PATH="${LOCAL_JOB_OUTPUT_PATH}"
-
 ###################################
 # Get all lines in part files and sort them numerically.
 #
@@ -65,12 +56,6 @@ elif [ "${OUT_TYPE}" == "s3" ]; then
   source "$(dirname "$0")"/common_s3_minio.sh
   s3_setup hadoop
 
-  # overwrites JOB_OUTPUT_PATH to point to S3
-  S3_DATA_PREFIX="${RANDOM_PREFIX}"
-  S3_CHECKPOINT_PREFIX="${RANDOM_PREFIX}-chk"
-  JOB_OUTPUT_PATH="s3://$IT_CASE_S3_BUCKET/${S3_DATA_PREFIX}"
-  set_config_key "execution.checkpointing.dir" 
"s3://$IT_CASE_S3_BUCKET/${S3_CHECKPOINT_PREFIX}"
-
   # overwrites implementation for local runs
   function get_complete_result {
     # copies the data from S3 to the local LOCAL_JOB_OUTPUT_PATH
@@ -85,33 +70,29 @@ elif [ "${OUT_TYPE}" == "s3" ]; then
     s3_get_number_of_lines_by_prefix "${S3_DATA_PREFIX}" "part-"
   }
 
-  # make sure we delete the file at the end
+  # make sure we delete the data of the current OpenSSL linkage iteration; 
also registered as a
+  # safety net for whichever iteration is active if the script exits 
unexpectedly
   function out_cleanup {
     s3_delete_by_full_path_prefix "${S3_DATA_PREFIX}"
     s3_delete_by_full_path_prefix "${S3_CHECKPOINT_PREFIX}"
   }
-
   on_exit out_cleanup
 else
   echo "[ERROR] Unknown out type: ${OUT_TYPE}"
   exit 1
 fi
 
-# randomly set up openSSL with dynamically/statically linked libraries
-OPENSSL_LINKAGE=$(if (( RANDOM % 2 )) ; then echo "dynamic"; else echo 
"static"; fi)
-echo "Executing test with ${OPENSSL_LINKAGE} openSSL linkage (random selection 
between 'dynamic' and 'static')"
-
-# Temporarily disable SSL in test_file_sink (see FLINK-39002 )
-#set_conf_ssl "mutual" "OPENSSL" "${OPENSSL_LINKAGE}"
-# set_conf_ssl moves netty libraries into FLINK_DIR which we want to rollback 
at the end of the test run
-on_exit rollback_openssl_lib
-
 set_config_key "metrics.fetcher.update-interval" "2000"
 # this test relies on global failovers
 set_config_key "jobmanager.execution.failover-strategy" "full"
 
 
TEST_PROGRAM_JAR="${END_TO_END_DIR}/flink-file-sink-test/target/FileSinkProgram.jar"
 
+# set_conf_ssl moves netty libraries into FLINK_DIR; rolled back explicitly 
between iterations,
+# and registered here as a safety net for the currently active iteration if 
the script exits
+# unexpectedly
+on_exit rollback_openssl_lib
+
 ###################################
 # Waits until a number of values have been written within a timeout.
 # If the timeout expires, exit with return code 1.
@@ -202,5 +183,53 @@ function run_file_sink_test {
   check_result_hash "File Streaming Sink" "$TEST_DATA_DIR/complete_result" 
"6727342fdd3aae2129e61fc8f433fb6f"
 }
 
-# usual runtime is ~6 minutes
-run_test_with_timeout 900 run_file_sink_test
+###################################
+# Runs the file sink test once, with the given OpenSSL linkage.
+#
+# Globals:
+#   TEST_DATA_DIR, OUT_TYPE, IT_CASE_S3_BUCKET
+# Arguments:
+#   $1: OpenSSL linkage, 'dynamic' or 'static'
+# Returns:
+#   None
+###################################
+function run_file_sink_test_with_linkage {
+  local openssl_linkage=$1
+
+  echo "Executing test with ${openssl_linkage} openSSL linkage"
+
+  # LOCAL_JOB_OUTPUT_PATH is a local folder that can be used as a download 
folder for remote data
+  # the helper functions will access this folder
+  local random_prefix="temp/test_file_sink-${openssl_linkage}-$(uuidgen)"
+  LOCAL_JOB_OUTPUT_PATH="$TEST_DATA_DIR/${random_prefix}"
+  mkdir -p "${LOCAL_JOB_OUTPUT_PATH}"
+
+  # JOB_OUTPUT_PATH is the location where the job writes its data to
+  JOB_OUTPUT_PATH="${LOCAL_JOB_OUTPUT_PATH}"
+
+  if [ "${OUT_TYPE}" == "s3" ]; then
+    # overwrites JOB_OUTPUT_PATH to point to S3
+    S3_DATA_PREFIX="${random_prefix}"
+    S3_CHECKPOINT_PREFIX="${random_prefix}-chk"
+    JOB_OUTPUT_PATH="s3://$IT_CASE_S3_BUCKET/${S3_DATA_PREFIX}"
+    set_config_key "execution.checkpointing.dir" 
"s3://$IT_CASE_S3_BUCKET/${S3_CHECKPOINT_PREFIX}"
+  fi
+
+  set_conf_ssl "mutual" "OPENSSL" "${openssl_linkage}"
+
+  run_test_with_timeout 900 run_file_sink_test
+
+  stop_cluster
+  # set_conf_ssl moves netty libraries into FLINK_DIR which we want to 
rollback before the next
+  # linkage is set up
+  rollback_openssl_lib
+
+  if [ "${OUT_TYPE}" == "s3" ]; then
+    out_cleanup
+  fi
+}
+
+# usual runtime is ~6 minutes per linkage
+for openssl_linkage in dynamic static; do
+  run_file_sink_test_with_linkage "${openssl_linkage}"
+done
diff --git 
a/flink-rpc/flink-rpc-akka/src/main/java/org/apache/flink/runtime/rpc/pekko/CustomSSLEngineProvider.java
 
b/flink-rpc/flink-rpc-akka/src/main/java/org/apache/flink/runtime/rpc/pekko/CustomSSLEngineProvider.java
index e2ea1801ea8..025db2b929c 100644
--- 
a/flink-rpc/flink-rpc-akka/src/main/java/org/apache/flink/runtime/rpc/pekko/CustomSSLEngineProvider.java
+++ 
b/flink-rpc/flink-rpc-akka/src/main/java/org/apache/flink/runtime/rpc/pekko/CustomSSLEngineProvider.java
@@ -24,6 +24,8 @@ import org.apache.pekko.actor.ActorSystem;
 import org.apache.pekko.remote.RemoteTransportException;
 import org.apache.pekko.remote.transport.netty.ConfigSSLEngineProvider;
 
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 
@@ -36,7 +38,16 @@ import java.security.KeyStore;
 import java.util.List;
 
 /**
- * Extension of the {@link ConfigSSLEngineProvider} to use a {@link 
FingerprintTrustManagerFactory}.
+ * Extension of the {@link ConfigSSLEngineProvider} to use a {@link 
FingerprintTrustManagerFactory}
+ * and to support a comma-separated protocol list in {@code security.protocol}.
+ *
+ * <p>{@link ConfigSSLEngineProvider}'s own engine creation reads {@code 
security.protocol} as a
+ * single string and passes it unsplit to both {@code 
SSLContext.getInstance(String)} and {@code
+ * SSLEngine#setEnabledProtocols(String[])}, so a comma-separated value breaks 
it. Its {@code
+ * sslContext} and {@code createSSLEngine} members are {@code private} and 
cannot be overridden, so
+ * {@link #createServerSSLEngine()} and {@link #createClientSSLEngine()} are 
reimplemented here
+ * instead, splitting the protocol list the same way {@code SSLUtils} does for 
the data-plane and
+ * REST SSL contexts.
  */
 @SuppressWarnings("deprecation")
 public class CustomSSLEngineProvider extends ConfigSSLEngineProvider {
@@ -45,6 +56,11 @@ public class CustomSSLEngineProvider extends 
ConfigSSLEngineProvider {
     private final List<String> sslCertFingerprints;
     private final String sslKeyStoreType;
     private final String sslTrustStoreType;
+    private final String[] sslProtocols;
+    private final String[] sslEnabledAlgorithms;
+    private final boolean sslRequireMutualAuthentication;
+
+    private SSLContext sslContext;
 
     public CustomSSLEngineProvider(ActorSystem system) {
         super(system);
@@ -55,6 +71,49 @@ public class CustomSSLEngineProvider extends 
ConfigSSLEngineProvider {
         sslCertFingerprints = 
securityConfig.getStringList("cert-fingerprints");
         sslKeyStoreType = securityConfig.getString("key-store-type");
         sslTrustStoreType = securityConfig.getString("trust-store-type");
+        sslProtocols = securityConfig.getString("protocol").split(",");
+        sslEnabledAlgorithms =
+                securityConfig.getStringList("enabled-algorithms").toArray(new 
String[0]);
+        sslRequireMutualAuthentication = 
securityConfig.getBoolean("require-mutual-authentication");
+    }
+
+    @Override
+    public SSLEngine createServerSSLEngine() {
+        SSLEngine engine = newEngine();
+        engine.setUseClientMode(false);
+        if (sslRequireMutualAuthentication) {
+            engine.setNeedClientAuth(true);
+        }
+        return engine;
+    }
+
+    @Override
+    public SSLEngine createClientSSLEngine() {
+        SSLEngine engine = newEngine();
+        engine.setUseClientMode(true);
+        return engine;
+    }
+
+    private SSLEngine newEngine() {
+        SSLEngine engine = getSslContext().createSSLEngine();
+        engine.setEnabledCipherSuites(sslEnabledAlgorithms);
+        engine.setEnabledProtocols(sslProtocols);
+        return engine;
+    }
+
+    private synchronized SSLContext getSslContext() {
+        if (sslContext == null) {
+            try {
+                SSLContext context = SSLContext.getInstance("TLS");
+                context.init(keyManagers(), trustManagers(), 
createSecureRandom());
+                sslContext = context;
+            } catch (GeneralSecurityException e) {
+                throw new RemoteTransportException(
+                        "Server SSL connection could not be established 
because SSL context could not be constructed",
+                        e);
+            }
+        }
+        return sslContext;
     }
 
     @Override
diff --git 
a/flink-rpc/flink-rpc-akka/src/main/java/org/apache/flink/runtime/rpc/pekko/PekkoUtils.java
 
b/flink-rpc/flink-rpc-akka/src/main/java/org/apache/flink/runtime/rpc/pekko/PekkoUtils.java
index 2825c834ffc..b12b03b4def 100644
--- 
a/flink-rpc/flink-rpc-akka/src/main/java/org/apache/flink/runtime/rpc/pekko/PekkoUtils.java
+++ 
b/flink-rpc/flink-rpc-akka/src/main/java/org/apache/flink/runtime/rpc/pekko/PekkoUtils.java
@@ -378,7 +378,7 @@ class PekkoUtils {
                 .add("          trust-store = \"" + sslTrustStore + "\"")
                 .add("          trust-store-password = \"" + 
sslTrustStorePassword + "\"")
                 .add("          trust-store-type = \"" + sslTrustStoreType + 
"\"")
-                .add("          protocol = " + sslProtocol + "")
+                .add("          protocol = \"" + sslProtocol + "\"")
                 .add("          enabled-algorithms = " + sslAlgorithms + "")
                 .add("          random-number-generator = \"\"")
                 .add("          require-mutual-authentication = on")
diff --git 
a/flink-rpc/flink-rpc-akka/src/test/java/org/apache/flink/runtime/rpc/pekko/CustomSSLEngineProviderTest.java
 
b/flink-rpc/flink-rpc-akka/src/test/java/org/apache/flink/runtime/rpc/pekko/CustomSSLEngineProviderTest.java
new file mode 100644
index 00000000000..3282a2f83e6
--- /dev/null
+++ 
b/flink-rpc/flink-rpc-akka/src/test/java/org/apache/flink/runtime/rpc/pekko/CustomSSLEngineProviderTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.flink.runtime.rpc.pekko;
+
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.configuration.SecurityOptions;
+import org.apache.flink.runtime.concurrent.pekko.ScalaFutureUtils;
+
+import org.apache.flink.shaded.netty4.io.netty.bootstrap.Bootstrap;
+import org.apache.flink.shaded.netty4.io.netty.bootstrap.ServerBootstrap;
+import org.apache.flink.shaded.netty4.io.netty.channel.Channel;
+import org.apache.flink.shaded.netty4.io.netty.channel.ChannelInitializer;
+import org.apache.flink.shaded.netty4.io.netty.channel.EventLoopGroup;
+import 
org.apache.flink.shaded.netty4.io.netty.channel.MultiThreadIoEventLoopGroup;
+import org.apache.flink.shaded.netty4.io.netty.channel.nio.NioIoHandler;
+import 
org.apache.flink.shaded.netty4.io.netty.channel.socket.nio.NioServerSocketChannel;
+import 
org.apache.flink.shaded.netty4.io.netty.channel.socket.nio.NioSocketChannel;
+import org.apache.flink.shaded.netty4.io.netty.handler.ssl.SslHandler;
+
+import org.apache.pekko.actor.ActorSystem;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSession;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.flink.util.Preconditions.checkNotNull;
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests that {@link CustomSSLEngineProvider} correctly negotiates TLS when 
{@link
+ * SecurityOptions#SSL_PROTOCOL} is configured with a comma-separated protocol 
list.
+ *
+ * <p>This is a regression test for the fact that {@link
+ * org.apache.pekko.remote.transport.netty.ConfigSSLEngineProvider}, which 
{@link
+ * CustomSSLEngineProvider} extends, only supports a single protocol name: it 
feeds the configured
+ * string, unsplit, into both {@code SSLContext.getInstance(String)} and {@code
+ * SSLEngine#setEnabledProtocols(String[])}.
+ */
+class CustomSSLEngineProviderTest {
+
+    private static final String KEY_STORE_PATH =
+            
checkNotNull(CustomSSLEngineProviderTest.class.getResource("/rpc.keystore")).getFile();
+    private static final String TRUST_STORE_PATH =
+            
checkNotNull(CustomSSLEngineProviderTest.class.getResource("/rpc.truststore"))
+                    .getFile();
+    private static final String STORE_PASSWORD = "password";
+
+    private static final String TLS_12_CIPHER =
+            
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384";
+    private static final String TLS_13_CIPHER = 
"TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384";
+
+    private ActorSystem actorSystem;
+
+    @AfterEach
+    void shutdown() throws Exception {
+        if (actorSystem != null) {
+            ScalaFutureUtils.toJava(actorSystem.terminate()).get(10, 
TimeUnit.SECONDS);
+        }
+    }
+
+    @Test
+    void negotiatesHighestCommonProtocolFromList() throws Exception {
+        SSLSession session = handshake("TLSv1.2,TLSv1.3", TLS_12_CIPHER + "," 
+ TLS_13_CIPHER);
+
+        assertThat(session.getProtocol()).isEqualTo("TLSv1.3");
+    }
+
+    @Test
+    void fallsBackToLowerListedProtocolWhenHigherOneHasNoUsableCipher() throws 
Exception {
+        SSLSession session = handshake("TLSv1.2,TLSv1.3", TLS_12_CIPHER);
+
+        assertThat(session.getProtocol()).isEqualTo("TLSv1.2");
+    }
+
+    /**
+     * Builds a {@link CustomSSLEngineProvider} from the given protocol list 
and ciphers, performs a
+     * real, socket-based TLS handshake between a server and a client engine 
it creates, and returns
+     * the client's negotiated session.
+     */
+    private SSLSession handshake(String protocolList, String ciphers) throws 
Exception {
+        final Configuration configuration = new Configuration();
+        configuration.set(SecurityOptions.SSL_INTERNAL_ENABLED, true);
+        configuration.set(SecurityOptions.SSL_INTERNAL_KEYSTORE, 
KEY_STORE_PATH);
+        configuration.set(SecurityOptions.SSL_INTERNAL_KEYSTORE_PASSWORD, 
STORE_PASSWORD);
+        configuration.set(SecurityOptions.SSL_INTERNAL_KEY_PASSWORD, 
STORE_PASSWORD);
+        configuration.set(SecurityOptions.SSL_INTERNAL_TRUSTSTORE, 
TRUST_STORE_PATH);
+        configuration.set(SecurityOptions.SSL_INTERNAL_TRUSTSTORE_PASSWORD, 
STORE_PASSWORD);
+        configuration.set(SecurityOptions.SSL_PROTOCOL, protocolList);
+        configuration.set(SecurityOptions.SSL_ALGORITHMS, ciphers);
+
+        actorSystem =
+                PekkoUtils.createActorSystem(
+                        "CustomSSLEngineProviderTest",
+                        PekkoUtils.getConfig(configuration, new 
HostAndPort("localhost", 0)));
+
+        final CustomSSLEngineProvider provider = new 
CustomSSLEngineProvider(actorSystem);
+
+        final EventLoopGroup group = new MultiThreadIoEventLoopGroup(2, 
NioIoHandler.newFactory());
+        try {
+            final CompletableFuture<SSLSession> serverSession = new 
CompletableFuture<>();
+            final CompletableFuture<SSLSession> clientSession = new 
CompletableFuture<>();
+
+            final ServerBootstrap serverBootstrap =
+                    new ServerBootstrap()
+                            .group(group)
+                            .channel(NioServerSocketChannel.class)
+                            .childHandler(
+                                    new ChannelInitializer<Channel>() {
+                                        @Override
+                                        protected void initChannel(Channel ch) 
{
+                                            SSLEngine engine = 
provider.createServerSSLEngine();
+                                            SslHandler handler = new 
SslHandler(engine);
+                                            ch.pipeline().addLast(handler);
+                                            completeWithSession(handler, 
serverSession);
+                                        }
+                                    });
+            final Channel serverChannel = 
serverBootstrap.bind(0).sync().channel();
+            final int port = ((InetSocketAddress) 
serverChannel.localAddress()).getPort();
+
+            final Bootstrap clientBootstrap =
+                    new Bootstrap()
+                            .group(group)
+                            .channel(NioSocketChannel.class)
+                            .handler(
+                                    new ChannelInitializer<Channel>() {
+                                        @Override
+                                        protected void initChannel(Channel ch) 
{
+                                            SSLEngine engine = 
provider.createClientSSLEngine();
+                                            SslHandler handler = new 
SslHandler(engine);
+                                            ch.pipeline().addLast(handler);
+                                            completeWithSession(handler, 
clientSession);
+                                        }
+                                    });
+            clientBootstrap.connect("localhost", port).sync();
+
+            final SSLSession client = clientSession.get(10, TimeUnit.SECONDS);
+            final SSLSession server = serverSession.get(10, TimeUnit.SECONDS);
+            assertThat(client.getProtocol()).isEqualTo(server.getProtocol());
+            
assertThat(client.getCipherSuite()).isEqualTo(server.getCipherSuite());
+
+            serverChannel.close().sync();
+            return client;
+        } finally {
+            group.shutdownGracefully(0, 1, TimeUnit.SECONDS).sync();
+        }
+    }
+
+    private static void completeWithSession(
+            SslHandler handler, CompletableFuture<SSLSession> future) {
+        handler.handshakeFuture()
+                .addListener(
+                        f -> {
+                            if (f.isSuccess()) {
+                                future.complete(handler.engine().getSession());
+                            } else {
+                                future.completeExceptionally(f.cause());
+                            }
+                        });
+    }
+}
diff --git a/flink-rpc/flink-rpc-akka/src/test/resources/rpc.keystore 
b/flink-rpc/flink-rpc-akka/src/test/resources/rpc.keystore
new file mode 100644
index 00000000000..b6cb8ce1522
Binary files /dev/null and 
b/flink-rpc/flink-rpc-akka/src/test/resources/rpc.keystore differ
diff --git a/flink-rpc/flink-rpc-akka/src/test/resources/rpc.truststore 
b/flink-rpc/flink-rpc-akka/src/test/resources/rpc.truststore
new file mode 100644
index 00000000000..5cd7f52f459
Binary files /dev/null and 
b/flink-rpc/flink-rpc-akka/src/test/resources/rpc.truststore differ
diff --git 
a/flink-runtime/src/main/java/org/apache/flink/runtime/net/SSLUtils.java 
b/flink-runtime/src/main/java/org/apache/flink/runtime/net/SSLUtils.java
index c89dd5eef7d..106f5c9af3c 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/net/SSLUtils.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/net/SSLUtils.java
@@ -377,6 +377,13 @@ public class SSLUtils {
                 .clientAuth(clientAuth)
                 .sessionCacheSize(sessionCacheSize)
                 .sessionTimeout(sessionTimeoutMs / 1000)
+                // Internal SSL uses one shared, mutually-trusted certificate 
across every node
+                // (see docs/deployment/security/security-ssl.md), so it must 
not be required to
+                // also match each peer's hostname. Explicitly disabled 
because Netty enables
+                // endpoint identification by default from 4.2 onward (both 
JDK and OpenSSL
+                // providers), which would otherwise break every connection 
whose resolved
+                // hostname isn't covered by the shared certificate.
+                .endpointIdentificationAlgorithm(null)
                 .build();
     }
 
diff --git 
a/flink-runtime/src/test/java/org/apache/flink/runtime/net/SSLUtilsTest.java 
b/flink-runtime/src/test/java/org/apache/flink/runtime/net/SSLUtilsTest.java
index 43e7de101ee..c68b53da6e5 100644
--- a/flink-runtime/src/test/java/org/apache/flink/runtime/net/SSLUtilsTest.java
+++ b/flink-runtime/src/test/java/org/apache/flink/runtime/net/SSLUtilsTest.java
@@ -23,7 +23,16 @@ import 
org.apache.flink.configuration.IllegalConfigurationException;
 import org.apache.flink.configuration.SecurityOptions;
 import org.apache.flink.runtime.io.network.netty.SSLHandlerFactory;
 
+import org.apache.flink.shaded.netty4.io.netty.bootstrap.Bootstrap;
+import org.apache.flink.shaded.netty4.io.netty.bootstrap.ServerBootstrap;
 import org.apache.flink.shaded.netty4.io.netty.buffer.UnpooledByteBufAllocator;
+import org.apache.flink.shaded.netty4.io.netty.channel.Channel;
+import org.apache.flink.shaded.netty4.io.netty.channel.ChannelInitializer;
+import org.apache.flink.shaded.netty4.io.netty.channel.EventLoopGroup;
+import 
org.apache.flink.shaded.netty4.io.netty.channel.MultiThreadIoEventLoopGroup;
+import org.apache.flink.shaded.netty4.io.netty.channel.nio.NioIoHandler;
+import 
org.apache.flink.shaded.netty4.io.netty.channel.socket.nio.NioServerSocketChannel;
+import 
org.apache.flink.shaded.netty4.io.netty.channel.socket.nio.NioSocketChannel;
 import org.apache.flink.shaded.netty4.io.netty.handler.ssl.ClientAuth;
 import org.apache.flink.shaded.netty4.io.netty.handler.ssl.JdkSslContext;
 import org.apache.flink.shaded.netty4.io.netty.handler.ssl.OpenSsl;
@@ -33,11 +42,16 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
 
+import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
 
 import java.io.File;
 import java.io.InputStream;
+import java.net.InetSocketAddress;
 import java.net.ServerSocket;
+import java.nio.channels.ClosedChannelException;
 import java.nio.file.Files;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
@@ -49,6 +63,8 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
 
 import static 
org.apache.flink.shaded.netty4.io.netty.handler.ssl.SslProvider.JDK;
 import static org.apache.flink.util.Preconditions.checkNotNull;
@@ -425,6 +441,301 @@ public class SSLUtilsTest {
         
assertThat(sslHandler.engine().getEnabledCipherSuites()).contains(sslAlgorithms);
     }
 
+    // -------------------- hostname verification (shared certificate) 
-------------------
+    //
+    // Internal SSL uses one shared, mutually-trusted certificate across every 
node in the
+    // cluster (see docs/content/docs/deployment/security/security-ssl.md: 
"Because internal
+    // connections are mutually authenticated with shared certificates, Flink 
can skip hostname
+    // verification. This makes container-based setups easier."), e.g. a 
Kubernetes deployment
+    // with hundreds of dynamically-scheduled, differently-named pods sharing 
one cert. Simulate
+    // that: the client connects to a real "localhost" socket but is told to 
verify a peer
+    // identity the shared certificate was never meant to cover; negotiation 
must still succeed.
+
+    @ParameterizedTest
+    @MethodSource("parameters")
+    void testInternalSSLIgnoresPeerHostMismatch(String sslProvider) throws 
Exception {
+        Configuration config = 
createInternalSslConfigWithKeyAndTrustStores(sslProvider);
+
+        SSLSession session =
+                negotiate(
+                        SSLUtils.createInternalServerSSLEngineFactory(config),
+                        SSLUtils.createInternalClientSSLEngineFactory(config),
+                        
"some-taskmanager-42.flink-headless.flink.svc.cluster.local");
+
+        assertThat(session).isNotNull();
+    }
+
+    // -------------------- multi-protocol negotiation -----------------------
+    //
+    // Regression coverage for SecurityOptions.SSL_PROTOCOL accepting a 
comma-separated protocol
+    // list: every consumer below is expected to negotiate the highest 
protocol both sides support,
+    // and to gracefully fall back rather than fail when the higher protocol 
has no usable cipher.
+
+    private static final String TLS_13_CIPHERS = 
"TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384";
+
+    private static String tls12Ciphers(String sslProvider) {
+        // openSSL does not support the same set of cipher algorithms as the 
JDK provider, see
+        // testCreateSSLEngineFactory above.
+        return sslProvider.equalsIgnoreCase("OPENSSL")
+                ? 
"TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384"
+                : 
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384";
+    }
+
+    /**
+     * TLSv1.3 support in OpenSSL depends on {@code 
OpenSsl.isTlsv13Supported()} at runtime, which
+     * this branch's pinned netty-tcnative (2.0.74.Final) does not report as 
available in this CI
+     * environment, unlike JDK's own TLSv1.3 support. Only verified against 
the JDK provider here;
+     * the underlying protocol-list handling in {@link SSLUtils} itself is 
provider-agnostic, and is
+     * verified against the OpenSSL provider on master.
+     */
+    @Test
+    void testInternalSSLNegotiatesTls13WhenBothSidesSupportIt() throws 
Exception {
+        Configuration config = 
createInternalSslConfigWithKeyAndTrustStores("JDK");
+        config.set(SecurityOptions.SSL_PROTOCOL, "TLSv1.2,TLSv1.3");
+        config.set(SecurityOptions.SSL_ALGORITHMS, tls12Ciphers("JDK") + "," + 
TLS_13_CIPHERS);
+
+        SSLSession session =
+                negotiate(
+                        SSLUtils.createInternalServerSSLEngineFactory(config),
+                        SSLUtils.createInternalClientSSLEngineFactory(config));
+
+        assertThat(session.getProtocol()).isEqualTo("TLSv1.3");
+    }
+
+    /**
+     * See {@link #testInternalSSLNegotiatesTls13WhenBothSidesSupportIt} for 
why this is not
+     * parameterized on this branch.
+     */
+    @Test
+    void testInternalSSLFallsBackToTls12WithoutTls13Cipher() throws Exception {
+        Configuration config = 
createInternalSslConfigWithKeyAndTrustStores("JDK");
+        config.set(SecurityOptions.SSL_PROTOCOL, "TLSv1.2,TLSv1.3");
+        config.set(SecurityOptions.SSL_ALGORITHMS, tls12Ciphers("JDK"));
+
+        SSLSession session =
+                negotiate(
+                        SSLUtils.createInternalServerSSLEngineFactory(config),
+                        SSLUtils.createInternalClientSSLEngineFactory(config));
+
+        assertThat(session.getProtocol()).isEqualTo("TLSv1.2");
+    }
+
+    @ParameterizedTest
+    @MethodSource("parameters")
+    void 
testInternalSSLNonContiguousProtocolListWithoutCipherForHighest(String 
sslProvider)
+            throws Exception {
+        // TLSv1.3 has no usable cipher here (only TLSv1.2-style ones are 
configured), so Netty
+        // drops it from the min/max range calculation entirely before any 
OpenSSL-specific
+        // contiguous-range widening happens (ReferenceCountedOpenSslEngine's
+        // explicitDisableTLSv13 handling). That collapses the enabled set to 
TLSv1.1 alone on
+        // both providers, which also has no usable cipher (GCM suites are 
TLSv1.2+), so the
+        // handshake fails on both providers instead of widening to TLSv1.2.
+        //
+        // On this branch, OpenSSL additionally has no TLSv1.3 support at all 
in this CI
+        // environment (see 
testInternalSSLNegotiatesTls13WhenBothSidesSupportIt), so TLSv1.3 is
+        // disabled at the library level rather than per-cipher; the handshake 
still fails as
+        // expected, but surfaces as a plain closed channel instead of an 
SSLHandshakeException.
+        // Either way, nothing gets incorrectly widened to TLSv1.2, which is 
what this test locks
+        // in.
+        Configuration config = 
createInternalSslConfigWithKeyAndTrustStores(sslProvider);
+        config.set(SecurityOptions.SSL_PROTOCOL, "TLSv1.1,TLSv1.3");
+        config.set(SecurityOptions.SSL_ALGORITHMS, tls12Ciphers(sslProvider));
+
+        final SSLHandlerFactory server = 
SSLUtils.createInternalServerSSLEngineFactory(config);
+        final SSLHandlerFactory client = 
SSLUtils.createInternalClientSSLEngineFactory(config);
+
+        assertThatThrownBy(() -> negotiate(server, client))
+                .satisfiesAnyOf(
+                        t -> 
assertThat(t).hasCauseInstanceOf(SSLHandshakeException.class),
+                        t -> 
assertThat(t).isInstanceOf(ClosedChannelException.class));
+    }
+
+    /**
+     * See {@link #testInternalSSLNegotiatesTls13WhenBothSidesSupportIt} for 
why this is not
+     * parameterized on this branch.
+     */
+    @Test
+    void testRestSSLNegotiatesTls13WhenBothSidesSupportIt() throws Exception {
+        Configuration config = createRestSslConfigWithKeyAndTrustStores("JDK");
+        config.set(SecurityOptions.SSL_PROTOCOL, "TLSv1.2,TLSv1.3");
+        config.set(SecurityOptions.SSL_ALGORITHMS, tls12Ciphers("JDK") + "," + 
TLS_13_CIPHERS);
+
+        SSLSession session =
+                negotiate(
+                        SSLUtils.createRestServerSSLEngineFactory(config),
+                        SSLUtils.createRestClientSSLEngineFactory(config));
+
+        assertThat(session.getProtocol()).isEqualTo("TLSv1.3");
+    }
+
+    /**
+     * See {@link #testInternalSSLNegotiatesTls13WhenBothSidesSupportIt} for 
why this is not
+     * parameterized on this branch.
+     */
+    @Test
+    void testRestSSLFallsBackToTls12WithoutTls13Cipher() throws Exception {
+        Configuration config = createRestSslConfigWithKeyAndTrustStores("JDK");
+        config.set(SecurityOptions.SSL_PROTOCOL, "TLSv1.2,TLSv1.3");
+        config.set(SecurityOptions.SSL_ALGORITHMS, tls12Ciphers("JDK"));
+
+        SSLSession session =
+                negotiate(
+                        SSLUtils.createRestServerSSLEngineFactory(config),
+                        SSLUtils.createRestClientSSLEngineFactory(config));
+
+        assertThat(session.getProtocol()).isEqualTo("TLSv1.2");
+    }
+
+    /**
+     * The Blob server/client socket path ({@link 
SSLUtils#createSSLServerSocketFactory}) always
+     * uses the JDK provider regardless of {@link 
SecurityOptions#SSL_PROVIDER}, so this is not
+     * parameterized.
+     */
+    @Test
+    void testBlobSocketSSLNegotiatesTls13WhenBothSidesSupportIt() throws 
Exception {
+        Configuration config = 
createInternalSslConfigWithKeyAndTrustStores("JDK");
+        config.set(SecurityOptions.SSL_PROTOCOL, "TLSv1.2,TLSv1.3");
+        config.set(SecurityOptions.SSL_ALGORITHMS, tls12Ciphers("JDK") + "," + 
TLS_13_CIPHERS);
+
+        assertThat(negotiateViaSockets(config)).isEqualTo("TLSv1.3");
+    }
+
+    @Test
+    void testBlobSocketSSLFallsBackToTls12WithoutTls13Cipher() throws 
Exception {
+        Configuration config = 
createInternalSslConfigWithKeyAndTrustStores("JDK");
+        config.set(SecurityOptions.SSL_PROTOCOL, "TLSv1.2,TLSv1.3");
+        config.set(SecurityOptions.SSL_ALGORITHMS, tls12Ciphers("JDK"));
+
+        assertThat(negotiateViaSockets(config)).isEqualTo("TLSv1.2");
+    }
+
+    /**
+     * Performs a real, socket-based TLS handshake between the given server 
and client {@link
+     * SSLHandlerFactory} and returns the client's negotiated session.
+     */
+    private static SSLSession negotiate(
+            SSLHandlerFactory serverFactory, SSLHandlerFactory clientFactory) 
throws Exception {
+        return negotiate(serverFactory, clientFactory, "localhost");
+    }
+
+    /**
+     * Like {@link #negotiate(SSLHandlerFactory, SSLHandlerFactory)}, but the 
client engine is told
+     * to verify the given {@code clientPeerIdentity} instead of the real TCP 
destination
+     * ("localhost"), so a mismatch between the two can be simulated without 
breaking the actual
+     * connection.
+     */
+    private static SSLSession negotiate(
+            SSLHandlerFactory serverFactory,
+            SSLHandlerFactory clientFactory,
+            String clientPeerIdentity)
+            throws Exception {
+        final EventLoopGroup group = new MultiThreadIoEventLoopGroup(2, 
NioIoHandler.newFactory());
+        try {
+            final CompletableFuture<SSLSession> serverSession = new 
CompletableFuture<>();
+            final CompletableFuture<SSLSession> clientSession = new 
CompletableFuture<>();
+
+            final ServerBootstrap serverBootstrap =
+                    new ServerBootstrap()
+                            .group(group)
+                            .channel(NioServerSocketChannel.class)
+                            .childHandler(
+                                    new ChannelInitializer<Channel>() {
+                                        @Override
+                                        protected void initChannel(Channel ch) 
{
+                                            SslHandler handler =
+                                                    
serverFactory.createNettySSLHandler(
+                                                            
UnpooledByteBufAllocator.DEFAULT);
+                                            ch.pipeline().addLast(handler);
+                                            completeWithSession(handler, 
serverSession);
+                                        }
+                                    });
+            final Channel serverChannel = 
serverBootstrap.bind(0).sync().channel();
+            final int port = ((InetSocketAddress) 
serverChannel.localAddress()).getPort();
+
+            final Bootstrap clientBootstrap =
+                    new Bootstrap()
+                            .group(group)
+                            .channel(NioSocketChannel.class)
+                            .handler(
+                                    new ChannelInitializer<Channel>() {
+                                        @Override
+                                        protected void initChannel(Channel ch) 
{
+                                            // Mirrors NettyClient/RestClient: 
the client engine
+                                            // must be created with the peer 
host/port, otherwise
+                                            // TLSv1.3 mutual-auth handshakes 
fail with
+                                            // "certificate_unknown: Hostname 
or IP address is
+                                            // undefined" (TLSv1.2 tolerates 
the hostless engine).
+                                            SslHandler handler =
+                                                    
clientFactory.createNettySSLHandler(
+                                                            
UnpooledByteBufAllocator.DEFAULT,
+                                                            clientPeerIdentity,
+                                                            port);
+                                            ch.pipeline().addLast(handler);
+                                            completeWithSession(handler, 
clientSession);
+                                        }
+                                    });
+            clientBootstrap.connect("localhost", port).sync();
+
+            final SSLSession client = clientSession.get(10, TimeUnit.SECONDS);
+            final SSLSession server = serverSession.get(10, TimeUnit.SECONDS);
+            assertThat(client.getProtocol()).isEqualTo(server.getProtocol());
+
+            serverChannel.close().sync();
+            return client;
+        } finally {
+            group.shutdownGracefully(0, 1, TimeUnit.SECONDS).sync();
+        }
+    }
+
+    private static void completeWithSession(
+            SslHandler handler, CompletableFuture<SSLSession> future) {
+        handler.handshakeFuture()
+                .addListener(
+                        f -> {
+                            if (f.isSuccess()) {
+                                future.complete(handler.engine().getSession());
+                            } else {
+                                future.completeExceptionally(f.cause());
+                            }
+                        });
+    }
+
+    /**
+     * Performs a real handshake through the raw-socket path used by the Blob 
server/client, and
+     * returns the negotiated protocol.
+     */
+    private static String negotiateViaSockets(Configuration config) throws 
Exception {
+        try (ServerSocket serverSocket =
+                
SSLUtils.createSSLServerSocketFactory(config).createServerSocket(0)) {
+            final int port = serverSocket.getLocalPort();
+
+            final CompletableFuture<String> serverProtocol = new 
CompletableFuture<>();
+            final Thread serverThread =
+                    new Thread(
+                            () -> {
+                                try (SSLSocket socket = (SSLSocket) 
serverSocket.accept()) {
+                                    socket.startHandshake();
+                                    
serverProtocol.complete(socket.getSession().getProtocol());
+                                } catch (Exception e) {
+                                    serverProtocol.completeExceptionally(e);
+                                }
+                            });
+            serverThread.start();
+
+            final String clientProtocol;
+            try (SSLSocket client =
+                    (SSLSocket)
+                            SSLUtils.createSSLClientSocketFactory(config)
+                                    .createSocket("localhost", port)) {
+                client.startHandshake();
+                clientProtocol = client.getSession().getProtocol();
+            }
+
+            assertThat(serverProtocol.get(10, 
TimeUnit.SECONDS)).isEqualTo(clientProtocol);
+            return clientProtocol;
+        }
+    }
+
     @ParameterizedTest
     @MethodSource("parameters")
     void testInvalidFingerprintParsing(String sslProvider) throws Exception {
diff --git a/tools/azure-pipelines/e2e-template.yml 
b/tools/azure-pipelines/e2e-template.yml
index 3b1229af1a0..5c1e268803d 100644
--- a/tools/azure-pipelines/e2e-template.yml
+++ b/tools/azure-pipelines/e2e-template.yml
@@ -124,8 +124,21 @@ jobs:
 
         echo "Installing required software for netty-tcnative"
         sudo apt-get install -y bc libapr1
-        wget -r --no-parent -nd --accept=libssl1.0.0_*ubuntu5.*_amd64.deb 
http://security.ubuntu.com/ubuntu/pool/main/o/openssl1.0/
-        sudo apt install ./libssl1.0.0_*.deb
+
+        # flink-shaded-netty-tcnative-dynamic links against OpenSSL symbols 
newer than what this
+        # Ubuntu release ships (system libssl.so.3 lacks OPENSSL_3.2.0). Per 
netty's own guidance
+        # for this exact case (http://netty.io/wiki/forked-tomcat-native.html: 
"If your Linux
+        # distro's package manager doesn't provide a new enough OpenSSL, you 
will need to compile
+        # it by your own and set the LD_LIBRARY_PATH"), fetch a newer prebuilt 
libssl/libcrypto
+        # into a private directory instead of replacing the system package, so 
the rest of this
+        # job keeps using the OS's own OpenSSL.
+        FLINK_E2E_OPENSSL32_LIB="$(pwd)/openssl32-lib"
+        mkdir -p "${FLINK_E2E_OPENSSL32_LIB}"
+        wget -q -O /tmp/libssl3t64.deb 
http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl3t64_3.5.5-1ubuntu3.5_amd64.deb
+        dpkg-deb -x /tmp/libssl3t64.deb /tmp/libssl3t64-extracted
+        cp /tmp/libssl3t64-extracted/usr/lib/x86_64-linux-gnu/libssl.so.3 
/tmp/libssl3t64-extracted/usr/lib/x86_64-linux-gnu/libcrypto.so.3 
"${FLINK_E2E_OPENSSL32_LIB}/"
+        rm -rf /tmp/libssl3t64.deb /tmp/libssl3t64-extracted
+        echo "##vso[task.setvariable 
variable=FLINK_E2E_OPENSSL32_LIB]${FLINK_E2E_OPENSSL32_LIB}"
       displayName: Prepare E2E run
       condition: not(eq(variables['SKIP'], '1'))
     - script: ${{parameters.environment}} PROFILE="$PROFILE -Dfast 
-Pskip-webui-build" ./tools/ci/compile_ci.sh

Reply via email to