You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by dc...@apache.org on 2021/01/07 22:42:32 UTC
[cassandra] branch trunk updated: SSLFactory should initialize
SSLContext before setting protocols
This is an automated email from the ASF dual-hosted git repository.
dcapwell 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 7637acc SSLFactory should initialize SSLContext before setting protocols
7637acc is described below
commit 7637acc3d762047f2a478855eb4d239b4f314cd8
Author: Jon Meredith <jm...@apple.com>
AuthorDate: Thu Jan 7 13:10:53 2021 -0800
SSLFactory should initialize SSLContext before setting protocols
patch by Jon Meredith; reviewed by David Capwell, Dinesh Joshi for CASSANDRA-16362
---
CHANGES.txt | 1 +
.../apache/cassandra/config/EncryptionOptions.java | 6 +-
.../org/apache/cassandra/security/SSLFactory.java | 21 +++-
.../org/apache/cassandra/tools/BulkLoader.java | 24 ++++-
.../test/SSTableLoaderEncryptionOptionsTest.java | 106 +++++++++++++++++++++
.../config/DatabaseDescriptorRefTest.java | 1 +
.../cassandra/db/virtual/SettingsTableTest.java | 3 +-
.../cassandra/stress/util/JavaDriverClient.java | 22 ++++-
8 files changed, 171 insertions(+), 13 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index b593a1b..10e7a43 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -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)
diff --git a/src/java/org/apache/cassandra/config/EncryptionOptions.java b/src/java/org/apache/cassandra/config/EncryptionOptions.java
index fad6190..a534e36 100644
--- a/src/java/org/apache/cassandra/config/EncryptionOptions.java
+++ b/src/java/org/apache/cassandra/config/EncryptionOptions.java
@@ -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
diff --git a/src/java/org/apache/cassandra/security/SSLFactory.java b/src/java/org/apache/cassandra/security/SSLFactory.java
index 7dc0256..2f7ea75 100644
--- a/src/java/org/apache/cassandra/security/SSLFactory.java
+++ b/src/java/org/apache/cassandra/security/SSLFactory.java
@@ -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;
}
diff --git a/src/java/org/apache/cassandra/tools/BulkLoader.java b/src/java/org/apache/cassandra/tools/BulkLoader.java
index 5fd3885..c1de1ff 100644
--- a/src/java/org/apache/cassandra/tools/BulkLoader.java
+++ b/src/java/org/apache/cassandra/tools/BulkLoader.java
@@ -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
diff --git a/test/distributed/org/apache/cassandra/distributed/test/SSTableLoaderEncryptionOptionsTest.java b/test/distributed/org/apache/cassandra/distributed/test/SSTableLoaderEncryptionOptionsTest.java
new file mode 100644
index 0000000..1ddc1fb
--- /dev/null
+++ b/test/distributed/org/apache/cassandra/distributed/test/SSTableLoaderEncryptionOptionsTest.java
@@ -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"));
+ }
+}
diff --git a/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java b/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java
index 7dc84f6..521071c 100644
--- a/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java
+++ b/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java
@@ -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",
diff --git a/test/unit/org/apache/cassandra/db/virtual/SettingsTableTest.java b/test/unit/org/apache/cassandra/db/virtual/SettingsTableTest.java
index c81da51..a2fda49 100644
--- a/test/unit/org/apache/cassandra/db/virtual/SettingsTableTest.java
+++ b/test/unit/org/apache/cassandra/db/virtual/SettingsTableTest.java
@@ -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"));
diff --git a/tools/stress/src/org/apache/cassandra/stress/util/JavaDriverClient.java b/tools/stress/src/org/apache/cassandra/stress/util/JavaDriverClient.java
index b934769..49cb0a8 100644
--- a/tools/stress/src/org/apache/cassandra/stress/util/JavaDriverClient.java
+++ b/tools/stress/src/org/apache/cassandra/stress/util/JavaDriverClient.java
@@ -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);
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org