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

aweisberg pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 367cdc9  SSL Cert Hot Reloading should check for sanity of the new 
keystore/truststore before loading it
367cdc9 is described below

commit 367cdc95514d4550db57054c90fb794fc29179d1
Author: Ariel Weisberg <[email protected]>
AuthorDate: Fri Feb 8 11:55:38 2019 -0500

    SSL Cert Hot Reloading should check for sanity of the new 
keystore/truststore before loading it
    
    Patch by Dinesh Joshi; reviewed by Ariel Weisberg for CASSANDRA-14991
---
 CHANGES.txt                                        |   1 +
 .../cassandra/config/DatabaseDescriptor.java       |   8 +-
 .../org/apache/cassandra/net/MessagingService.java |   8 +-
 .../cassandra/net/MessagingServiceMBean.java       |   3 +-
 .../apache/cassandra/net/async/NettyFactory.java   |   4 +-
 .../cassandra/net/async/OptionalSslHandler.java    |   2 +-
 .../org/apache/cassandra/security/SSLFactory.java  | 109 ++++++++++++-------
 src/java/org/apache/cassandra/tools/NodeProbe.java |   2 +-
 .../cassandra/tools/ReloadSslCertificates.java     |  13 ++-
 .../org/apache/cassandra/transport/Server.java     |   3 +-
 .../apache/cassandra/transport/SimpleClient.java   |   2 +-
 .../apache/cassandra/security/SSLFactoryTest.java  | 117 ++++++++++++++++++---
 12 files changed, 207 insertions(+), 65 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index e8d4598..89c558a8 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.0
+ * SSL Cert Hot Reloading should check for sanity of the new 
keystore/truststore before loading it (CASSANDRA-14991)
  * Avoid leaking threads when failing anticompactions and rate limit 
anticompactions (CASSANDRA-15002)
  * Validate token() arguments early instead of throwing NPE at execution 
(CASSANDRA-14989)
  * Add a new tool to dump audit logs (CASSANDRA-14885)
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java 
b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index 5feaea3..9b7a6e5 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -902,7 +902,13 @@ public class DatabaseDescriptor
 
     public static void applySslContextHotReload()
     {
-        SSLFactory.initHotReloading(conf.server_encryption_options, 
conf.client_encryption_options, false);
+        try
+        {
+            SSLFactory.initHotReloading(conf.server_encryption_options, 
conf.client_encryption_options, false);
+        } catch(IOException e)
+        {
+            throw new ConfigurationException("Failed to initialize SSL hot 
reloading", e);
+        }
     }
 
     public static void applySeedProvider()
diff --git a/src/java/org/apache/cassandra/net/MessagingService.java 
b/src/java/org/apache/cassandra/net/MessagingService.java
index f5c064e..f72cd61 100644
--- a/src/java/org/apache/cassandra/net/MessagingService.java
+++ b/src/java/org/apache/cassandra/net/MessagingService.java
@@ -56,6 +56,7 @@ import org.apache.cassandra.concurrent.ScheduledExecutors;
 import org.apache.cassandra.concurrent.Stage;
 import org.apache.cassandra.concurrent.StageManager;
 import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.config.EncryptionOptions;
 import org.apache.cassandra.config.EncryptionOptions.ServerEncryptionOptions;
 import org.apache.cassandra.db.ColumnFamilyStore;
 import org.apache.cassandra.db.ConsistencyLevel;
@@ -1716,8 +1717,11 @@ public final class MessagingService implements 
MessagingServiceMBean
     }
 
     @Override
-    public void reloadSslCertificates()
+    public void reloadSslCertificates() throws IOException
     {
-        SSLFactory.checkCertFilesForHotReloading();
+        final ServerEncryptionOptions serverOpts = 
DatabaseDescriptor.getInternodeMessagingEncyptionOptions();
+        final EncryptionOptions clientOpts = 
DatabaseDescriptor.getNativeProtocolEncryptionOptions();
+        SSLFactory.validateSslCerts(serverOpts, clientOpts);
+        SSLFactory.checkCertFilesForHotReloading(serverOpts, clientOpts);
     }
 }
diff --git a/src/java/org/apache/cassandra/net/MessagingServiceMBean.java 
b/src/java/org/apache/cassandra/net/MessagingServiceMBean.java
index 6adb891..732a5ed 100644
--- a/src/java/org/apache/cassandra/net/MessagingServiceMBean.java
+++ b/src/java/org/apache/cassandra/net/MessagingServiceMBean.java
@@ -19,6 +19,7 @@ package org.apache.cassandra.net;
 
 
 
+import java.io.IOException;
 import java.net.UnknownHostException;
 import java.util.Map;
 
@@ -130,5 +131,5 @@ public interface MessagingServiceMBean
 
     public int getVersion(String address) throws UnknownHostException;
 
-    void reloadSslCertificates();
+    void reloadSslCertificates() throws IOException;
 }
diff --git a/src/java/org/apache/cassandra/net/async/NettyFactory.java 
b/src/java/org/apache/cassandra/net/async/NettyFactory.java
index 81de5d8..3752927 100644
--- a/src/java/org/apache/cassandra/net/async/NettyFactory.java
+++ b/src/java/org/apache/cassandra/net/async/NettyFactory.java
@@ -292,7 +292,7 @@ public final class NettyFactory
                 }
                 else
                 {
-                    SslContext sslContext = 
SSLFactory.getSslContext(encryptionOptions, true, 
SSLFactory.ConnectionType.INTERNODE_MESSAGING, SSLFactory.SocketType.SERVER);
+                    SslContext sslContext = 
SSLFactory.getSslContext(encryptionOptions, true, SSLFactory.SocketType.SERVER);
                     InetSocketAddress peer = 
encryptionOptions.require_endpoint_verification ? channel.remoteAddress() : 
null;
                     SslHandler sslHandler = newSslHandler(channel, sslContext, 
peer);
                     logger.trace("creating inbound netty SslContext: 
context={}, engine={}", sslContext.getClass().getName(), 
sslHandler.engine().getClass().getName());
@@ -369,7 +369,7 @@ public final class NettyFactory
             // order of handlers: ssl -> logger -> handshakeHandler
             if (params.encryptionOptions != null)
             {
-                SslContext sslContext = 
SSLFactory.getSslContext(params.encryptionOptions, true, 
SSLFactory.ConnectionType.INTERNODE_MESSAGING, SSLFactory.SocketType.CLIENT);
+                SslContext sslContext = 
SSLFactory.getSslContext(params.encryptionOptions, true, 
SSLFactory.SocketType.CLIENT);
                 // for some reason channel.remoteAddress() will return null
                 InetAddressAndPort address = params.connectionId.remote();
                 InetSocketAddress peer = 
params.encryptionOptions.require_endpoint_verification ? new 
InetSocketAddress(address.address, address.port) : null;
diff --git a/src/java/org/apache/cassandra/net/async/OptionalSslHandler.java 
b/src/java/org/apache/cassandra/net/async/OptionalSslHandler.java
index d57518c..3b4f794 100644
--- a/src/java/org/apache/cassandra/net/async/OptionalSslHandler.java
+++ b/src/java/org/apache/cassandra/net/async/OptionalSslHandler.java
@@ -51,7 +51,7 @@ public class OptionalSslHandler extends ByteToMessageDecoder
         if (SslHandler.isEncrypted(in))
         {
             // Connection uses SSL/TLS, replace the detection handler with a 
SslHandler and so use encryption.
-            SslContext sslContext = 
SSLFactory.getSslContext(encryptionOptions, true, 
SSLFactory.ConnectionType.INTERNODE_MESSAGING, SSLFactory.SocketType.SERVER);
+            SslContext sslContext = 
SSLFactory.getSslContext(encryptionOptions, true, SSLFactory.SocketType.SERVER);
             Channel channel = ctx.channel();
             InetSocketAddress peer = 
encryptionOptions.require_endpoint_verification ? (InetSocketAddress) 
channel.remoteAddress() : null;
             SslHandler sslHandler = NettyFactory.newSslHandler(channel, 
sslContext, peer);
diff --git a/src/java/org/apache/cassandra/security/SSLFactory.java 
b/src/java/org/apache/cassandra/security/SSLFactory.java
index d64dded..700142d 100644
--- a/src/java/org/apache/cassandra/security/SSLFactory.java
+++ b/src/java/org/apache/cassandra/security/SSLFactory.java
@@ -56,6 +56,7 @@ import io.netty.handler.ssl.SslProvider;
 import io.netty.handler.ssl.SupportedCipherSuiteFilter;
 import io.netty.util.ReferenceCountUtil;
 import org.apache.cassandra.concurrent.ScheduledExecutors;
+import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.EncryptionOptions;
 
 /**
@@ -71,15 +72,6 @@ public final class SSLFactory
     private static final Logger logger = 
LoggerFactory.getLogger(SSLFactory.class);
 
     /**
-     * Indicator if a connection is shared with a client application ({@link 
ConnectionType#NATIVE_TRANSPORT})
-     * or another cassandra node  ({@link ConnectionType#INTERNODE_MESSAGING}).
-     */
-    public enum ConnectionType
-    {
-        NATIVE_TRANSPORT, INTERNODE_MESSAGING
-    }
-
-    /**
      * Indicates if the process holds the inbound/listening end of the socket 
({@link SocketType#SERVER})), or the
      * outbound side ({@link SocketType#CLIENT}).
      */
@@ -229,27 +221,28 @@ public final class SSLFactory
     /**
      * get a netty {@link SslContext} instance
      */
-    public static SslContext getSslContext(EncryptionOptions options, boolean 
buildTruststore, ConnectionType connectionType,
+    public static SslContext getSslContext(EncryptionOptions options, boolean 
buildTruststore,
                                            SocketType socketType) throws 
IOException
     {
-        return getSslContext(options, buildTruststore, connectionType, 
socketType, OpenSsl.isAvailable());
+        return getSslContext(options, buildTruststore, socketType, 
OpenSsl.isAvailable());
     }
 
     /**
      * Get a netty {@link SslContext} instance.
      */
     @VisibleForTesting
-    static SslContext getSslContext(EncryptionOptions options, boolean 
buildTruststore, ConnectionType connectionType,
+    static SslContext getSslContext(EncryptionOptions options, boolean 
buildTruststore,
                                     SocketType socketType, boolean useOpenSsl) 
throws IOException
     {
-        CacheKey key = new CacheKey(options, connectionType, socketType);
+        CacheKey key = new CacheKey(options, socketType);
         SslContext sslContext;
 
         sslContext = cachedSslContexts.get(key);
         if (sslContext != null)
             return sslContext;
 
-        sslContext = createNettySslContext(options, buildTruststore, 
connectionType, socketType, useOpenSsl);
+        sslContext = createNettySslContext(options, buildTruststore, 
socketType, useOpenSsl);
+
         SslContext previous = cachedSslContexts.putIfAbsent(key, sslContext);
         if (previous == null)
             return sslContext;
@@ -261,7 +254,7 @@ public final class SSLFactory
     /**
      * Create a Netty {@link SslContext}
      */
-    static SslContext createNettySslContext(EncryptionOptions options, boolean 
buildTruststore, ConnectionType connectionType,
+    static SslContext createNettySslContext(EncryptionOptions options, boolean 
buildTruststore,
                                             SocketType socketType, boolean 
useOpenSsl) throws IOException
     {
         /*
@@ -303,7 +296,8 @@ public final class SSLFactory
      * @throws IllegalStateException if {@link 
#initHotReloading(EncryptionOptions.ServerEncryptionOptions, EncryptionOptions, 
boolean)}
      *                               is not called first
      */
-    public static void checkCertFilesForHotReloading()
+    public static void 
checkCertFilesForHotReloading(EncryptionOptions.ServerEncryptionOptions 
serverOpts,
+                                                     EncryptionOptions 
clientOpts)
     {
         if (!isHotReloadingInitialized)
             throw new IllegalStateException("Hot reloading functionality has 
not been initialized.");
@@ -313,61 +307,104 @@ public final class SSLFactory
         if 
(hotReloadableFiles.stream().anyMatch(HotReloadableFile::shouldReload))
         {
             logger.info("SSL certificates have been updated. Reseting the ssl 
contexts for new connections.");
-            cachedSslContexts.clear();
+            try
+            {
+                validateSslCerts(serverOpts, clientOpts);
+                cachedSslContexts.clear();
+            } catch(Exception e)
+            {
+                logger.error("Failed to hot reload the SSL Certificates! 
Please check the certificate files.", e);
+            }
         }
     }
 
     /**
      * Determines whether to hot reload certificates and schedules a periodic 
task for it.
      *
-     * @param serverEncryptionOptions
-     * @param clientEncryptionOptions
+     * @param serverOpts Server encryption options (Internode)
+     * @param clientOpts Client encryption options (Native Protocol)
      */
-    public static synchronized void 
initHotReloading(EncryptionOptions.ServerEncryptionOptions 
serverEncryptionOptions,
-                                                     EncryptionOptions 
clientEncryptionOptions,
-                                                     boolean force)
+    public static synchronized void 
initHotReloading(EncryptionOptions.ServerEncryptionOptions serverOpts,
+                                                     EncryptionOptions 
clientOpts,
+                                                     boolean force) throws 
IOException
     {
         if (isHotReloadingInitialized && !force)
             return;
 
         logger.debug("Initializing hot reloading SSLContext");
 
+        validateSslCerts(serverOpts, clientOpts);
+
         List<HotReloadableFile> fileList = new ArrayList<>();
 
-        if (serverEncryptionOptions.enabled)
+        if (serverOpts != null && serverOpts.enabled)
         {
-            fileList.add(new 
HotReloadableFile(serverEncryptionOptions.keystore));
-            fileList.add(new 
HotReloadableFile(serverEncryptionOptions.truststore));
+            fileList.add(new HotReloadableFile(serverOpts.keystore));
+            fileList.add(new HotReloadableFile(serverOpts.truststore));
         }
 
-        if (clientEncryptionOptions.enabled)
+        if (clientOpts != null && clientOpts.enabled)
         {
-            fileList.add(new 
HotReloadableFile(clientEncryptionOptions.keystore));
-            fileList.add(new 
HotReloadableFile(clientEncryptionOptions.truststore));
+            fileList.add(new HotReloadableFile(clientOpts.keystore));
+            fileList.add(new HotReloadableFile(clientOpts.truststore));
         }
 
         hotReloadableFiles = ImmutableList.copyOf(fileList);
 
         if (!isHotReloadingInitialized)
         {
-            
ScheduledExecutors.scheduledTasks.scheduleWithFixedDelay(SSLFactory::checkCertFilesForHotReloading,
-                                                                     
DEFAULT_HOT_RELOAD_INITIAL_DELAY_SEC,
-                                                                     
DEFAULT_HOT_RELOAD_PERIOD_SEC, TimeUnit.SECONDS);
+            ScheduledExecutors.scheduledTasks
+                .scheduleWithFixedDelay(() -> checkCertFilesForHotReloading(
+                                                
DatabaseDescriptor.getInternodeMessagingEncyptionOptions(),
+                                                
DatabaseDescriptor.getNativeProtocolEncryptionOptions()),
+                                        DEFAULT_HOT_RELOAD_INITIAL_DELAY_SEC,
+                                        DEFAULT_HOT_RELOAD_PERIOD_SEC, 
TimeUnit.SECONDS);
         }
 
         isHotReloadingInitialized = true;
     }
 
+
+    /**
+     * Sanity checks all certificates to ensure we can actually load them
+     */
+    public static void 
validateSslCerts(EncryptionOptions.ServerEncryptionOptions serverOpts, 
EncryptionOptions clientOpts) throws IOException
+    {
+        try
+        {
+            // Ensure we're able to create both server & client SslContexts
+            if (serverOpts != null && serverOpts.enabled)
+            {
+                createNettySslContext(serverOpts, true, SocketType.SERVER, 
OpenSsl.isAvailable());
+                createNettySslContext(serverOpts, true, SocketType.CLIENT, 
OpenSsl.isAvailable());
+            }
+        } catch (Exception e)
+        {
+            throw new IOException("Failed to create SSL context using 
server_encryption_options!", e);
+        }
+
+        try
+        {
+            // Ensure we're able to create both server & client SslContexts
+            if (clientOpts != null && clientOpts.enabled)
+            {
+                createNettySslContext(clientOpts, 
clientOpts.require_client_auth, SocketType.SERVER, OpenSsl.isAvailable());
+                createNettySslContext(clientOpts, 
clientOpts.require_client_auth, SocketType.CLIENT, OpenSsl.isAvailable());
+            }
+        } catch (Exception e)
+        {
+            throw new IOException("Failed to create SSL context using 
client_encryption_options!", e);
+        }
+    }
+
     static class CacheKey
     {
         private final EncryptionOptions encryptionOptions;
-        private final ConnectionType connectionType;
         private final SocketType socketType;
 
-        public CacheKey(EncryptionOptions encryptionOptions, ConnectionType 
connectionType, SocketType socketType)
+        public CacheKey(EncryptionOptions encryptionOptions, SocketType 
socketType)
         {
             this.encryptionOptions = encryptionOptions;
-            this.connectionType = connectionType;
             this.socketType = socketType;
         }
 
@@ -376,15 +413,13 @@ public final class SSLFactory
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             CacheKey cacheKey = (CacheKey) o;
-            return (connectionType == cacheKey.connectionType &&
-                    socketType == cacheKey.socketType &&
+            return (socketType == cacheKey.socketType &&
                     Objects.equals(encryptionOptions, 
cacheKey.encryptionOptions));
         }
 
         public int hashCode()
         {
             int result = 0;
-            result += 31 * connectionType.hashCode();
             result += 31 * socketType.hashCode();
             result += 31 * encryptionOptions.hashCode();
             return result;
diff --git a/src/java/org/apache/cassandra/tools/NodeProbe.java 
b/src/java/org/apache/cassandra/tools/NodeProbe.java
index 197dd50..a2bff26 100644
--- a/src/java/org/apache/cassandra/tools/NodeProbe.java
+++ b/src/java/org/apache/cassandra/tools/NodeProbe.java
@@ -1746,7 +1746,7 @@ public class NodeProbe implements AutoCloseable
         return arsProxy;
     }
 
-    public void reloadSslCerts()
+    public void reloadSslCerts() throws IOException
     {
         msProxy.reloadSslCertificates();
     }
diff --git a/src/java/org/apache/cassandra/tools/ReloadSslCertificates.java 
b/src/java/org/apache/cassandra/tools/ReloadSslCertificates.java
index f38b8c0..a572648 100644
--- a/src/java/org/apache/cassandra/tools/ReloadSslCertificates.java
+++ b/src/java/org/apache/cassandra/tools/ReloadSslCertificates.java
@@ -17,6 +17,8 @@
  */
 package org.apache.cassandra.tools;
 
+import java.io.IOException;
+
 import io.airlift.airline.Command;
 
 @Command(name = "reloadssl", description = "Signals Cassandra to reload SSL 
certificates")
@@ -25,6 +27,13 @@ public class ReloadSslCertificates extends 
NodeTool.NodeToolCmd
     @Override
     public void execute(NodeProbe probe)
     {
-        probe.reloadSslCerts();
+        try
+        {
+            probe.reloadSslCerts();
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("Failed to reload SSL certificates. 
Please check the SSL certificates", e);
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/src/java/org/apache/cassandra/transport/Server.java 
b/src/java/org/apache/cassandra/transport/Server.java
index 67532ac..056a4a0 100644
--- a/src/java/org/apache/cassandra/transport/Server.java
+++ b/src/java/org/apache/cassandra/transport/Server.java
@@ -406,8 +406,7 @@ public class Server implements CassandraDaemon.Server
 
         protected final SslHandler createSslHandler(ByteBufAllocator 
allocator) throws IOException
         {
-            SslContext sslContext = 
SSLFactory.getSslContext(encryptionOptions, 
encryptionOptions.require_client_auth,
-                                                             
SSLFactory.ConnectionType.NATIVE_TRANSPORT, SSLFactory.SocketType.SERVER);
+            SslContext sslContext = 
SSLFactory.getSslContext(encryptionOptions, 
encryptionOptions.require_client_auth, SSLFactory.SocketType.SERVER);
             return sslContext.newHandler(allocator);
         }
     }
diff --git a/src/java/org/apache/cassandra/transport/SimpleClient.java 
b/src/java/org/apache/cassandra/transport/SimpleClient.java
index 1334448..0db9136 100644
--- a/src/java/org/apache/cassandra/transport/SimpleClient.java
+++ b/src/java/org/apache/cassandra/transport/SimpleClient.java
@@ -293,7 +293,7 @@ public class SimpleClient implements Closeable
         {
             super.initChannel(channel);
             SslContext sslContext = 
SSLFactory.getSslContext(encryptionOptions, 
encryptionOptions.require_client_auth,
-                                                             
SSLFactory.ConnectionType.NATIVE_TRANSPORT, SSLFactory.SocketType.CLIENT);
+                                                             
SSLFactory.SocketType.CLIENT);
             channel.pipeline().addFirst("ssl", 
sslContext.newHandler(channel.alloc()));
         }
     }
diff --git a/test/unit/org/apache/cassandra/security/SSLFactoryTest.java 
b/test/unit/org/apache/cassandra/security/SSLFactoryTest.java
index 19e88de..b253c59 100644
--- a/test/unit/org/apache/cassandra/security/SSLFactoryTest.java
+++ b/test/unit/org/apache/cassandra/security/SSLFactoryTest.java
@@ -24,6 +24,7 @@ import java.security.cert.CertificateException;
 import java.util.Arrays;
 import javax.net.ssl.TrustManagerFactory;
 
+import org.apache.commons.io.FileUtils;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -95,8 +96,7 @@ public class SSLFactoryTest
         }
 
         EncryptionOptions options = addKeystoreOptions(encryptionOptions);
-        SslContext sslContext = SSLFactory.getSslContext(options, true, 
SSLFactory.ConnectionType.NATIVE_TRANSPORT,
-                                                         
SSLFactory.SocketType.CLIENT, true);
+        SslContext sslContext = SSLFactory.getSslContext(options, true, 
SSLFactory.SocketType.CLIENT, true);
         Assert.assertNotNull(sslContext);
         Assert.assertTrue(sslContext instanceof OpenSslContext);
     }
@@ -105,8 +105,7 @@ public class SSLFactoryTest
     public void getSslContext_JdkSsl() throws IOException
     {
         EncryptionOptions options = addKeystoreOptions(encryptionOptions);
-        SslContext sslContext = SSLFactory.getSslContext(options, true, 
SSLFactory.ConnectionType.NATIVE_TRANSPORT,
-                                                         
SSLFactory.SocketType.CLIENT, false);
+        SslContext sslContext = SSLFactory.getSslContext(options, true, 
SSLFactory.SocketType.CLIENT, false);
         Assert.assertNotNull(sslContext);
         Assert.assertTrue(sslContext instanceof JdkSslContext);
         Assert.assertEquals(Arrays.asList(encryptionOptions.cipher_suites), 
sslContext.cipherSuites());
@@ -175,17 +174,17 @@ public class SSLFactoryTest
 
             SSLFactory.initHotReloading((ServerEncryptionOptions) options, 
options, true);
 
-            SslContext oldCtx = SSLFactory.getSslContext(options, true, 
SSLFactory.ConnectionType.NATIVE_TRANSPORT,
-                                                         
SSLFactory.SocketType.CLIENT, OpenSsl.isAvailable());
+            SslContext oldCtx = SSLFactory.getSslContext(options, true, 
SSLFactory.SocketType.CLIENT, OpenSsl
+                                                                               
                            .isAvailable());
             File keystoreFile = new File(options.keystore);
 
-            SSLFactory.checkCertFilesForHotReloading();
+            SSLFactory.checkCertFilesForHotReloading((ServerEncryptionOptions) 
options, options);
             Thread.sleep(5000);
-            keystoreFile.setLastModified(System.currentTimeMillis());
+            FileUtils.touch(keystoreFile);
 
-            SSLFactory.checkCertFilesForHotReloading();
-            SslContext newCtx = SSLFactory.getSslContext(options, true, 
SSLFactory.ConnectionType.NATIVE_TRANSPORT,
-                                                         
SSLFactory.SocketType.CLIENT, OpenSsl.isAvailable());
+            SSLFactory.checkCertFilesForHotReloading((ServerEncryptionOptions) 
options, options);;
+            SslContext newCtx = SSLFactory.getSslContext(options, true, 
SSLFactory.SocketType.CLIENT, OpenSsl
+                                                                               
                           .isAvailable());
 
             Assert.assertNotSame(oldCtx, newCtx);
         }
@@ -199,6 +198,94 @@ public class SSLFactoryTest
         }
     }
 
+    @Test(expected = IOException.class)
+    public void testSslFactorySslInit_BadPassword_ThrowsException() throws 
IOException
+    {
+        EncryptionOptions options = addKeystoreOptions(encryptionOptions);
+        options.keystore_password = "bad password";
+        options.enabled = true;
+
+        SSLFactory.initHotReloading((ServerEncryptionOptions) options, 
options, true);
+    }
+
+    @Test
+    public void 
testSslFactoryHotReload_BadPassword_DoesNotClearExistingSslContext() throws 
IOException,
+                                                                               
             InterruptedException
+    {
+        try
+        {
+            addKeystoreOptions(encryptionOptions);
+
+            EncryptionOptions options = new 
ServerEncryptionOptions(encryptionOptions);
+            options.enabled = true;
+
+            SSLFactory.initHotReloading((ServerEncryptionOptions) options, 
options, true);
+            SslContext oldCtx = SSLFactory.getSslContext(options, true, 
SSLFactory.SocketType.CLIENT, OpenSsl
+                                                                               
                           .isAvailable());
+            File keystoreFile = new File(options.keystore);
+
+            SSLFactory.checkCertFilesForHotReloading((ServerEncryptionOptions) 
options, options);
+            Thread.sleep(5000);
+            FileUtils.touch(keystoreFile);
+
+            options.keystore_password = "bad password";
+            SSLFactory.checkCertFilesForHotReloading((ServerEncryptionOptions) 
options, options);;
+            SslContext newCtx = SSLFactory.getSslContext(options, true, 
SSLFactory.SocketType.CLIENT, OpenSsl
+                                                                               
                           .isAvailable());
+
+            Assert.assertSame(oldCtx, newCtx);
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+        finally
+        {
+            DatabaseDescriptor.loadConfig();
+        }
+    }
+
+    @Test
+    public void 
testSslFactoryHotReload_CorruptOrNonExistentFile_DoesNotClearExistingSslContext()
 throws IOException,
+                                                                               
                          InterruptedException
+    {
+        try
+        {
+            addKeystoreOptions(encryptionOptions);
+
+            File testKeystoreFile = new File(encryptionOptions.keystore + 
".test");
+            FileUtils.copyFile(new 
File(encryptionOptions.keystore),testKeystoreFile);
+            encryptionOptions.keystore = testKeystoreFile.getPath();
+
+            EncryptionOptions options = new 
ServerEncryptionOptions(encryptionOptions);
+            options.enabled = true;
+
+            SSLFactory.initHotReloading((ServerEncryptionOptions) options, 
options, true);
+            SslContext oldCtx = SSLFactory.getSslContext(options, true, 
SSLFactory.SocketType.CLIENT, OpenSsl
+                                                                               
                           .isAvailable());
+            SSLFactory.checkCertFilesForHotReloading((ServerEncryptionOptions) 
options, options);
+            Thread.sleep(5000);
+
+            FileUtils.touch(testKeystoreFile);
+            FileUtils.forceDelete(testKeystoreFile);
+
+            SSLFactory.checkCertFilesForHotReloading((ServerEncryptionOptions) 
options, options);;
+            SslContext newCtx = SSLFactory.getSslContext(options, true, 
SSLFactory.SocketType.CLIENT, OpenSsl
+                                                                               
                           .isAvailable());
+
+            Assert.assertSame(oldCtx, newCtx);
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+        finally
+        {
+            DatabaseDescriptor.loadConfig();
+            FileUtils.deleteQuietly(new File(encryptionOptions.keystore + 
".test"));
+        }
+    }
+
     @Test
     public void getSslContext_ParamChanges() throws IOException
     {
@@ -206,15 +293,15 @@ public class SSLFactoryTest
         options.enabled = true;
         options.cipher_suites = new String[]{ 
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" };
 
-        SslContext ctx1 = SSLFactory.getSslContext(options, true, 
SSLFactory.ConnectionType.NATIVE_TRANSPORT,
-                                                   
SSLFactory.SocketType.CLIENT, OpenSsl.isAvailable());
+        SslContext ctx1 = SSLFactory.getSslContext(options, true,
+                                                   
SSLFactory.SocketType.SERVER, OpenSsl.isAvailable());
 
-        Assert.assertTrue(ctx1.isClient());
+        Assert.assertTrue(ctx1.isServer());
         Assert.assertArrayEquals(ctx1.cipherSuites().toArray(), 
options.cipher_suites);
 
         options.cipher_suites = new String[]{ 
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" };
 
-        SslContext ctx2 = SSLFactory.getSslContext(options, true, 
SSLFactory.ConnectionType.NATIVE_TRANSPORT,
+        SslContext ctx2 = SSLFactory.getSslContext(options, true,
                                                    
SSLFactory.SocketType.CLIENT, OpenSsl.isAvailable());
 
         Assert.assertTrue(ctx2.isClient());


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

Reply via email to