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

jonmeredith pushed a commit to branch cassandra-4.1
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/cassandra-4.1 by this push:
     new b9586501a6 Internode legacy SSL storage port certificate is not hot 
reloaded on update
b9586501a6 is described below

commit b9586501a6b6cdfe465302448018785652c9b966
Author: Jon Meredith <[email protected]>
AuthorDate: Thu Sep 21 16:07:29 2023 -0600

    Internode legacy SSL storage port certificate is not hot reloaded on update
    
    patch by Jon Meredith; reviewed by Dinesh Joshi, Francisco Guerrero for 
CASSANDRA-18681
---
 CHANGES.txt                                        |   1 +
 .../cassandra/net/InboundConnectionInitiator.java  |   3 +-
 .../org/apache/cassandra/net/InboundSockets.java   |  12 +++
 .../org/apache/cassandra/net/MessagingService.java |   1 +
 .../cassandra/net/MessagingServiceMBeanImpl.java   |  10 +-
 .../cassandra/net/OutboundConnectionInitiator.java |   4 +-
 .../org/apache/cassandra/security/SSLFactory.java  |  82 +++++++++-------
 .../cassandra/transport/PipelineConfigurator.java  |   8 +-
 .../apache/cassandra/transport/SimpleClient.java   |   3 +-
 .../test/AbstractEncryptionOptionsImpl.java        |   2 +-
 .../security/DefaultSslContextFactoryTest.java     |   2 +-
 .../security/PEMBasedSslContextFactoryTest.java    |   2 +-
 .../apache/cassandra/security/SSLFactoryTest.java  | 107 ++++++++++++++-------
 13 files changed, 153 insertions(+), 84 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index adc7860393..d902ca1e1c 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.1.4
+ * Internode legacy SSL storage port certificate is not hot reloaded on update 
(CASSANDRA-18681)
  * Nodetool paxos-only repair is no longer incremental (CASSANDRA-18466)
  * Waiting indefinitely on ReceivedMessage response in StreamSession#receive() 
can cause deadlock (CASSANDRA-18733)
  * Allow empty keystore_password in encryption_options (CASSANDRA-18778)
diff --git a/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java 
b/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java
index 807d0262d8..3067b587c9 100644
--- a/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java
+++ b/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java
@@ -549,7 +549,8 @@ public class InboundConnectionInitiator
     {
         final boolean verifyPeerCertificate = true;
         SslContext sslContext = 
SSLFactory.getOrCreateSslContext(encryptionOptions, verifyPeerCertificate,
-                                                                 
ISslContextFactory.SocketType.SERVER);
+                                                                 
ISslContextFactory.SocketType.SERVER,
+                                                                 
SSL_FACTORY_CONTEXT_DESCRIPTION);
         InetSocketAddress peer = 
encryptionOptions.require_endpoint_verification ? (InetSocketAddress) 
channel.remoteAddress() : null;
         SslHandler sslHandler = newSslHandler(channel, sslContext, peer);
         logger.trace("{} inbound netty SslContext: context={}, engine={}", 
description, sslContext.getClass().getName(), 
sslHandler.engine().getClass().getName());
diff --git a/src/java/org/apache/cassandra/net/InboundSockets.java 
b/src/java/org/apache/cassandra/net/InboundSockets.java
index 58cd88e6d9..80309aa443 100644
--- a/src/java/org/apache/cassandra/net/InboundSockets.java
+++ b/src/java/org/apache/cassandra/net/InboundSockets.java
@@ -24,6 +24,8 @@ import java.util.function.Consumer;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
@@ -43,6 +45,7 @@ import org.apache.cassandra.utils.concurrent.FutureCombiner;
 
 class InboundSockets
 {
+    private static Logger logger = 
LoggerFactory.getLogger(InboundSockets.class);
     /**
      * A simple struct to wrap up the components needed for each listening 
socket.
      */
@@ -219,6 +222,15 @@ class InboundSockets
 
         if (settings.encryption.legacy_ssl_storage_port_enabled)
         {
+            // Initialize hot reloading here rather than in 
org.apache.cassandra.security.SSLFactory.initHotReloading
+            // as the legacySettings.encryption.sslContextFactory is not 
shared outside the messaging system.
+            // Any SslContexts created will be checked when 
checkCertFilesForHotReloading is called if initialization
+            // is successful.
+            try {
+                
legacySettings.encryption.sslContextFactoryInstance.initHotReloading();
+            } catch (Throwable tr) {
+                logger.warn("Unable to initialize hot reloading for legacy 
internode socket - continuing disabled", tr);
+            }
             out.add(new InboundSocket(legacySettings));
 
             /*
diff --git a/src/java/org/apache/cassandra/net/MessagingService.java 
b/src/java/org/apache/cassandra/net/MessagingService.java
index f14835661e..910623aae6 100644
--- a/src/java/org/apache/cassandra/net/MessagingService.java
+++ b/src/java/org/apache/cassandra/net/MessagingService.java
@@ -213,6 +213,7 @@ public class MessagingService extends 
MessagingServiceMBeanImpl
     public static final int current_version = VERSION_40;
     static AcceptVersions accept_messaging = new 
AcceptVersions(minimum_version, current_version);
     static AcceptVersions accept_streaming = new 
AcceptVersions(current_version, current_version);
+    public static String SSL_FACTORY_CONTEXT_DESCRIPTION = 
"server_encryption_options";
 
     public enum Version
     {
diff --git a/src/java/org/apache/cassandra/net/MessagingServiceMBeanImpl.java 
b/src/java/org/apache/cassandra/net/MessagingServiceMBeanImpl.java
index b77fa8396e..3b89834a31 100644
--- a/src/java/org/apache/cassandra/net/MessagingServiceMBeanImpl.java
+++ b/src/java/org/apache/cassandra/net/MessagingServiceMBeanImpl.java
@@ -17,15 +17,12 @@
  */
 package org.apache.cassandra.net;
 
-import java.io.IOException;
 import java.net.UnknownHostException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
-import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.config.EncryptionOptions;
 import org.apache.cassandra.locator.InetAddressAndPort;
 import org.apache.cassandra.metrics.InternodeOutboundMetrics;
 import org.apache.cassandra.metrics.MessagingMetrics;
@@ -276,12 +273,9 @@ public class MessagingServiceMBeanImpl implements 
MessagingServiceMBean
     }
 
     @Override
-    public void reloadSslCertificates() throws IOException
+    public void reloadSslCertificates()
     {
-        final EncryptionOptions.ServerEncryptionOptions serverOpts = 
DatabaseDescriptor.getInternodeMessagingEncyptionOptions();
-        final EncryptionOptions clientOpts = 
DatabaseDescriptor.getNativeProtocolEncryptionOptions();
-        SSLFactory.validateSslCerts(serverOpts, clientOpts);
-        SSLFactory.checkCertFilesForHotReloading(serverOpts, clientOpts);
+        SSLFactory.forceCheckCertFiles();
     }
 
     @Override
diff --git a/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java 
b/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java
index a187068ce7..5de2a080d9 100644
--- a/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java
+++ b/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java
@@ -59,6 +59,7 @@ import org.apache.cassandra.utils.JVMStabilityInspector;
 import org.apache.cassandra.utils.memory.BufferPools;
 
 import static java.util.concurrent.TimeUnit.*;
+import static 
org.apache.cassandra.net.MessagingService.SSL_FACTORY_CONTEXT_DESCRIPTION;
 import static org.apache.cassandra.net.MessagingService.VERSION_40;
 import static org.apache.cassandra.net.HandshakeProtocol.*;
 import static org.apache.cassandra.net.ConnectionType.STREAMING;
@@ -203,7 +204,8 @@ public class OutboundConnectionInitiator<SuccessType 
extends OutboundConnectionI
             {
                 // check if we should actually encrypt this connection
                 SslContext sslContext = 
SSLFactory.getOrCreateSslContext(settings.encryption, true,
-                                                                         
ISslContextFactory.SocketType.CLIENT);
+                                                                         
ISslContextFactory.SocketType.CLIENT,
+                                                                         
SSL_FACTORY_CONTEXT_DESCRIPTION);
                 // for some reason channel.remoteAddress() will return null
                 InetAddressAndPort address = settings.to;
                 InetSocketAddress peer = 
settings.encryption.require_endpoint_verification ? new 
InetSocketAddress(address.getAddress(), address.getPort()) : null;
diff --git a/src/java/org/apache/cassandra/security/SSLFactory.java 
b/src/java/org/apache/cassandra/security/SSLFactory.java
index e06da1f0cb..51bde34258 100644
--- a/src/java/org/apache/cassandra/security/SSLFactory.java
+++ b/src/java/org/apache/cassandra/security/SSLFactory.java
@@ -21,6 +21,7 @@ package org.apache.cassandra.security;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -42,7 +43,6 @@ import io.netty.handler.ssl.SslContext;
 import io.netty.util.ReferenceCountUtil;
 import org.apache.cassandra.concurrent.ScheduledExecutors;
 import org.apache.cassandra.config.Config;
-import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.EncryptionOptions;
 import org.apache.cassandra.security.ISslContextFactory.SocketType;
 
@@ -130,9 +130,10 @@ public final class SSLFactory
      * get a netty {@link SslContext} instance
      */
     public static SslContext getOrCreateSslContext(EncryptionOptions options, 
boolean verifyPeerCertificate,
-                                                   SocketType socketType) 
throws IOException
+                                                   SocketType socketType,
+                                                   String contextDescription) 
throws IOException
     {
-        CacheKey key = new CacheKey(options, socketType);
+        CacheKey key = new CacheKey(options, socketType, contextDescription);
         SslContext sslContext;
 
         sslContext = cachedSslContexts.get(key);
@@ -175,42 +176,47 @@ public final class SSLFactory
      * @throws IllegalStateException if {@link 
#initHotReloading(EncryptionOptions.ServerEncryptionOptions, EncryptionOptions, 
boolean)}
      *                               is not called first
      */
-    public static void 
checkCertFilesForHotReloading(EncryptionOptions.ServerEncryptionOptions 
serverOpts,
-                                                     EncryptionOptions 
clientOpts)
+    public static void checkCertFilesForHotReloading()
     {
         if (!isHotReloadingInitialized)
             throw new IllegalStateException("Hot reloading functionality has 
not been initialized.");
+        checkCachedContextsForReload(false);
+    }
 
-        logger.debug("Checking whether certificates have been updated for 
server {} and client {}",
-                     
serverOpts.sslContextFactoryInstance.getClass().getName(), 
clientOpts.sslContextFactoryInstance.getClass().getName());
-
-        if (serverOpts != null)
-        {
-            checkCertFilesForHotReloading(serverOpts, 
"server_encryption_options", true);
-        }
-        if (clientOpts != null)
-        {
-            checkCertFilesForHotReloading(clientOpts, 
"client_encryption_options", clientOpts.require_client_auth);
-        }
+    /**
+     * Forces revalidation and loading of SSL certifcates if valid
+     */
+    public static void forceCheckCertFiles()
+    {
+        checkCachedContextsForReload(true);
     }
 
-    private static void checkCertFilesForHotReloading(EncryptionOptions 
options, String contextDescription,
-                                                      boolean 
verifyPeerCertificate)
+    private static void checkCachedContextsForReload(boolean forceReload)
     {
-        try
+        List<CacheKey> keysToCheck = new 
ArrayList<>(Collections.list(cachedSslContexts.keys()));
+        while (!keysToCheck.isEmpty())
         {
-            if (options.sslContextFactoryInstance.shouldReload())
+            CacheKey key = keysToCheck.remove(keysToCheck.size()-1);
+            final EncryptionOptions opts = key.encryptionOptions;
+
+            logger.debug("Checking whether certificates have been updated for 
{}", key.contextDescription);
+            if (forceReload || opts.sslContextFactoryInstance.shouldReload())
             {
-                logger.info("SSL certificates have been updated for {}. 
Resetting the ssl contexts for new " +
-                            "connections.", options.getClass().getName());
-                validateSslContext(contextDescription, options, 
verifyPeerCertificate, false);
-                clearSslContextCache(options);
+                try
+                {
+                    validateSslContext(key.contextDescription, opts,
+                            opts instanceof 
EncryptionOptions.ServerEncryptionOptions || opts.require_client_auth, false);
+                    logger.info("SSL certificates have been updated for {}. 
Resetting the ssl contexts for new " +
+                                "connections.", key.contextDescription);
+                    clearSslContextCache(key.encryptionOptions, keysToCheck);
+                }
+                catch (Throwable tr)
+                {
+                    logger.error("Failed to hot reload the SSL Certificates! 
Please check the certificate files for {}.",
+                                 key.contextDescription, tr);
+                }
             }
         }
-        catch(Exception e)
-        {
-            logger.error("Failed to hot reload the SSL Certificates! Please 
check the certificate files.", e);
-        }
     }
 
     /**
@@ -225,12 +231,16 @@ public final class SSLFactory
         cachedSslContexts.clear();
     }
 
-    private static void clearSslContextCache(EncryptionOptions options)
+    /**
+     * Clear all cachedSslContexts with this encryption option and remove them 
from future keys to check
+     */
+    private static void clearSslContextCache(EncryptionOptions options, 
List<CacheKey> keysToCheck)
     {
         cachedSslContexts.forEachKey(1, cacheKey -> {
-            if (cacheKey.encryptionOptions.equals(options))
+            if (Objects.equals(options, cacheKey.encryptionOptions))
             {
                 cachedSslContexts.remove(cacheKey);
+                keysToCheck.remove(cacheKey);
             }
         });
     }
@@ -261,9 +271,7 @@ public final class SSLFactory
         if (!isHotReloadingInitialized)
         {
             ScheduledExecutors.scheduledTasks
-                .scheduleWithFixedDelay(() -> checkCertFilesForHotReloading(
-                                                
DatabaseDescriptor.getInternodeMessagingEncyptionOptions(),
-                                                
DatabaseDescriptor.getNativeProtocolEncryptionOptions()),
+                
.scheduleWithFixedDelay(SSLFactory::checkCertFilesForHotReloading,
                                         DEFAULT_HOT_RELOAD_INITIAL_DELAY_SEC,
                                         DEFAULT_HOT_RELOAD_PERIOD_SEC, 
TimeUnit.SECONDS);
         }
@@ -420,11 +428,13 @@ public final class SSLFactory
     {
         private final EncryptionOptions encryptionOptions;
         private final SocketType socketType;
+        private final String contextDescription;
 
-        public CacheKey(EncryptionOptions encryptionOptions, SocketType 
socketType)
+        public CacheKey(EncryptionOptions encryptionOptions, SocketType 
socketType, String contextDescription)
         {
             this.encryptionOptions = encryptionOptions;
             this.socketType = socketType;
+            this.contextDescription = contextDescription;
         }
 
         public boolean equals(Object o)
@@ -433,7 +443,8 @@ public final class SSLFactory
             if (o == null || getClass() != o.getClass()) return false;
             CacheKey cacheKey = (CacheKey) o;
             return (socketType == cacheKey.socketType &&
-                    Objects.equals(encryptionOptions, 
cacheKey.encryptionOptions));
+                    Objects.equals(encryptionOptions, 
cacheKey.encryptionOptions) &&
+                    Objects.equals(contextDescription, 
cacheKey.contextDescription));
         }
 
         public int hashCode()
@@ -441,6 +452,7 @@ public final class SSLFactory
             int result = 0;
             result += 31 * socketType.hashCode();
             result += 31 * encryptionOptions.hashCode();
+            result += 31 * contextDescription.hashCode();
             return result;
         }
     }
diff --git a/src/java/org/apache/cassandra/transport/PipelineConfigurator.java 
b/src/java/org/apache/cassandra/transport/PipelineConfigurator.java
index 81ff13605e..cefba02c59 100644
--- a/src/java/org/apache/cassandra/transport/PipelineConfigurator.java
+++ b/src/java/org/apache/cassandra/transport/PipelineConfigurator.java
@@ -63,6 +63,8 @@ public class PipelineConfigurator
     // which will throttle a system under any normal load.
     private static final boolean DEBUG = 
Boolean.getBoolean("cassandra.unsafe_verbose_debug_client_protocol");
 
+    public static final String SSL_FACTORY_CONTEXT_DESCRIPTION = 
"client_encryption_options";
+
     // Stateless handlers
     private static final ConnectionLimitHandler connectionLimitHandler = new 
ConnectionLimitHandler();
 
@@ -164,7 +166,8 @@ public class PipelineConfigurator
                 return channel -> {
                     SslContext sslContext = 
SSLFactory.getOrCreateSslContext(encryptionOptions,
                                                                              
encryptionOptions.require_client_auth,
-                                                                             
ISslContextFactory.SocketType.SERVER);
+                                                                             
ISslContextFactory.SocketType.SERVER,
+                                                                             
SSL_FACTORY_CONTEXT_DESCRIPTION);
 
                     channel.pipeline().addFirst(SSL_HANDLER, new 
ByteToMessageDecoder()
                     {
@@ -198,7 +201,8 @@ public class PipelineConfigurator
                 return channel -> {
                     SslContext sslContext = 
SSLFactory.getOrCreateSslContext(encryptionOptions,
                                                                              
encryptionOptions.require_client_auth,
-                                                                             
ISslContextFactory.SocketType.SERVER);
+                                                                             
ISslContextFactory.SocketType.SERVER,
+                                                                             
SSL_FACTORY_CONTEXT_DESCRIPTION);
                     channel.pipeline().addFirst(SSL_HANDLER, 
sslContext.newHandler(channel.alloc()));
                 };
             default:
diff --git a/src/java/org/apache/cassandra/transport/SimpleClient.java 
b/src/java/org/apache/cassandra/transport/SimpleClient.java
index 43bb8addee..5ea0190aea 100644
--- a/src/java/org/apache/cassandra/transport/SimpleClient.java
+++ b/src/java/org/apache/cassandra/transport/SimpleClient.java
@@ -54,6 +54,7 @@ import 
org.apache.cassandra.utils.concurrent.UncheckedInterruptedException;
 
 import static org.apache.cassandra.transport.CQLMessageHandler.envelopeSize;
 import static org.apache.cassandra.transport.Flusher.MAX_FRAMED_PAYLOAD_SIZE;
+import static 
org.apache.cassandra.transport.PipelineConfigurator.SSL_FACTORY_CONTEXT_DESCRIPTION;
 import static 
org.apache.cassandra.utils.concurrent.NonBlockingRateLimiter.NO_OP_LIMITER;
 
 import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis;
@@ -623,7 +624,7 @@ public class SimpleClient implements Closeable
         {
             super.initChannel(channel);
             SslContext sslContext = 
SSLFactory.getOrCreateSslContext(encryptionOptions, 
encryptionOptions.require_client_auth,
-                                                                     
ISslContextFactory.SocketType.CLIENT);
+                                                                     
ISslContextFactory.SocketType.CLIENT, SSL_FACTORY_CONTEXT_DESCRIPTION);
             channel.pipeline().addFirst("ssl", 
sslContext.newHandler(channel.alloc()));
         }
     }
diff --git 
a/test/distributed/org/apache/cassandra/distributed/test/AbstractEncryptionOptionsImpl.java
 
b/test/distributed/org/apache/cassandra/distributed/test/AbstractEncryptionOptionsImpl.java
index 1c5ddbf6b5..b488867433 100644
--- 
a/test/distributed/org/apache/cassandra/distributed/test/AbstractEncryptionOptionsImpl.java
+++ 
b/test/distributed/org/apache/cassandra/distributed/test/AbstractEncryptionOptionsImpl.java
@@ -200,7 +200,7 @@ public class AbstractEncryptionOptionsImpl extends 
TestBaseImpl
 
             SslContext sslContext = SSLFactory.getOrCreateSslContext(
                 
encryptionOptions.withAcceptedProtocols(acceptedProtocols).withCipherSuites(cipherSuites),
-                true, ISslContextFactory.SocketType.CLIENT);
+                true, ISslContextFactory.SocketType.CLIENT, "test");
 
             EventLoopGroup workerGroup = new NioEventLoopGroup();
             Bootstrap b = new Bootstrap();
diff --git 
a/test/unit/org/apache/cassandra/security/DefaultSslContextFactoryTest.java 
b/test/unit/org/apache/cassandra/security/DefaultSslContextFactoryTest.java
index 0657eb67a6..bec9d20042 100644
--- a/test/unit/org/apache/cassandra/security/DefaultSslContextFactoryTest.java
+++ b/test/unit/org/apache/cassandra/security/DefaultSslContextFactoryTest.java
@@ -62,7 +62,7 @@ public class DefaultSslContextFactoryTest
                                                            
.withKeyStorePassword("cassandra")
                                                            
.withRequireClientAuth(false)
                                                            
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA");
-        SslContext sslContext = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT);
+        SslContext sslContext = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT, "test");
         Assert.assertNotNull(sslContext);
         if (OpenSsl.isAvailable())
             Assert.assertTrue(sslContext instanceof OpenSslContext);
diff --git 
a/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java 
b/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java
index 243d300539..f44e34492e 100644
--- a/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java
+++ b/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java
@@ -216,7 +216,7 @@ public class PEMBasedSslContextFactoryTest
                                                            
.withRequireClientAuth(false)
                                                            
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA")
                                                            
.withSslContextFactory(sslContextFactory);
-        SslContext sslContext = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT);
+        SslContext sslContext = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT, "test");
         Assert.assertNotNull(sslContext);
         if (OpenSsl.isAvailable())
             Assert.assertTrue(sslContext instanceof OpenSslContext);
diff --git a/test/unit/org/apache/cassandra/security/SSLFactoryTest.java 
b/test/unit/org/apache/cassandra/security/SSLFactoryTest.java
index e5aa4b1057..91e4062be4 100644
--- a/test/unit/org/apache/cassandra/security/SSLFactoryTest.java
+++ b/test/unit/org/apache/cassandra/security/SSLFactoryTest.java
@@ -19,7 +19,10 @@
 package org.apache.cassandra.security;
 
 import org.apache.cassandra.io.util.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.security.KeyStore;
 import java.security.cert.CertificateException;
 import java.util.HashMap;
 import java.util.Map;
@@ -61,6 +64,7 @@ public class SSLFactoryTest
     @Before
     public void setup()
     {
+        SSLFactory.clearSslContextCache();
         encryptionOptions = new ServerEncryptionOptions()
                             
.withTrustStore("test/conf/cassandra_ssl_test.truststore")
                             .withTrustStorePassword("cassandra")
@@ -91,20 +95,24 @@ public class SSLFactoryTest
         {
             ServerEncryptionOptions options = 
addKeystoreOptions(encryptionOptions)
                                               
.withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all);
+            ServerEncryptionOptions legacyOptions = 
options.withOptional(false).withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all);
+            options.sslContextFactoryInstance.initHotReloading();
+            legacyOptions.sslContextFactoryInstance.initHotReloading();
 
-            SSLFactory.initHotReloading(options, options, true);
-
-            SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT);
+            SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT, "test");
+            SslContext oldLegacyCtx = 
SSLFactory.getOrCreateSslContext(legacyOptions, true, 
ISslContextFactory.SocketType.CLIENT, "test legacy");
             File keystoreFile = new File(options.keystore);
 
-            SSLFactory.checkCertFilesForHotReloading(options, options);
+            SSLFactory.checkCertFilesForHotReloading();
 
             keystoreFile.trySetLastModified(System.currentTimeMillis() + 
15000);
 
-            SSLFactory.checkCertFilesForHotReloading(options, options);
-            SslContext newCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT);
+            SSLFactory.checkCertFilesForHotReloading();
+            SslContext newCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT, "test");
+            SslContext newLegacyCtx = 
SSLFactory.getOrCreateSslContext(legacyOptions, true, 
ISslContextFactory.SocketType.CLIENT, "test legacy");
 
             Assert.assertNotSame(oldCtx, newCtx);
+            Assert.assertNotSame(oldLegacyCtx, newLegacyCtx);
         }
         catch (Exception e)
         {
@@ -122,21 +130,26 @@ public class SSLFactoryTest
         try
         {
             ServerEncryptionOptions options = 
addPEMKeystoreOptions(encryptionOptions)
-                                              
.withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all);
-
-            SSLFactory.initHotReloading(options, options, true);
-
-            SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT);
+                                              
.withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.dc);
+            // emulate InboundSockets and share the cert but with different 
options, no extra hot reloading init
+            ServerEncryptionOptions legacyOptions = 
options.withOptional(false).withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all);
+            options.sslContextFactoryInstance.initHotReloading();
+            legacyOptions.sslContextFactoryInstance.initHotReloading();
+
+            SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT, "test");
+            SslContext oldLegacyCtx = 
SSLFactory.getOrCreateSslContext(legacyOptions, true, 
ISslContextFactory.SocketType.CLIENT, "test legacy");
             File keystoreFile = new File(options.keystore);
 
-            SSLFactory.checkCertFilesForHotReloading(options, options);
+            SSLFactory.checkCertFilesForHotReloading();
 
             keystoreFile.trySetLastModified(System.currentTimeMillis() + 
15000);
 
-            SSLFactory.checkCertFilesForHotReloading(options, options);
-            SslContext newCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT);
+            SSLFactory.checkCertFilesForHotReloading();
+            SslContext newCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT, "test");
+            SslContext newLegacyCtx = 
SSLFactory.getOrCreateSslContext(legacyOptions, true, 
ISslContextFactory.SocketType.CLIENT, "test legacy");
 
             Assert.assertNotSame(oldCtx, newCtx);
+            Assert.assertNotSame(oldLegacyCtx, newLegacyCtx);
         }
         catch (Exception e)
         {
@@ -164,20 +177,26 @@ public class SSLFactoryTest
         try
         {
             ServerEncryptionOptions options = 
addKeystoreOptions(encryptionOptions);
+            // emulate InboundSockets and share the cert but with different 
options, no extra hot reloading init
+            ServerEncryptionOptions legacyOptions = 
options.withOptional(false).withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all);
 
-            SSLFactory.initHotReloading(options, options, true);
-            SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT);
-            File keystoreFile = new File(options.keystore);
+            File testKeystoreFile = new File(options.keystore + ".test");
+            FileUtils.copyFile(new File(options.keystore).toJavaIOFile(), 
testKeystoreFile.toJavaIOFile());
+            options = options.withKeyStore(testKeystoreFile.path());
 
-            SSLFactory.checkCertFilesForHotReloading(options, options);
-            keystoreFile.trySetLastModified(System.currentTimeMillis() + 5000);
+            SSLFactory.initHotReloading(options, options, true);  // 
deliberately not initializing with legacyOptions to match 
InboundSockets.addBindings
 
-            ServerEncryptionOptions modOptions = new 
ServerEncryptionOptions(options)
-                                                 .withKeyStorePassword("bad 
password");
-            SSLFactory.checkCertFilesForHotReloading(modOptions, modOptions);
-            SslContext newCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT);
+            SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT, "test");
+            SslContext oldLegacyCtx = 
SSLFactory.getOrCreateSslContext(options, true, 
ISslContextFactory.SocketType.CLIENT, "test legacy");
+
+            changeKeystorePassword(options.keystore, 
options.keystore_password, "bad password");
+
+            SSLFactory.checkCertFilesForHotReloading();
+            SslContext newCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT, "test");
+            SslContext newLegacyCtx = 
SSLFactory.getOrCreateSslContext(options, true, 
ISslContextFactory.SocketType.CLIENT, "test legacy");
 
             Assert.assertSame(oldCtx, newCtx);
+            Assert.assertSame(oldLegacyCtx, newLegacyCtx);
         }
         finally
         {
@@ -198,14 +217,14 @@ public class SSLFactoryTest
 
 
             SSLFactory.initHotReloading(options, options, true);
-            SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT);
-            SSLFactory.checkCertFilesForHotReloading(options, options);
+            SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT, "test");
+            SSLFactory.checkCertFilesForHotReloading();
 
             testKeystoreFile.trySetLastModified(System.currentTimeMillis() + 
15000);
             FileUtils.forceDelete(testKeystoreFile.toJavaIOFile());
 
-            SSLFactory.checkCertFilesForHotReloading(options, options);
-            SslContext newCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT);
+            SSLFactory.checkCertFilesForHotReloading();
+            SslContext newCtx = SSLFactory.getOrCreateSslContext(options, 
true, ISslContextFactory.SocketType.CLIENT, "test");
 
             Assert.assertSame(oldCtx, newCtx);
         }
@@ -228,7 +247,7 @@ public class SSLFactoryTest
                                     
.withCipherSuites("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
 
         SslContext ctx1 = SSLFactory.getOrCreateSslContext(options, true,
-                                                           
ISslContextFactory.SocketType.SERVER);
+                                                           
ISslContextFactory.SocketType.SERVER, "test");
 
         Assert.assertTrue(ctx1.isServer());
         Assert.assertEquals(ctx1.cipherSuites(), options.cipher_suites);
@@ -236,7 +255,7 @@ public class SSLFactoryTest
         options = 
options.withCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
 
         SslContext ctx2 = SSLFactory.getOrCreateSslContext(options, true,
-                                                           
ISslContextFactory.SocketType.CLIENT);
+                                                           
ISslContextFactory.SocketType.CLIENT, "test");
 
         Assert.assertTrue(ctx2.isClient());
         Assert.assertEquals(ctx2.cipherSuites(), options.cipher_suites);
@@ -255,7 +274,7 @@ public class SSLFactoryTest
         .withRequireClientAuth(true)
         .withRequireEndpointVerification(false);
 
-        SSLFactory.CacheKey cacheKey1 = new 
SSLFactory.CacheKey(encryptionOptions1, ISslContextFactory.SocketType.SERVER
+        SSLFactory.CacheKey cacheKey1 = new 
SSLFactory.CacheKey(encryptionOptions1, ISslContextFactory.SocketType.SERVER, 
"test"
         );
 
         Map<String,String> parameters2 = new HashMap<>();
@@ -268,7 +287,7 @@ public class SSLFactoryTest
         .withRequireClientAuth(true)
         .withRequireEndpointVerification(false);
 
-        SSLFactory.CacheKey cacheKey2 = new 
SSLFactory.CacheKey(encryptionOptions2, ISslContextFactory.SocketType.SERVER
+        SSLFactory.CacheKey cacheKey2 = new 
SSLFactory.CacheKey(encryptionOptions2, ISslContextFactory.SocketType.SERVER, 
"test"
         );
 
         Assert.assertEquals(cacheKey1, cacheKey2);
@@ -285,7 +304,7 @@ public class SSLFactoryTest
         .withSslContextFactory(new 
ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters1))
         .withProtocol("TLSv1.1");
 
-        SSLFactory.CacheKey cacheKey1 = new 
SSLFactory.CacheKey(encryptionOptions1, ISslContextFactory.SocketType.SERVER
+        SSLFactory.CacheKey cacheKey1 = new 
SSLFactory.CacheKey(encryptionOptions1, ISslContextFactory.SocketType.SERVER, 
"test"
         );
 
         Map<String,String> parameters2 = new HashMap<>();
@@ -296,9 +315,31 @@ public class SSLFactoryTest
         .withSslContextFactory(new 
ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters2))
         .withProtocol("TLSv1.1");
 
-        SSLFactory.CacheKey cacheKey2 = new 
SSLFactory.CacheKey(encryptionOptions2, ISslContextFactory.SocketType.SERVER
+        SSLFactory.CacheKey cacheKey2 = new 
SSLFactory.CacheKey(encryptionOptions2, ISslContextFactory.SocketType.SERVER, 
"test"
         );
 
         Assert.assertNotEquals(cacheKey1, cacheKey2);
     }
+
+    void changeKeystorePassword(String filename, String currentPassword, 
String newPassword)
+    {
+        try
+        {
+            KeyStore keystore = 
KeyStore.getInstance(KeyStore.getDefaultType());
+            char[] loadPasswd = currentPassword.toCharArray();
+            char[] storePasswd = newPassword.toCharArray();
+            try (FileInputStream is = new FileInputStream(filename))
+            {
+                keystore.load(is, loadPasswd);
+            }
+            try (FileOutputStream os = new FileOutputStream(filename))
+            {
+                keystore.store(os, storePasswd);
+            }
+        }
+        catch (Throwable tr)
+        {
+            throw new RuntimeException(tr);
+        }
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to