mirror of https://github.com/apache/cassandra
SSLFactory should initialize SSLContext before setting protocols
patch by Jon Meredith; reviewed by David Capwell, Dinesh Joshi for CASSANDRA-16362
This commit is contained in:
parent
2ef0ac6ae9
commit
7637acc3d7
|
|
@ -1,6 +1,7 @@
|
|||
4.0-beta5
|
||||
* Restore sasi dependencies jflex, snowball-stemmer, and concurrent-trees, in the cassandra-all pom (CASSANDRA-16303)
|
||||
* Fix DecimalDeserializer#toString OOM (CASSANDRA-14925)
|
||||
* SSLFactory should initialize SSLContext before setting protocols (CASSANDRA-16362)
|
||||
|
||||
4.0-beta4
|
||||
* DROP COMPACT STORAGE should invalidate prepared statements still using CompactTableMetadata (CASSANDRA-16361)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import org.apache.cassandra.locator.IEndpointSnitch;
|
||||
import org.apache.cassandra.locator.InetAddressAndPort;
|
||||
import org.apache.cassandra.security.SSLFactory;
|
||||
|
||||
public class EncryptionOptions
|
||||
{
|
||||
|
|
@ -236,10 +237,9 @@ public class EncryptionOptions
|
|||
}
|
||||
|
||||
/* This list is substituted in configurations that have explicitly specified the original "TLS" default,
|
||||
* it is not a 'default' list or 'support protocol versions' list. It is just an attempt to preserve the
|
||||
* original intent for the user configuration
|
||||
* by extracting it from the default "TLS" SSL Context instance
|
||||
*/
|
||||
private final List<String> TLS_PROTOCOL_SUBSTITUTION = ImmutableList.of("TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1");
|
||||
static private final List<String> TLS_PROTOCOL_SUBSTITUTION = SSLFactory.tlsInstanceProtocolSubstitution();
|
||||
|
||||
/**
|
||||
* Combine the pre-4.0 protocol field with the accepted_protocols list, substituting a list of
|
||||
|
|
|
|||
|
|
@ -161,6 +161,26 @@ public final class SSLFactory
|
|||
}
|
||||
}
|
||||
|
||||
/** Provides the list of protocols that would have been supported if "TLS" was selected as the
|
||||
* protocol before the change for CASSANDRA-13325 that expects explicit protocol versions.
|
||||
* @return list of enabled protocol names
|
||||
*/
|
||||
public static List<String> tlsInstanceProtocolSubstitution()
|
||||
{
|
||||
try
|
||||
{
|
||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||
ctx.init(null, null, null);
|
||||
SSLParameters params = ctx.getSupportedSSLParameters();
|
||||
String[] protocols = params.getProtocols();
|
||||
return Arrays.asList(protocols);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException("Error finding supported TLS Protocols", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSSE {@link SSLContext}.
|
||||
*/
|
||||
|
|
@ -175,7 +195,6 @@ public final class SSLFactory
|
|||
try
|
||||
{
|
||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||
ctx.getDefaultSSLParameters().setProtocols(options.acceptedProtocolsArray());
|
||||
ctx.init(kmf.getKeyManagers(), trustManagers, null);
|
||||
return ctx;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,15 +21,17 @@ import java.io.IOException;
|
|||
import java.net.InetSocketAddress;
|
||||
import java.util.Set;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import com.datastax.driver.core.AuthProvider;
|
||||
import com.datastax.driver.core.JdkSSLOptions;
|
||||
import com.datastax.driver.core.RemoteEndpointAwareJdkSSLOptions;
|
||||
import com.datastax.driver.core.SSLOptions;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
|
||||
import com.datastax.shaded.netty.channel.socket.SocketChannel;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.config.EncryptionOptions;
|
||||
import org.apache.cassandra.io.sstable.SSTableLoader;
|
||||
|
|
@ -261,10 +263,22 @@ public class BulkLoader
|
|||
throw new RuntimeException("Could not create SSL Context.", e);
|
||||
}
|
||||
|
||||
return JdkSSLOptions.builder()
|
||||
.withSSLContext(sslContext)
|
||||
.withCipherSuites(clientEncryptionOptions.cipherSuitesArray())
|
||||
.build();
|
||||
// Temporarily override newSSLEngine to set accepted protocols until it is added to
|
||||
// RemoteEndpointAwareJdkSSLOptions. See CASSANDRA-13325 and CASSANDRA-16362.
|
||||
RemoteEndpointAwareJdkSSLOptions sslOptions = new RemoteEndpointAwareJdkSSLOptions(sslContext, null)
|
||||
{
|
||||
protected SSLEngine newSSLEngine(SocketChannel channel, InetSocketAddress remoteEndpoint)
|
||||
{
|
||||
SSLEngine engine = super.newSSLEngine(channel, remoteEndpoint);
|
||||
|
||||
String[] acceptedProtocols = clientEncryptionOptions.acceptedProtocolsArray();
|
||||
if (acceptedProtocols != null && acceptedProtocols.length > 0)
|
||||
engine.setEnabledProtocols(acceptedProtocols);
|
||||
|
||||
return engine;
|
||||
}
|
||||
};
|
||||
return sslOptions;
|
||||
}
|
||||
|
||||
static class ExternalClient extends NativeSSTableLoaderClient
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.distributed.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.distributed.Cluster;
|
||||
import org.apache.cassandra.distributed.api.Feature;
|
||||
import org.apache.cassandra.tools.BulkLoader;
|
||||
import org.apache.cassandra.tools.ToolRunner;
|
||||
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
public class SSTableLoaderEncryptionOptionsTest extends AbstractEncryptionOptionsImpl
|
||||
{
|
||||
static Cluster CLUSTER;
|
||||
static String NODES;
|
||||
static int NATIVE_PORT;
|
||||
static int STORAGE_PORT;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupCluster() throws IOException
|
||||
{
|
||||
CLUSTER = Cluster.build().withNodes(1).withConfig(c -> {
|
||||
c.with(Feature.NATIVE_PROTOCOL, Feature.NETWORK, Feature.GOSSIP); // need gossip to get hostid for java driver
|
||||
c.set("server_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
.put("internode_encryption", "all")
|
||||
.put("optional", false)
|
||||
.build());
|
||||
c.set("client_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
.put("enabled", true)
|
||||
.put("optional", false)
|
||||
.put("accepted_protocols", Collections.singletonList("TLSv1.2"))
|
||||
.build());
|
||||
}).start();
|
||||
NODES = CLUSTER.get(1).config().broadcastAddress().getHostString();
|
||||
NATIVE_PORT = CLUSTER.get(1).callOnInstance(DatabaseDescriptor::getNativeTransportPort);
|
||||
STORAGE_PORT = CLUSTER.get(1).callOnInstance(DatabaseDescriptor::getStoragePort);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownCluster()
|
||||
{
|
||||
if (CLUSTER != null)
|
||||
CLUSTER.close();
|
||||
}
|
||||
@Test
|
||||
public void bulkLoaderSuccessfullyConnectsOverSsl() throws Throwable
|
||||
{
|
||||
ToolRunner.ToolResult tool = ToolRunner.invokeClass(BulkLoader.class,
|
||||
"--nodes", NODES,
|
||||
"--port", Integer.toString(NATIVE_PORT),
|
||||
"--storage-port", Integer.toString(STORAGE_PORT),
|
||||
"--keystore", validKeyStorePath,
|
||||
"--keystore-password", validKeyStorePassword,
|
||||
"--truststore", validTrustStorePath,
|
||||
"--truststore-password", validTrustStorePassword,
|
||||
"test/data/legacy-sstables/na/legacy_tables/legacy_na_clust");
|
||||
tool.assertOnCleanExit();
|
||||
assertTrue(tool.getStdout().contains("Summary statistics"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bulkLoaderCannotAgreeOnClientTLSProtocol()
|
||||
{
|
||||
ToolRunner.ToolResult tool = ToolRunner.invokeClass(BulkLoader.class,
|
||||
"--ssl-protocol", "TLSv1",
|
||||
"--nodes", NODES,
|
||||
"--port", Integer.toString(NATIVE_PORT),
|
||||
"--storage-port", Integer.toString(STORAGE_PORT),
|
||||
"--keystore", validKeyStorePath,
|
||||
"--keystore-password", validKeyStorePassword,
|
||||
"--truststore", validTrustStorePath,
|
||||
"--truststore-password", validTrustStorePassword,
|
||||
"test/data/legacy-sstables/na/legacy_tables/legacy_na_clust");
|
||||
assertNotEquals(0, tool.getExitCode());
|
||||
assertTrue(tool.getStdout().contains("SSLHandshakeException"));
|
||||
}
|
||||
}
|
||||
|
|
@ -141,6 +141,7 @@ public class DatabaseDescriptorRefTest
|
|||
"org.apache.cassandra.locator.Replica",
|
||||
"org.apache.cassandra.locator.SimpleSeedProvider",
|
||||
"org.apache.cassandra.locator.SeedProvider",
|
||||
"org.apache.cassandra.security.SSLFactory",
|
||||
"org.apache.cassandra.security.EncryptionContext",
|
||||
"org.apache.cassandra.service.CacheService$CacheType",
|
||||
"org.apache.cassandra.utils.binlog.BinLogOptions",
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import org.apache.cassandra.config.Config;
|
|||
import org.apache.cassandra.config.EncryptionOptions.ServerEncryptionOptions.InternodeEncryption;
|
||||
import org.apache.cassandra.config.ParameterizedClass;
|
||||
import org.apache.cassandra.cql3.CQLTester;
|
||||
import org.apache.cassandra.security.SSLFactory;
|
||||
|
||||
public class SettingsTableTest extends CQLTester
|
||||
{
|
||||
|
|
@ -154,7 +155,7 @@ public class SettingsTableTest extends CQLTester
|
|||
check(pre + "protocol", "[TLSv5]");
|
||||
|
||||
config.server_encryption_options = config.server_encryption_options.withProtocol("TLS");
|
||||
check(pre + "protocol", "[TLSv1.3, TLSv1.2, TLSv1.1, TLSv1]");
|
||||
check(pre + "protocol", SSLFactory.tlsInstanceProtocolSubstitution().toString());
|
||||
|
||||
config.server_encryption_options = config.server_encryption_options.withProtocol("TLS");
|
||||
config.server_encryption_options = config.server_encryption_options.withAcceptedProtocols(ImmutableList.of("TLSv1.2","TLSv1.1"));
|
||||
|
|
|
|||
|
|
@ -17,16 +17,19 @@
|
|||
*/
|
||||
package org.apache.cassandra.stress.util;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import com.datastax.driver.core.*;
|
||||
import com.datastax.driver.core.policies.DCAwareRoundRobinPolicy;
|
||||
import com.datastax.driver.core.policies.LoadBalancingPolicy;
|
||||
import com.datastax.driver.core.policies.TokenAwarePolicy;
|
||||
import com.datastax.driver.core.policies.WhiteListPolicy;
|
||||
import com.datastax.shaded.netty.channel.socket.SocketChannel;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
import io.netty.util.internal.logging.Slf4JLoggerFactory;
|
||||
import org.apache.cassandra.config.EncryptionOptions;
|
||||
|
|
@ -142,9 +145,22 @@ public class JavaDriverClient
|
|||
{
|
||||
SSLContext sslContext;
|
||||
sslContext = SSLFactory.createSSLContext(encryptionOptions, true);
|
||||
SSLOptions sslOptions = JdkSSLOptions.builder()
|
||||
.withSSLContext(sslContext)
|
||||
.withCipherSuites(encryptionOptions.cipherSuitesArray()).build();
|
||||
|
||||
// Temporarily override newSSLEngine to set accepted protocols until it is added to
|
||||
// RemoteEndpointAwareJdkSSLOptions. See CASSANDRA-13325 and CASSANDRA-16362.
|
||||
RemoteEndpointAwareJdkSSLOptions sslOptions = new RemoteEndpointAwareJdkSSLOptions(sslContext, null)
|
||||
{
|
||||
protected SSLEngine newSSLEngine(SocketChannel channel, InetSocketAddress remoteEndpoint)
|
||||
{
|
||||
SSLEngine engine = super.newSSLEngine(channel, remoteEndpoint);
|
||||
|
||||
String[] acceptedProtocols = encryptionOptions.acceptedProtocolsArray();
|
||||
if (acceptedProtocols != null && acceptedProtocols.length > 0)
|
||||
engine.setEnabledProtocols(acceptedProtocols);
|
||||
|
||||
return engine;
|
||||
}
|
||||
};
|
||||
clusterBuilder.withSSL(sslOptions);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue