mirror of https://github.com/apache/cassandra
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
This commit is contained in:
parent
2747dbf091
commit
b9586501a6
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
|
||||
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);
|
||||
}
|
||||
checkCachedContextsForReload(false);
|
||||
}
|
||||
|
||||
private static void checkCertFilesForHotReloading(EncryptionOptions options, String contextDescription,
|
||||
boolean verifyPeerCertificate)
|
||||
/**
|
||||
* Forces revalidation and loading of SSL certifcates if valid
|
||||
*/
|
||||
public static void forceCheckCertFiles()
|
||||
{
|
||||
try
|
||||
checkCachedContextsForReload(true);
|
||||
}
|
||||
|
||||
private static void checkCachedContextsForReload(boolean forceReload)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
.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();
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue