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);