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 bceb3c209 RATIS-2602. Pass CipherSuiteFilter to the TlsConf. (#1517)
bceb3c209 is described below

commit bceb3c20929dabe3041b5793a53d3a7881278f61
Author: Haonan <[email protected]>
AuthorDate: Tue Jul 14 11:36:01 2026 +0800

    RATIS-2602. Pass CipherSuiteFilter to the TlsConf. (#1517)
---
 .../java/org/apache/ratis/security/TlsConf.java    | 25 +++++++++++++++++
 .../java/org/apache/ratis/util/NettyUtils.java     |  8 ++----
 .../java/org/apache/ratis/grpc/GrpcTlsConfig.java  |  7 +++++
 .../main/java/org/apache/ratis/grpc/GrpcUtil.java  |  3 +--
 .../org/apache/ratis/grpc/TestGrpcTlsConfig.java   | 31 ++++++++++++++++++++++
 .../apache/ratis/netty/TestTlsConfWithNetty.java   | 19 +++++++++++++
 6 files changed, 85 insertions(+), 8 deletions(-)

diff --git a/ratis-common/src/main/java/org/apache/ratis/security/TlsConf.java 
b/ratis-common/src/main/java/org/apache/ratis/security/TlsConf.java
index b44212459..1fde322e3 100644
--- a/ratis-common/src/main/java/org/apache/ratis/security/TlsConf.java
+++ b/ratis-common/src/main/java/org/apache/ratis/security/TlsConf.java
@@ -17,7 +17,9 @@
  */
 package org.apache.ratis.security;
 
+import org.apache.ratis.thirdparty.io.netty.handler.ssl.CipherSuiteFilter;
 import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslProvider;
+import 
org.apache.ratis.thirdparty.io.netty.handler.ssl.SupportedCipherSuiteFilter;
 import org.apache.ratis.util.JavaUtils;
 import org.apache.ratis.util.Preconditions;
 
@@ -178,6 +180,7 @@ public class TlsConf {
   private final String jsseProviderName;
   private final List<String> protocols;
   private final List<String> cipherSuites;
+  private final CipherSuiteFilter cipherSuiteFilter;
 
   protected TlsConf(Builder b) {
     final String buildName = b.buildName();
@@ -190,6 +193,7 @@ public class TlsConf {
     this.jsseProviderName = b.jsseProviderName;
     this.protocols = copy(b.protocols);
     this.cipherSuites = copy(b.cipherSuites);
+    this.cipherSuiteFilter = b.cipherSuiteFilter;
   }
 
   /** @return the key manager configuration. */
@@ -223,6 +227,14 @@ public class TlsConf {
     return copy(cipherSuites);
   }
 
+  /**
+   * @return the filter applied to configured cipher suites. The default is
+   *     {@link SupportedCipherSuiteFilter#INSTANCE}.
+   */
+  public CipherSuiteFilter getCipherSuiteFilter() {
+    return cipherSuiteFilter;
+  }
+
   @Override
   public String toString() {
     return name;
@@ -253,6 +265,7 @@ public class TlsConf {
     private String jsseProviderName;
     private List<String> protocols;
     private List<String> cipherSuites;
+    private CipherSuiteFilter cipherSuiteFilter = 
SupportedCipherSuiteFilter.INSTANCE;
 
     public Builder setName(String name) {
       this.name = name;
@@ -319,6 +332,18 @@ public class TlsConf {
       return this;
     }
 
+    /**
+     * Set the filter applied to configured cipher suites.
+     *
+     * <p>A pass-through filter such as {@code IdentityCipherSuiteFilter} 
preserves provider-specific
+     * suites that may be absent from Netty's supported set. It does not make 
an unsupported suite
+     * valid; validation may instead fail when the TLS engine is initialized 
or during the handshake.
+     */
+    public Builder setCipherSuiteFilter(CipherSuiteFilter cipherSuiteFilter) {
+      this.cipherSuiteFilter = Objects.requireNonNull(cipherSuiteFilter, 
"cipherSuiteFilter == null");
+      return this;
+    }
+
     private boolean isMutualTls() {
       return mutualTls;
     }
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 c7ba9d3bb..aa6738bb8 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
@@ -37,7 +37,6 @@ 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;
@@ -197,10 +196,7 @@ public interface NettyUtils {
    * {@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.
+   * <p>The cipher suite filter is supplied by {@link 
TlsConf#getCipherSuiteFilter()}.
    */
   static SslContextBuilder configureSslContextBuilder(SslContextBuilder b, 
TlsConf tlsConf) {
     final SslProvider sslProvider = tlsConf.getSslProvider();
@@ -217,7 +213,7 @@ public interface NettyUtils {
     }
     final List<String> cipherSuites = tlsConf.getCipherSuites();
     if (cipherSuites != null && !cipherSuites.isEmpty()) {
-      b.ciphers(cipherSuites, SupportedCipherSuiteFilter.INSTANCE);
+      b.ciphers(cipherSuites, tlsConf.getCipherSuiteFilter());
     }
     return b;
   }
diff --git a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcTlsConfig.java 
b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcTlsConfig.java
index c6b8dce59..c38cb0671 100644
--- a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcTlsConfig.java
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcTlsConfig.java
@@ -18,6 +18,7 @@
 package org.apache.ratis.grpc;
 
 import org.apache.ratis.security.TlsConf;
+import org.apache.ratis.thirdparty.io.netty.handler.ssl.CipherSuiteFilter;
 import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslProvider;
 
 import javax.net.ssl.KeyManager;
@@ -217,6 +218,12 @@ public class GrpcTlsConfig extends TlsConf {
       return this;
     }
 
+    @Override
+    public Builder setCipherSuiteFilter(CipherSuiteFilter cipherSuiteFilter) {
+      super.setCipherSuiteFilter(cipherSuiteFilter);
+      return this;
+    }
+
     @Override
     public GrpcTlsConfig build() {
       return new GrpcTlsConfig(this, false);
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 3abbb1832..b1456480f 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
@@ -36,7 +36,6 @@ import 
org.apache.ratis.thirdparty.io.netty.handler.ssl.ClientAuth;
 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.util.IOUtils;
 import org.apache.ratis.util.JavaUtils;
 import org.apache.ratis.util.LogUtils;
@@ -339,7 +338,7 @@ public interface GrpcUtil {
     }
     final List<String> cipherSuites = tlsConf.getCipherSuites();
     if (cipherSuites != null && !cipherSuites.isEmpty()) {
-      b.ciphers(cipherSuites, SupportedCipherSuiteFilter.INSTANCE);
+      b.ciphers(cipherSuites, tlsConf.getCipherSuiteFilter());
     }
     return b;
   }
diff --git 
a/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestGrpcTlsConfig.java 
b/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestGrpcTlsConfig.java
index d3c6be8e2..1403dc97f 100644
--- a/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestGrpcTlsConfig.java
+++ b/ratis-grpc/src/test/java/org/apache/ratis/grpc/TestGrpcTlsConfig.java
@@ -17,7 +17,11 @@
  */
 package org.apache.ratis.grpc;
 
+import org.apache.ratis.thirdparty.io.netty.handler.ssl.CipherSuiteFilter;
+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.SupportedCipherSuiteFilter;
+import org.apache.ratis.thirdparty.io.netty.util.ReferenceCountUtil;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -26,6 +30,7 @@ import java.security.Provider;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 public class TestGrpcTlsConfig {
   @Test
@@ -48,6 +53,32 @@ public class TestGrpcTlsConfig {
         () -> conf.getCipherSuites().add("TLS_AES_256_GCM_SHA384"));
   }
 
+  @Test
+  public void testCipherSuiteFilterIsApplied() throws SSLException {
+    Assertions.assertSame(SupportedCipherSuiteFilter.INSTANCE,
+        GrpcTlsConfig.newBuilder().build().getCipherSuiteFilter());
+
+    final AtomicBoolean invoked = new AtomicBoolean();
+    final CipherSuiteFilter filter = (ciphers, defaultCiphers, 
supportedCiphers) -> {
+      invoked.set(true);
+      return SupportedCipherSuiteFilter.INSTANCE
+          .filterCipherSuites(null, defaultCiphers, supportedCiphers);
+    };
+    final GrpcTlsConfig conf = GrpcTlsConfig.newBuilder()
+        .setCipherSuites("TLS_TEST_CIPHER")
+        .setCipherSuiteFilter(filter)
+        .build();
+
+    final SslContext context = GrpcUtil.configureSslContextBuilder(
+        SslContextBuilder.forClient(), conf, null).build();
+    try {
+      Assertions.assertTrue(invoked.get());
+      Assertions.assertSame(filter, conf.getCipherSuiteFilter());
+    } finally {
+      ReferenceCountUtil.release(context);
+    }
+  }
+
   @Test
   public void testUnknownJsseProviderUsesGenericJdkConfiguration() {
     final SslContextBuilder builder = GrpcUtil.configureJsseProvider(
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 b74fa92ce..ca913d7bc 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
@@ -36,6 +36,7 @@ import 
org.apache.ratis.thirdparty.io.netty.channel.socket.SocketChannel;
 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.IdentityCipherSuiteFilter;
 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;
@@ -144,6 +145,24 @@ public class TestTlsConfWithNetty {
     Assertions.assertEquals(Collections.singletonList(VALID_CIPHER), 
ctx.cipherSuites());
   }
 
+  /** The configured cipher suite filter must be passed to the Netty {@link 
SslContext}. See RATIS-2602. */
+  @Test
+  public void testIdentityCipherSuiteFilterIsApplied() {
+    final List<String> ciphers = Arrays.asList(UNSUPPORTED_CIPHER, 
VALID_CIPHER);
+    final TlsConf conf = TlsConf.newBuilder()
+        .setName("server")
+        .setPrivateKey(new 
TlsConf.PrivateKeyConf(SecurityTestUtils.getResource("ssl/server.pem")))
+        .setKeyCertificates(new 
TlsConf.CertificatesConf(SecurityTestUtils.getResource("ssl/server.crt")))
+        .setSslProvider(SslProvider.JDK)
+        .setProtocols(TLS_1_2)
+        .setCipherSuites(ciphers)
+        .setCipherSuiteFilter(IdentityCipherSuiteFilter.INSTANCE)
+        .build();
+
+    final SslContext ctx = NettyUtils.buildSslContextForServer(conf);
+    Assertions.assertEquals(ciphers, 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.

Reply via email to