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

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


The following commit(s) were added to refs/heads/master by this push:
     new 1be42107f RATIS-2598. TLS provider/protocol/cipher config is not 
applied to DataStream server/client. (#1511)
1be42107f is described below

commit 1be42107f4804d1c2ab194a00ee569b31138a8fb
Author: Istvan Fajth <[email protected]>
AuthorDate: Tue Jul 14 01:31:00 2026 +0200

    RATIS-2598. TLS provider/protocol/cipher config is not applied to 
DataStream server/client. (#1511)
---
 .../java/org/apache/ratis/util/NettyUtils.java     | 52 ++++++++++++++-
 .../main/java/org/apache/ratis/grpc/GrpcUtil.java  | 14 +---
 .../apache/ratis/netty/TestTlsConfWithNetty.java   | 76 ++++++++++++++++++++++
 3 files changed, 128 insertions(+), 14 deletions(-)

diff --git a/ratis-common/src/main/java/org/apache/ratis/util/NettyUtils.java 
b/ratis-common/src/main/java/org/apache/ratis/util/NettyUtils.java
index 819c93db9..c7ba9d3bb 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/NettyUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/NettyUtils.java
@@ -36,6 +36,8 @@ import 
org.apache.ratis.thirdparty.io.netty.channel.socket.nio.NioServerSocketCh
 import 
org.apache.ratis.thirdparty.io.netty.channel.socket.nio.NioSocketChannel;
 import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslContext;
 import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslContextBuilder;
+import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslProvider;
+import 
org.apache.ratis.thirdparty.io.netty.handler.ssl.SupportedCipherSuiteFilter;
 import org.apache.ratis.thirdparty.io.netty.util.concurrent.Future;
 import org.apache.ratis.thirdparty.io.netty.util.concurrent.ScheduledFuture;
 import org.slf4j.Logger;
@@ -43,6 +45,8 @@ import org.slf4j.LoggerFactory;
 
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.TrustManager;
+import java.security.Provider;
+import java.security.Security;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -172,7 +176,7 @@ public interface NettyUtils {
     if (tlsConf.isMutualTls()) {
       setTrustManager(b, tlsConf.getTrustManager());
     }
-    return b;
+    return configureSslContextBuilder(b, tlsConf);
   }
 
   static SslContext buildSslContextForServer(TlsConf tlsConf) {
@@ -185,9 +189,55 @@ public interface NettyUtils {
     if (tlsConf.isMutualTls()) {
       setKeyManager(b, tlsConf.getKeyManager());
     }
+    return configureSslContextBuilder(b, tlsConf);
+  }
+
+  /**
+   * Apply the {@link SslProvider}, JSSE provider, enabled TLS protocols and 
cipher suites from the given
+   * {@link TlsConf} to the builder, so that the Netty DataStream transport 
honours the same
+   * configuration instead of falling back on the provider defaults.
+   *
+   * <p>Unlike the gRPC path ({@code GrpcUtil.configureSslContextBuilder}) 
this uses
+   * {@link SupportedCipherSuiteFilter}, which intersects the requested cipher 
suites with the
+   * ones actually supported by the negotiated provider/engine, rather than 
passing them through
+   * verbatim.
+   */
+  static SslContextBuilder configureSslContextBuilder(SslContextBuilder b, 
TlsConf tlsConf) {
+    final SslProvider sslProvider = tlsConf.getSslProvider();
+    if (sslProvider != null) {
+      b.sslProvider(sslProvider);
+    }
+    final Provider jsseProvider = getJsseProvider(tlsConf);
+    if (jsseProvider != null) {
+      b.sslProvider(SslProvider.JDK).sslContextProvider(jsseProvider);
+    }
+    final List<String> protocols = tlsConf.getProtocols();
+    if (protocols != null && !protocols.isEmpty()) {
+      b.protocols(protocols);
+    }
+    final List<String> cipherSuites = tlsConf.getCipherSuites();
+    if (cipherSuites != null && !cipherSuites.isEmpty()) {
+      b.ciphers(cipherSuites, SupportedCipherSuiteFilter.INSTANCE);
+    }
     return b;
   }
 
+  /**
+   * @return the named JSSE {@link Provider} from {@link 
TlsConf#getJsseProviderName()}, or null when
+   *     unset; throws {@link IllegalArgumentException} when the named 
provider is not registered.
+   */
+  static Provider getJsseProvider(TlsConf tlsConf) {
+    final String providerName = tlsConf.getJsseProviderName();
+    if (providerName == null || providerName.trim().isEmpty()) {
+      return null;
+    }
+    final Provider namedProvider = Security.getProvider(providerName.trim());
+    if (namedProvider == null) {
+      throw new IllegalArgumentException("JSSE provider not found: " + 
providerName);
+    }
+    return namedProvider;
+  }
+
   static SslContext buildSslContextForClient(TlsConf tlsConf) {
     return buildSslContext("client", tlsConf, 
NettyUtils::initSslContextBuilderForClient);
   }
diff --git a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcUtil.java 
b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcUtil.java
index 800e40cee..3abbb1832 100644
--- a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcUtil.java
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcUtil.java
@@ -50,7 +50,6 @@ import javax.net.ssl.SSLException;
 import javax.net.ssl.TrustManager;
 import java.io.IOException;
 import java.security.Provider;
-import java.security.Security;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
@@ -58,6 +57,7 @@ import java.util.function.Function;
 import java.util.function.Supplier;
 
 import static 
org.apache.ratis.thirdparty.io.netty.handler.ssl.SslProvider.OPENSSL;
+import static org.apache.ratis.util.NettyUtils.getJsseProvider;
 
 public interface GrpcUtil {
   Logger LOG = LoggerFactory.getLogger(GrpcUtil.class);
@@ -357,18 +357,6 @@ public interface GrpcUtil {
     }
   }
 
-  static Provider getJsseProvider(TlsConf tlsConf) {
-    final String providerName = tlsConf.getJsseProviderName();
-    if (providerName == null || providerName.trim().isEmpty()) {
-      return null;
-    }
-    final Provider namedProvider = Security.getProvider(providerName.trim());
-    if (namedProvider == null) {
-      throw new IllegalArgumentException("JSSE provider not found: " + 
providerName);
-    }
-    return namedProvider;
-  }
-
   static SslContext buildSslContextForServer(GrpcTlsConfig tlsConf) {
     if (tlsConf == null) {
       return null;
diff --git 
a/ratis-test/src/test/java/org/apache/ratis/netty/TestTlsConfWithNetty.java 
b/ratis-test/src/test/java/org/apache/ratis/netty/TestTlsConfWithNetty.java
index 5d73d9ea3..b74fa92ce 100644
--- a/ratis-test/src/test/java/org/apache/ratis/netty/TestTlsConfWithNetty.java
+++ b/ratis-test/src/test/java/org/apache/ratis/netty/TestTlsConfWithNetty.java
@@ -37,6 +37,7 @@ import 
org.apache.ratis.thirdparty.io.netty.channel.socket.nio.NioSocketChannel;
 import org.apache.ratis.thirdparty.io.netty.handler.logging.LogLevel;
 import org.apache.ratis.thirdparty.io.netty.handler.logging.LoggingHandler;
 import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslContext;
+import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslProvider;
 import org.apache.ratis.util.JavaUtils;
 import org.apache.ratis.util.NettyUtils;
 import org.junit.jupiter.api.Assertions;
@@ -47,6 +48,8 @@ import org.slf4j.LoggerFactory;
 import java.io.Closeable;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Queue;
@@ -93,6 +96,79 @@ public class TestTlsConfWithNetty {
     runTest(randomPort(), serverTlsConfig, clientTlsConfig);
   }
 
+  /** RSA-certificate cipher, broadly supported on the JDK provider for 
TLSv1.2. */
+  private static final String VALID_CIPHER = 
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
+  private static final String UNSUPPORTED_CIPHER = 
"TLS_UNSUPPORTED_BOGUS_CIPHER";
+  private static final List<String> TLS_1_2 = 
Collections.singletonList("TLSv1.2");
+
+  static TlsConf newServerTlsConfig(SslProvider provider, List<String> 
protocols, List<String> cipherSuites) {
+    return TlsConf.newBuilder()
+        .setName("server")
+        .setPrivateKey(new 
TlsConf.PrivateKeyConf(SecurityTestUtils.getResource("ssl/server.pem")))
+        .setKeyCertificates(new 
TlsConf.CertificatesConf(SecurityTestUtils.getResource("ssl/server.crt")))
+        .setSslProvider(provider)
+        .setProtocols(protocols)
+        .setCipherSuites(cipherSuites)
+        .build();
+  }
+
+  static TlsConf newClientTlsConfig(SslProvider provider, List<String> 
protocols, List<String> cipherSuites) {
+    return TlsConf.newBuilder()
+        .setName("client")
+        .setTrustCertificates(new 
TlsConf.CertificatesConf(SecurityTestUtils.getResource("ssl/ca.crt")))
+        .setSslProvider(provider)
+        .setProtocols(protocols)
+        .setCipherSuites(cipherSuites)
+        .build();
+  }
+
+  /**
+   * The configured cipher suites must actually be applied to the {@link 
SslContext}; previously
+   * they were silently dropped on the Netty path. See RATIS-2598.
+   */
+  @Test
+  public void testConfiguredCipherSuitesAreApplied() {
+    final SslContext ctx = NettyUtils.buildSslContextForServer(
+        newServerTlsConfig(SslProvider.JDK, TLS_1_2, 
Collections.singletonList(VALID_CIPHER)));
+    Assertions.assertEquals(Collections.singletonList(VALID_CIPHER), 
ctx.cipherSuites());
+  }
+
+  /**
+   * An unsupported cipher suite must be filtered out rather than passed 
through verbatim; otherwise
+   * building the {@link SslContext} fails and crashes the server. See 
RATIS-2598.
+   */
+  @Test
+  public void testUnsupportedCipherSuiteIsFilteredOut() {
+    final SslContext ctx = NettyUtils.buildSslContextForServer(
+        newServerTlsConfig(SslProvider.JDK, TLS_1_2, 
Arrays.asList(UNSUPPORTED_CIPHER, VALID_CIPHER)));
+    Assertions.assertEquals(Collections.singletonList(VALID_CIPHER), 
ctx.cipherSuites());
+  }
+
+  /**
+   * End-to-end handshake with explicit protocol and cipher configuration, 
including an unsupported
+   * cipher that must be filtered out without breaking the handshake.
+   */
+  @Test
+  public void testSslWithProtocolAndCipher() throws Exception {
+    final List<String> ciphers = Arrays.asList(UNSUPPORTED_CIPHER, 
VALID_CIPHER);
+    final TlsConf serverTlsConfig = newServerTlsConfig(SslProvider.JDK, 
TLS_1_2, ciphers);
+    final TlsConf clientTlsConfig = newClientTlsConfig(SslProvider.JDK, 
TLS_1_2, ciphers);
+    runTest(randomPort(), serverTlsConfig, clientTlsConfig);
+  }
+
+  /** An unset JSSE provider name resolves to null (no provider override). See 
RATIS-2598. */
+  @Test
+  public void testNoJsseProviderReturnsNull() {
+    
Assertions.assertNull(NettyUtils.getJsseProvider(TlsConf.newBuilder().build()));
+  }
+
+  /** An unknown JSSE provider name must fail fast rather than be silently 
ignored. */
+  @Test
+  public void testUnknownJsseProviderThrows() {
+    final TlsConf conf = 
TlsConf.newBuilder().setJsseProviderName("NoSuchJsseProvider").build();
+    Assertions.assertThrows(IllegalArgumentException.class, () -> 
NettyUtils.getJsseProvider(conf));
+  }
+
   static void runTest(int port, TlsConf serverSslConf, TlsConf clientSslConf) 
throws Exception {
     final SslContext serverSslContext = serverSslConf == null? null
         : NettyUtils.buildSslContextForServer(serverSslConf);

Reply via email to