This is an automated email from the ASF dual-hosted git repository.
jonmeredith 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 bfcb21fbeb Set right client auth for creating SSL context in mTLS
optional mode
bfcb21fbeb is described below
commit bfcb21fbebfef14fbfe626bfd39d66f5e5c51018
Author: Jyothsna Konisa <[email protected]>
AuthorDate: Wed Aug 30 11:58:21 2023 -0700
Set right client auth for creating SSL context in mTLS optional mode
patch by Jyothsna Konisa; reviewed by Francisco Guerrero, Jon Meredith for
CASSANDRA-18811
---
conf/cassandra.yaml | 3 +
.../pages/getting-started/mtlsauthenticators.adoc | 53 ++++++++
.../KubernetesSecretsPEMSslContextFactoryTest.java | 2 +-
.../KubernetesSecretsSslContextFactoryTest.java | 2 +-
.../cassandra/auth/MutualTlsAuthenticator.java | 23 ++--
.../auth/MutualTlsInternodeAuthenticator.java | 23 ++--
...MutualTlsWithPasswordFallbackAuthenticator.java | 16 +++
.../cassandra/config/DatabaseDescriptor.java | 5 +-
.../apache/cassandra/config/EncryptionOptions.java | 64 ++++++++--
.../cassandra/net/InboundConnectionInitiator.java | 3 +-
.../cassandra/net/OutboundConnectionInitiator.java | 16 ++-
.../security/AbstractSslContextFactory.java | 45 ++++++-
.../cassandra/security/ISslContextFactory.java | 53 ++++++++
.../org/apache/cassandra/security/SSLFactory.java | 30 +++--
.../org/apache/cassandra/tools/BulkLoader.java | 3 +-
.../org/apache/cassandra/tools/LoaderOptions.java | 3 +-
.../cassandra/transport/PipelineConfigurator.java | 4 +-
.../apache/cassandra/transport/SimpleClient.java | 2 +-
.../test/AbstractEncryptionOptionsImpl.java | 5 +-
.../test/NativeTransportEncryptionOptionsTest.java | 141 ++++++++++++++++++++-
.../cassandra/auth/MutualTlsAuthenticatorTest.java | 20 ++-
...alTlsWithPasswordFallbackAuthenticatorTest.java | 10 +-
.../cassandra/config/ConfigCompatibilityTest.java | 3 +-
.../config/EncryptionOptionsEqualityTest.java | 16 ++-
.../cassandra/config/EncryptionOptionsTest.java | 6 +-
.../cassandra/db/virtual/SettingsTableTest.java | 4 +-
.../org/apache/cassandra/net/ConnectionTest.java | 3 +-
.../org/apache/cassandra/net/HandshakeTest.java | 6 +-
.../security/DefaultSslContextFactoryTest.java | 9 +-
.../security/DummySslContextFactoryImpl.java | 15 ++-
.../security/FileBasedSslContextFactoryTest.java | 4 +-
.../security/PEMBasedSslContextFactoryTest.java | 12 +-
.../apache/cassandra/security/SSLFactoryTest.java | 46 +++----
.../cassandra/stress/util/JavaDriverClient.java | 4 +-
34 files changed, 528 insertions(+), 126 deletions(-)
diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml
index 5c5019962e..978b991449 100644
--- a/conf/cassandra.yaml
+++ b/conf/cassandra.yaml
@@ -1567,6 +1567,9 @@ client_encryption_options:
# # Must be an instance of
org.apache.cassandra.security.ISslContextFactory
# class_name: org.apache.cassandra.security.DefaultSslContextFactory
# Verify client certificates
+ # - true/REQUIRED, Verifies the client and forces the client to send client
certificate
+ # - false/NOT_REQUIRED, Doesn't verify the client
+ # - optional, Optionally verifies the client if client certificate is sent,
but doesn't force client certificate to send certificates
require_client_auth: false
# require_endpoint_verification: false
# Set trustore and truststore_password if require_client_auth is true
diff --git
a/doc/modules/cassandra/pages/getting-started/mtlsauthenticators.adoc
b/doc/modules/cassandra/pages/getting-started/mtlsauthenticators.adoc
index e3cd6ef79f..719f069739 100644
--- a/doc/modules/cassandra/pages/getting-started/mtlsauthenticators.adoc
+++ b/doc/modules/cassandra/pages/getting-started/mtlsauthenticators.adoc
@@ -66,6 +66,59 @@ authenticator:
After the bounce, C* will accept mTLS connections from the clients and if their
identity is present in the `identity_to_roles` table, access will be granted.
+== Configuring mTLS with password fallback authenticator for client connections
+
+Operators that wish to migrate cannot immediately change the configuration to
require
+mTLS authentication as it will break existing non-mTLS based clients of the
cluster.
+In order to make a smooth transition from non-mTLS based authentication to
mTLS authentication,
+the operator can run Cassandra in optional mTLS mode and configure
authenticator to be
+`MutualTlsWithPasswordFallbackAuthenticator` which can accept both certificate
based
+and password based connections.
+
+Below are the steps to configure C* in optional mTLS mode with fallback
authenticator.
+Note that the following steps uses SPIFFE identity as an example, If you are
using
+a custom validator, use appropriate identity in place of
`spiffe://testdomain.com/testIdentifier/testValue`.
+
+*STEP 1: Add authorized users to system_auth.identity_to_roles table*
+
+Note that only users with permissions to create/modify roles can add/remove
identities.
+Client certificates with the identities in this table will be trusted by C*.
+[source, plaintext]
+----
+ADD IDENTITY 'spiffe://testdomain.com/testIdentifier/testValue' TO ROLE
'read_only_user'
+----
+
+*STEP 2: Configure Cassandra.yaml with right properties*
+
+`client_encryption_options` configuration for mTLS connections, Note that
require_client_auth configuration
+is optional.
+[source, plaintext]
+----
+client_encryption_options:
+ enabled: true
+ optional: true
+ keystore: conf/.keystore
+ keystore_password: cassandra
+ truststore: conf/.truststore
+ truststore_password: cassandra
+ require_client_auth: optional // to enable mTLS in optional mode
+----
+Configure fallback authenticator and the validator for client connections . If
you are
+implementing a custom validator, use that instead of Spiffe validator
+[source, plaintext]
+----
+authenticator:
+ class_name :
org.apache.cassandra.auth.MutualTlsWithPasswordFallbackAuthenticator
+ parameters :
+ validator_class_name: org.apache.cassandra.auth.SpiffeCertificateValidator
+----
+
+*STEP 3: Bounce the cluster*
+
+After the bounce, C* will accept both mTLS connections and password based
connections from
+the clients. This configuration should be used during transition phase and the
require_client_auth
+configuration should be set to true when all the clients start making mTLS
connections to the cluster.
+
== Configuring mTLS authenticator for Internode connections
Internode authenticator trusts certificates whose identities are present in
diff --git
a/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsPEMSslContextFactoryTest.java
b/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsPEMSslContextFactoryTest.java
index f101e8955d..5ecec4ed26 100644
---
a/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsPEMSslContextFactoryTest.java
+++
b/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsPEMSslContextFactoryTest.java
@@ -114,7 +114,7 @@ public class KubernetesSecretsPEMSslContextFactoryTest
* In order to test with real 'env' variables comment out this line
and set appropriate env variable. This is
* done to avoid having a dependency on env in the unit test.
*/
- commonConfig.put("require_client_auth", Boolean.FALSE);
+ commonConfig.put("require_client_auth", "false";
commonConfig.put("cipher_suites",
Arrays.asList("TLS_RSA_WITH_AES_128_CBC_SHA"));
}
diff --git
a/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java
b/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java
index 0c364196b8..77e9788d73 100644
---
a/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java
+++
b/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java
@@ -80,7 +80,7 @@ public class KubernetesSecretsSslContextFactoryTest
* done to avoid having a dependency on env in the unit test.
*/
commonConfig.put("MY_TRUSTSTORE_PASSWORD", "cassandra");
- commonConfig.put("require_client_auth", Boolean.FALSE);
+ commonConfig.put("require_client_auth", "false");
commonConfig.put("cipher_suites",
Arrays.asList("TLS_RSA_WITH_AES_128_CBC_SHA"));
}
diff --git a/src/java/org/apache/cassandra/auth/MutualTlsAuthenticator.java
b/src/java/org/apache/cassandra/auth/MutualTlsAuthenticator.java
index c327a4bd21..01ff6919fa 100644
--- a/src/java/org/apache/cassandra/auth/MutualTlsAuthenticator.java
+++ b/src/java/org/apache/cassandra/auth/MutualTlsAuthenticator.java
@@ -40,6 +40,8 @@ import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.utils.NoSpamLogger;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
+
/*
* Performs mTLS authentication for client connections by extracting
identities from client certificate
* and verifying them against the authorized identities in IdentityCache.
IdentityCache is a loading cache that
@@ -79,7 +81,6 @@ public class MutualTlsAuthenticator implements IAuthenticator
}
certificateValidator = ParameterizedClass.newInstance(new
ParameterizedClass(certificateValidatorClassName),
Arrays.asList("", AuthConfig.class.getPackage().getName()));
- checkMtlsConfigurationIsValid(DatabaseDescriptor.getRawConfig());
AuthCacheService.instance.register(identityCache);
}
@@ -98,7 +99,14 @@ public class MutualTlsAuthenticator implements IAuthenticator
@Override
public void validateConfiguration() throws ConfigurationException
{
-
+ Config config = DatabaseDescriptor.getRawConfig();
+ if (!config.client_encryption_options.getEnabled() ||
config.client_encryption_options.getClientAuth() != REQUIRED)
+ {
+ String msg = "MutualTlsAuthenticator requires
client_encryption_options.enabled to be true" +
+ " & client_encryption_options.require_client_auth to
be true";
+ logger.error(msg);
+ throw new ConfigurationException(msg);
+ }
}
@Override
@@ -175,17 +183,6 @@ public class MutualTlsAuthenticator implements
IAuthenticator
}
}
- private void checkMtlsConfigurationIsValid(Config config)
- {
- if (!config.client_encryption_options.getEnabled() ||
!config.client_encryption_options.require_client_auth)
- {
- String msg = "MutualTlsAuthenticator requires
client_encryption_options.enabled to be true" +
- " & client_encryption_options.require_client_auth to
be true";
- logger.error(msg);
- throw new ConfigurationException(msg);
- }
- }
-
static class IdentityCache extends AuthCache<String, String>
{
IdentityCache()
diff --git
a/src/java/org/apache/cassandra/auth/MutualTlsInternodeAuthenticator.java
b/src/java/org/apache/cassandra/auth/MutualTlsInternodeAuthenticator.java
index 3c466df5fa..cf5d764c9d 100644
--- a/src/java/org/apache/cassandra/auth/MutualTlsInternodeAuthenticator.java
+++ b/src/java/org/apache/cassandra/auth/MutualTlsInternodeAuthenticator.java
@@ -46,6 +46,8 @@ import
org.apache.cassandra.exceptions.AuthenticationException;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.utils.NoSpamLogger;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
+
/*
* Performs mTLS authentication for internode connections by extracting
identities from the certificates of incoming
* connection and verifying them against a list of authorized peers.
Authorized peers can be configured in
@@ -89,7 +91,6 @@ public class MutualTlsInternodeAuthenticator implements
IInternodeAuthenticator
certificateValidator = ParameterizedClass.newInstance(new
ParameterizedClass(certificateValidatorClassName),
Arrays.asList("", AuthConfig.class.getPackage().getName()));
Config config = DatabaseDescriptor.getRawConfig();
- checkInternodeMtlsConfigurationIsValid(config);
if (parameters.containsKey(TRUSTED_PEER_IDENTITIES))
{
@@ -146,6 +147,15 @@ public class MutualTlsInternodeAuthenticator implements
IInternodeAuthenticator
@Override
public void validateConfiguration() throws ConfigurationException
{
+ Config config = DatabaseDescriptor.getRawConfig();
+ if (config.server_encryption_options.internode_encryption ==
EncryptionOptions.ServerEncryptionOptions.InternodeEncryption.none
+ || config.server_encryption_options.getClientAuth() != REQUIRED)
+ {
+ String msg = "MutualTlsInternodeAuthenticator requires
server_encryption_options.internode_encryption to be enabled" +
+ " & server_encryption_options.require_client_auth to
be true";
+ logger.error(msg);
+ throw new ConfigurationException(msg);
+ }
}
@@ -212,15 +222,4 @@ public class MutualTlsInternodeAuthenticator implements
IInternodeAuthenticator
return allUsers;
}
- private void checkInternodeMtlsConfigurationIsValid(Config config)
- {
- if (config.server_encryption_options.internode_encryption ==
EncryptionOptions.ServerEncryptionOptions.InternodeEncryption.none
- || !config.server_encryption_options.require_client_auth)
- {
- String msg = "MutualTlsInternodeAuthenticator requires
server_encryption_options.internode_encryption to be enabled" +
- " & server_encryption_options.require_client_auth to
be true";
- logger.error(msg);
- throw new ConfigurationException(msg);
- }
- }
}
diff --git
a/src/java/org/apache/cassandra/auth/MutualTlsWithPasswordFallbackAuthenticator.java
b/src/java/org/apache/cassandra/auth/MutualTlsWithPasswordFallbackAuthenticator.java
index eba0ea54c0..4c86f6a3fe 100644
---
a/src/java/org/apache/cassandra/auth/MutualTlsWithPasswordFallbackAuthenticator.java
+++
b/src/java/org/apache/cassandra/auth/MutualTlsWithPasswordFallbackAuthenticator.java
@@ -22,6 +22,11 @@ import java.net.InetAddress;
import java.security.cert.Certificate;
import java.util.Map;
+import org.apache.cassandra.config.Config;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.config.EncryptionOptions;
+import org.apache.cassandra.exceptions.ConfigurationException;
+
/**
* This authenticator can be used in optional mTLS mode, If the client doesn't
make an mTLS connection
* this fallbacks to password authentication.
@@ -50,4 +55,15 @@ public class MutualTlsWithPasswordFallbackAuthenticator
extends PasswordAuthenti
}
return mutualTlsAuthenticator.newSaslNegotiator(clientAddress,
certificates);
}
+
+ @Override
+ public void validateConfiguration() throws ConfigurationException
+ {
+ Config config = DatabaseDescriptor.getRawConfig();
+ if (config.client_encryption_options.getClientAuth() ==
EncryptionOptions.ClientAuth.NOT_REQUIRED)
+ {
+ String msg = "MutualTlsWithPasswordFallbackAuthenticator requires
client_encryption_options.require_client_auth to be optional/true";
+ throw new ConfigurationException(msg);
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index b304100957..4b0f036914 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -140,6 +140,7 @@ import static
org.apache.cassandra.config.CassandraRelevantProperties.UNSAFE_SYS
import static
org.apache.cassandra.config.DataRateSpec.DataRateUnit.BYTES_PER_SECOND;
import static
org.apache.cassandra.config.DataRateSpec.DataRateUnit.MEBIBYTES_PER_SECOND;
import static
org.apache.cassandra.config.DataStorageSpec.DataStorageUnit.MEBIBYTES;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
import static org.apache.cassandra.db.ConsistencyLevel.ALL;
import static org.apache.cassandra.db.ConsistencyLevel.EACH_QUORUM;
import static org.apache.cassandra.db.ConsistencyLevel.LOCAL_QUORUM;
@@ -1241,8 +1242,8 @@ public class DatabaseDescriptor
try
{
- SSLFactory.validateSslContext("Internode messaging",
conf.server_encryption_options, true, true);
- SSLFactory.validateSslContext("Native transport",
conf.client_encryption_options,
conf.client_encryption_options.require_client_auth, true);
+ SSLFactory.validateSslContext("Internode messaging",
conf.server_encryption_options, REQUIRED, true);
+ SSLFactory.validateSslContext("Native transport",
conf.client_encryption_options, conf.client_encryption_options.getClientAuth(),
true);
SSLFactory.initHotReloading(conf.server_encryption_options,
conf.client_encryption_options, false);
}
catch (IOException e)
diff --git a/src/java/org/apache/cassandra/config/EncryptionOptions.java
b/src/java/org/apache/cassandra/config/EncryptionOptions.java
index 9db2406a44..91d9ae5ee2 100644
--- a/src/java/org/apache/cassandra/config/EncryptionOptions.java
+++ b/src/java/org/apache/cassandra/config/EncryptionOptions.java
@@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.locator.IEndpointSnitch;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.security.DisableSslContextFactory;
@@ -67,6 +68,42 @@ public class EncryptionOptions
}
}
+ public enum ClientAuth
+ {
+ REQUIRED("true"),
+ NOT_REQUIRED("false"),
+ OPTIONAL("optional");
+ private final String value;
+ private static final Map<String, ClientAuth> VALUES = new HashMap<>();
+ static
+ {
+ for (ClientAuth clientAuth : ClientAuth.values())
+ {
+ VALUES.put(clientAuth.value, clientAuth);
+ VALUES.put(clientAuth.name().toLowerCase(), clientAuth);
+ }
+ }
+
+ ClientAuth(String value)
+ {
+ this.value = value;
+ }
+
+ public static ClientAuth from(String value)
+ {
+ if (VALUES.containsKey(value.toLowerCase()))
+ {
+ return VALUES.get(value.toLowerCase());
+ }
+ throw new ConfigurationException(value + " is not a valid
ClientAuth option");
+ }
+
+ public String value()
+ {
+ return value;
+ }
+ }
+
/*
* If the ssl_context_factory is configured, most likely it won't use file
based keystores and truststores and
* can choose to completely customize SSL context's creation. Most likely
it won't also use keystore_password and
@@ -84,7 +121,7 @@ public class EncryptionOptions
protected List<String> accepted_protocols;
public final String algorithm;
public final String store_type;
- public final boolean require_client_auth;
+ public final String require_client_auth;
public final boolean require_endpoint_verification;
// ServerEncryptionOptions does not use the enabled flag at all instead
using the existing
// internode_encryption option. So we force this private and expose
through isEnabled
@@ -159,7 +196,7 @@ public class EncryptionOptions
accepted_protocols = null;
algorithm = null;
store_type = "JKS";
- require_client_auth = false;
+ require_client_auth = "false";
require_endpoint_verification = false;
enabled = null;
optional = null;
@@ -168,7 +205,7 @@ public class EncryptionOptions
public EncryptionOptions(ParameterizedClass ssl_context_factory, String
keystore, String keystore_password,
String truststore, String truststore_password,
List<String> cipher_suites,
String protocol, List<String> accepted_protocols,
String algorithm, String store_type,
- boolean require_client_auth, boolean
require_endpoint_verification, Boolean enabled,
+ String require_client_auth, boolean
require_endpoint_verification, Boolean enabled,
Boolean optional)
{
this.ssl_context_factory = ssl_context_factory;
@@ -402,6 +439,11 @@ public class EncryptionOptions
return sslContextFactoryInstance == null ? null :
sslContextFactoryInstance.getAcceptedProtocols();
}
+ public ClientAuth getClientAuth()
+ {
+ return this.require_client_auth == null ? ClientAuth.NOT_REQUIRED :
ClientAuth.from(this.require_client_auth);
+ }
+
public String[] acceptedProtocolsArray()
{
List<String> ap = getAcceptedProtocols();
@@ -520,11 +562,11 @@ public class EncryptionOptions
optional).applyConfig();
}
- public EncryptionOptions withRequireClientAuth(boolean require_client_auth)
+ public EncryptionOptions withRequireClientAuth(ClientAuth
require_client_auth)
{
return new EncryptionOptions(ssl_context_factory, keystore,
keystore_password, truststore,
truststore_password, cipher_suites,
protocol, accepted_protocols, algorithm,
- store_type, require_client_auth,
require_endpoint_verification, enabled,
+ store_type, require_client_auth.value,
require_endpoint_verification, enabled,
optional).applyConfig();
}
@@ -567,7 +609,7 @@ public class EncryptionOptions
EncryptionOptions opt = (EncryptionOptions)o;
return enabled == opt.enabled &&
optional == opt.optional &&
- require_client_auth == opt.require_client_auth &&
+ require_client_auth.equals(opt.require_client_auth) &&
require_endpoint_verification ==
opt.require_endpoint_verification &&
Objects.equals(keystore, opt.keystore) &&
Objects.equals(keystore_password, opt.keystore_password) &&
@@ -600,7 +642,7 @@ public class EncryptionOptions
result += 31 * (enabled == null ? 0 : Boolean.hashCode(enabled));
result += 31 * (optional == null ? 0 : Boolean.hashCode(optional));
result += 31 * (cipher_suites == null ? 0 : cipher_suites.hashCode());
- result += 31 * Boolean.hashCode(require_client_auth);
+ result += 31 * require_client_auth.hashCode();
result += 31 * Boolean.hashCode(require_endpoint_verification);
result += 31 * (ssl_context_factory == null ? 0 :
ssl_context_factory.hashCode());
return result;
@@ -632,7 +674,7 @@ public class EncryptionOptions
String keystore_password,String
outbound_keystore,
String outbound_keystore_password,
String truststore, String truststore_password,
List<String> cipher_suites, String
protocol, List<String> accepted_protocols,
- String algorithm, String store_type,
boolean require_client_auth,
+ String algorithm, String store_type,
String require_client_auth,
boolean require_endpoint_verification,
Boolean optional,
InternodeEncryption
internode_encryption, boolean legacy_ssl_storage_port_enabled)
{
@@ -679,7 +721,7 @@ public class EncryptionOptions
logger.warn("Setting server_encryption_options.enabled has no
effect, use internode_encryption");
}
- if (require_client_auth && (internode_encryption ==
InternodeEncryption.rack || internode_encryption == InternodeEncryption.dc))
+ if (getClientAuth() != ClientAuth.NOT_REQUIRED &&
(internode_encryption == InternodeEncryption.rack || internode_encryption ==
InternodeEncryption.dc))
{
logger.warn("Setting require_client_auth is incompatible with
'rack' and 'dc' internode_encryption values."
+ " It is possible for an internode connection to
pretend to be in the same rack/dc by spoofing"
@@ -875,12 +917,12 @@ public class EncryptionOptions
legacy_ssl_storage_port_enabled).applyConfigInternal();
}
- public ServerEncryptionOptions withRequireClientAuth(boolean
require_client_auth)
+ public ServerEncryptionOptions withRequireClientAuth(ClientAuth
require_client_auth)
{
return new ServerEncryptionOptions(ssl_context_factory, keystore,
keystore_password,
outbound_keystore,
outbound_keystore_password, truststore,
truststore_password,
cipher_suites, protocol, accepted_protocols,
- algorithm, store_type,
require_client_auth,
+ algorithm, store_type,
require_client_auth.value,
require_endpoint_verification,
optional, internode_encryption,
legacy_ssl_storage_port_enabled).applyConfigInternal();
}
diff --git a/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java
b/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java
index c76d9b0375..cd4e33244c 100644
--- a/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java
+++ b/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java
@@ -64,6 +64,7 @@ import static java.lang.Math.*;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static
org.apache.cassandra.auth.IInternodeAuthenticator.InternodeConnectionDirection.INBOUND;
import static
org.apache.cassandra.concurrent.ExecutorFactory.Global.executorFactory;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
import static
org.apache.cassandra.net.InternodeConnectionUtils.DISCARD_HANDLER_NAME;
import static
org.apache.cassandra.net.InternodeConnectionUtils.SSL_FACTORY_CONTEXT_DESCRIPTION;
import static
org.apache.cassandra.net.InternodeConnectionUtils.SSL_HANDLER_NAME;
@@ -521,7 +522,7 @@ public class InboundConnectionInitiator
private static SslHandler getSslHandler(String description, Channel
channel, EncryptionOptions.ServerEncryptionOptions encryptionOptions) throws
IOException
{
- final boolean verifyPeerCertificate = true;
+ final EncryptionOptions.ClientAuth verifyPeerCertificate = REQUIRED;
SslContext sslContext =
SSLFactory.getOrCreateSslContext(encryptionOptions, verifyPeerCertificate,
ISslContextFactory.SocketType.SERVER,
SSL_FACTORY_CONTEXT_DESCRIPTION);
diff --git a/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java
b/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java
index 63b99a45db..b95d20a158 100644
--- a/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java
+++ b/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java
@@ -29,6 +29,7 @@ import com.google.common.annotations.VisibleForTesting;
import io.netty.util.concurrent.Future; //checkstyle: permit this import
import io.netty.util.concurrent.Promise; //checkstyle: permit this import
+import org.apache.cassandra.config.EncryptionOptions;
import org.apache.cassandra.utils.concurrent.AsyncPromise;
import org.slf4j.Logger;
@@ -65,6 +66,9 @@ import org.apache.cassandra.utils.memory.BufferPools;
import static java.util.concurrent.TimeUnit.*;
import static
org.apache.cassandra.auth.IInternodeAuthenticator.InternodeConnectionDirection.OUTBOUND;
import static
org.apache.cassandra.auth.IInternodeAuthenticator.InternodeConnectionDirection.OUTBOUND_PRECONNECT;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.NOT_REQUIRED;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.OPTIONAL;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
import static
org.apache.cassandra.net.InternodeConnectionUtils.DISCARD_HANDLER_NAME;
import static
org.apache.cassandra.net.InternodeConnectionUtils.SSL_FACTORY_CONTEXT_DESCRIPTION;
import static
org.apache.cassandra.net.InternodeConnectionUtils.SSL_HANDLER_NAME;
@@ -241,14 +245,18 @@ public class OutboundConnectionInitiator<SuccessType
extends OutboundConnectionI
private SslContext getSslContext(SslFallbackConnectionType
connectionType) throws IOException
{
- boolean requireClientAuth = false;
- if (connectionType == SslFallbackConnectionType.MTLS ||
connectionType == SslFallbackConnectionType.SSL)
+ EncryptionOptions.ClientAuth requireClientAuth = NOT_REQUIRED;
+ if (connectionType == SslFallbackConnectionType.MTLS )
{
- requireClientAuth = true;
+ requireClientAuth = REQUIRED;
+ }
+ else if(connectionType == SslFallbackConnectionType.SSL)
+ {
+ requireClientAuth = OPTIONAL;
}
else if (connectionType == SslFallbackConnectionType.SERVER_CONFIG)
{
- requireClientAuth = settings.withEncryption();
+ requireClientAuth = settings.withEncryption() ? REQUIRED:
NOT_REQUIRED;
}
return SSLFactory.getOrCreateSslContext(settings.encryption,
requireClientAuth, ISslContextFactory.SocketType.CLIENT,
SSL_FACTORY_CONTEXT_DESCRIPTION);
}
diff --git
a/src/java/org/apache/cassandra/security/AbstractSslContextFactory.java
b/src/java/org/apache/cassandra/security/AbstractSslContextFactory.java
index 406c4720f2..c2a7fdb975 100644
--- a/src/java/org/apache/cassandra/security/AbstractSslContextFactory.java
+++ b/src/java/org/apache/cassandra/security/AbstractSslContextFactory.java
@@ -35,6 +35,10 @@ import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
+import org.apache.cassandra.config.EncryptionOptions;
+
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.NOT_REQUIRED;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
import static
org.apache.cassandra.config.CassandraRelevantProperties.DISABLE_TCACTIVE_OPENSSL;
@@ -64,7 +68,7 @@ abstract public class AbstractSslContextFactory implements
ISslContextFactory
protected final List<String> accepted_protocols;
protected final String algorithm;
protected final String store_type;
- protected final boolean require_client_auth;
+ protected final EncryptionOptions.ClientAuth clientAuth;
protected final boolean require_endpoint_verification;
/*
ServerEncryptionOptions does not use the enabled flag at all instead using
the existing
@@ -86,7 +90,7 @@ abstract public class AbstractSslContextFactory implements
ISslContextFactory
accepted_protocols = null;
algorithm = null;
store_type = "JKS";
- require_client_auth = false;
+ clientAuth = NOT_REQUIRED;
require_endpoint_verification = false;
enabled = null;
optional = null;
@@ -101,7 +105,7 @@ abstract public class AbstractSslContextFactory implements
ISslContextFactory
accepted_protocols = getStringList("accepted_protocols");
algorithm = getString("algorithm");
store_type = getString("store_type", "JKS");
- require_client_auth = getBoolean("require_client_auth", false);
+ clientAuth = parameters.get("require_client_auth") == null ?
NOT_REQUIRED :
EncryptionOptions.ClientAuth.from(getString("require_client_auth"));
require_endpoint_verification =
getBoolean("require_endpoint_verification", false);
enabled = getBoolean("enabled");
optional = getBoolean("optional");
@@ -149,9 +153,15 @@ abstract public class AbstractSslContextFactory implements
ISslContextFactory
@Override
public SSLContext createJSSESslContext(boolean verifyPeerCertificate)
throws SSLException
+ {
+ return createJSSESslContext(verifyPeerCertificate ? REQUIRED :
NOT_REQUIRED);
+ }
+
+ @Override
+ public SSLContext createJSSESslContext(EncryptionOptions.ClientAuth
clientAuth) throws SSLException
{
TrustManager[] trustManagers = null;
- if (verifyPeerCertificate)
+ if (clientAuth != NOT_REQUIRED)
trustManagers = buildTrustManagerFactory().getTrustManagers();
KeyManagerFactory kmf = buildKeyManagerFactory();
@@ -171,6 +181,13 @@ abstract public class AbstractSslContextFactory implements
ISslContextFactory
@Override
public SslContext createNettySslContext(boolean verifyPeerCertificate,
SocketType socketType,
CipherSuiteFilter cipherFilter)
throws SSLException
+ {
+ return createNettySslContext(verifyPeerCertificate ? REQUIRED :
NOT_REQUIRED, socketType, cipherFilter);
+ }
+
+ @Override
+ public SslContext createNettySslContext(EncryptionOptions.ClientAuth
clientAuth, SocketType socketType,
+ CipherSuiteFilter cipherFilter)
throws SSLException
{
/*
There is a case where the netty/openssl combo might not support
using KeyManagerFactory. Specifically,
@@ -184,8 +201,7 @@ abstract public class AbstractSslContextFactory implements
ISslContextFactory
if (socketType == SocketType.SERVER)
{
KeyManagerFactory kmf = buildKeyManagerFactory();
- builder =
SslContextBuilder.forServer(kmf).clientAuth(this.require_client_auth ?
ClientAuth.REQUIRE :
-
ClientAuth.NONE);
+ builder =
SslContextBuilder.forServer(kmf).clientAuth(toNettyClientAuth(this.clientAuth));
}
else
{
@@ -200,7 +216,7 @@ abstract public class AbstractSslContextFactory implements
ISslContextFactory
if (cipher_suites != null && !cipher_suites.isEmpty())
builder.ciphers(cipher_suites, cipherFilter);
- if (verifyPeerCertificate)
+ if (clientAuth != NOT_REQUIRED)
builder.trustManager(buildTrustManagerFactory());
return builder.build();
@@ -274,4 +290,19 @@ abstract public class AbstractSslContextFactory implements
ISslContextFactory
* @throws SSLException
*/
abstract protected KeyManagerFactory buildOutboundKeyManagerFactory()
throws SSLException;
+
+ private ClientAuth toNettyClientAuth(EncryptionOptions.ClientAuth
clientAuth)
+ {
+ switch (clientAuth)
+ {
+ case REQUIRED:
+ return ClientAuth.REQUIRE;
+ case NOT_REQUIRED:
+ return ClientAuth.NONE;
+ case OPTIONAL:
+ return ClientAuth.OPTIONAL;
+ default:
+ throw new RuntimeException("Unsupported client auth " +
clientAuth);
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/security/ISslContextFactory.java
b/src/java/org/apache/cassandra/security/ISslContextFactory.java
index 11c4717b3c..1db5f579b1 100644
--- a/src/java/org/apache/cassandra/security/ISslContextFactory.java
+++ b/src/java/org/apache/cassandra/security/ISslContextFactory.java
@@ -24,6 +24,7 @@ import javax.net.ssl.SSLException;
import io.netty.handler.ssl.CipherSuiteFilter;
import io.netty.handler.ssl.SslContext;
+import org.apache.cassandra.config.EncryptionOptions;
/**
* The purpose of this interface is to provide pluggable mechanism for
creating custom JSSE and Netty SSLContext
@@ -57,9 +58,33 @@ public interface ISslContextFactory
* @param verifyPeerCertificate {@code true} if SSL peer's certificate
needs to be verified; {@code false} otherwise
* @return JSSE's {@link SSLContext}
* @throws SSLException in case the Ssl Context creation fails for some
reason
+ *
+ * @deprecated See CASSANDRA-18811
*/
+ @Deprecated(since = "5.1")
SSLContext createJSSESslContext(boolean verifyPeerCertificate) throws
SSLException;
+ /**
+ * Creates JSSE SSLContext.
+ *
+ * @param clientAuth {@code REQUIRED} if SSL peer's certificate needs to
be verified; {@code OPTIONAL} if peer's
+ * certificate needs to be optionally
verfied; {@code NOT_REQUIRED} otherwise.
+ * @return JSSE's {@link SSLContext}
+ * @throws SSLException in case the Ssl Context creation fails for some
reason
+ */
+ default SSLContext createJSSESslContext(EncryptionOptions.ClientAuth
clientAuth) throws SSLException
+ {
+ switch (clientAuth)
+ {
+ case REQUIRED:
+ return createJSSESslContext(true);
+ case NOT_REQUIRED:
+ case OPTIONAL:
+ default:
+ return createJSSESslContext(false);
+ }
+ }
+
/**
* Creates Netty's SslContext object.
*
@@ -69,10 +94,38 @@ public interface ISslContextFactory
* {@link
io.netty.handler.ssl.SslContextBuilder#ciphers(Iterable, CipherSuiteFilter)}
* @return Netty's {@link SslContext}
* @throws SSLException in case the Ssl Context creation fails for some
reason
+ *
+ * @deprecated See CASSANDRA-18811
*/
+ @Deprecated(since = "5.1")
SslContext createNettySslContext(boolean verifyPeerCertificate, SocketType
socketType,
CipherSuiteFilter cipherFilter) throws
SSLException;
+ /**
+ * Creates Netty's SslContext object.
+ *
+ * @param clientAuth {@code REQUIRED} if SSL peer's certificate needs to
be verified; {@code OPTIONAL} if peer's
+ * * certificate needs to be optionally
verfied; {@code NOT_REQUIRED} otherwise.
+ * @param socketType {@link SocketType} for Netty's Inbound or
Outbound channels
+ * @param cipherFilter to allow Netty's cipher suite filtering,
e.g.
+ * {@link
io.netty.handler.ssl.SslContextBuilder#ciphers(Iterable, CipherSuiteFilter)}
+ * @return Netty's {@link SslContext}
+ * @throws SSLException in case the Ssl Context creation fails for some
reason
+ */
+ default SslContext createNettySslContext(EncryptionOptions.ClientAuth
clientAuth, SocketType socketType,
+ CipherSuiteFilter cipherFilter)
throws SSLException
+ {
+ switch (clientAuth)
+ {
+ case REQUIRED:
+ return createNettySslContext(true, socketType, cipherFilter);
+ case NOT_REQUIRED:
+ case OPTIONAL:
+ default:
+ return createNettySslContext(false, socketType, cipherFilter);
+ }
+ }
+
/**
* Initializes hot reloading of the security keys/certs. The
implementation must guarantee this to be thread safe.
*
diff --git a/src/java/org/apache/cassandra/security/SSLFactory.java
b/src/java/org/apache/cassandra/security/SSLFactory.java
index 5a175b3fd6..c55e970b4f 100644
--- a/src/java/org/apache/cassandra/security/SSLFactory.java
+++ b/src/java/org/apache/cassandra/security/SSLFactory.java
@@ -47,6 +47,8 @@ import
org.apache.cassandra.security.ISslContextFactory.SocketType;
import static
org.apache.cassandra.config.CassandraRelevantProperties.DISABLE_TCACTIVE_OPENSSL;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
+
/**
* A Factory for providing and setting up client {@link SSLSocket}s. Also
provides
* methods for creating both JSSE {@link SSLContext} instances as well as
netty {@link SslContext} instances.
@@ -122,15 +124,15 @@ public final class SSLFactory
/**
* Create a JSSE {@link SSLContext}.
*/
- public static SSLContext createSSLContext(EncryptionOptions options,
boolean verifyPeerCertificate) throws IOException
+ public static SSLContext createSSLContext(EncryptionOptions options,
EncryptionOptions.ClientAuth clientAuth) throws IOException
{
- return
options.sslContextFactoryInstance.createJSSESslContext(verifyPeerCertificate);
+ return
options.sslContextFactoryInstance.createJSSESslContext(clientAuth);
}
/**
* get a netty {@link SslContext} instance
*/
- public static SslContext getOrCreateSslContext(EncryptionOptions options,
boolean verifyPeerCertificate,
+ public static SslContext getOrCreateSslContext(EncryptionOptions options,
EncryptionOptions.ClientAuth clientAuth,
SocketType socketType,
String contextDescription)
throws IOException
{
@@ -141,7 +143,7 @@ public final class SSLFactory
if (sslContext != null)
return sslContext;
- sslContext = createNettySslContext(options, verifyPeerCertificate,
socketType);
+ sslContext = createNettySslContext(options, clientAuth, socketType);
SslContext previous = cachedSslContexts.putIfAbsent(key, sslContext);
if (previous == null)
@@ -154,20 +156,20 @@ public final class SSLFactory
/**
* Create a Netty {@link SslContext}
*/
- static SslContext createNettySslContext(EncryptionOptions options, boolean
verifyPeerCertificate,
+ static SslContext createNettySslContext(EncryptionOptions options,
EncryptionOptions.ClientAuth clientAuth,
SocketType socketType) throws
IOException
{
- return createNettySslContext(options, verifyPeerCertificate,
socketType,
+ return createNettySslContext(options, clientAuth, socketType,
LoggingCipherSuiteFilter.QUIET_FILTER);
}
/**
* Create a Netty {@link SslContext} with a supplied cipherFilter
*/
- static SslContext createNettySslContext(EncryptionOptions options, boolean
verifyPeerCertificate,
+ static SslContext createNettySslContext(EncryptionOptions options,
EncryptionOptions.ClientAuth clientAuth,
SocketType socketType,
CipherSuiteFilter cipherFilter) throws IOException
{
- return
options.sslContextFactoryInstance.createNettySslContext(verifyPeerCertificate,
socketType,
+ return
options.sslContextFactoryInstance.createNettySslContext(clientAuth, socketType,
cipherFilter);
}
@@ -206,7 +208,7 @@ public final class SSLFactory
try
{
validateSslContext(key.contextDescription, opts,
- opts instanceof
EncryptionOptions.ServerEncryptionOptions || opts.require_client_auth, false);
+ opts instanceof
EncryptionOptions.ServerEncryptionOptions? REQUIRED : opts.getClientAuth(),
false);
logger.info("SSL certificates have been updated for {}.
Resetting the ssl contexts for new " +
"connections.", key.contextDescription);
clearSslContextCache(key.encryptionOptions, keysToCheck);
@@ -353,7 +355,7 @@ public final class SSLFactory
return !string.equals("SSLv2Hello");
}
- public static void validateSslContext(String contextDescription,
EncryptionOptions options, boolean verifyPeerCertificate, boolean
logProtocolAndCiphers) throws IOException
+ public static void validateSslContext(String contextDescription,
EncryptionOptions options, EncryptionOptions.ClientAuth clientAuth, boolean
logProtocolAndCiphers) throws IOException
{
if (options != null && options.tlsEncryptionPolicy() !=
EncryptionOptions.TlsEncryptionPolicy.UNENCRYPTED)
{
@@ -361,7 +363,7 @@ public final class SSLFactory
{
CipherSuiteFilter loggingCipherSuiteFilter =
logProtocolAndCiphers ? new LoggingCipherSuiteFilter(contextDescription)
: LoggingCipherSuiteFilter.QUIET_FILTER;
- SslContext serverSslContext = createNettySslContext(options,
verifyPeerCertificate, SocketType.SERVER, loggingCipherSuiteFilter);
+ SslContext serverSslContext = createNettySslContext(options,
clientAuth, SocketType.SERVER, loggingCipherSuiteFilter);
try
{
SSLEngine engine =
serverSslContext.newEngine(ByteBufAllocator.DEFAULT);
@@ -406,7 +408,7 @@ public final class SSLFactory
}
// Make sure it is possible to build the client context too
- SslContext clientSslContext = createNettySslContext(options,
verifyPeerCertificate, SocketType.CLIENT);
+ SslContext clientSslContext = createNettySslContext(options,
clientAuth, SocketType.CLIENT);
ReferenceCountUtil.release(clientSslContext);
}
catch (Exception e)
@@ -421,8 +423,8 @@ public final class SSLFactory
*/
public static void
validateSslCerts(EncryptionOptions.ServerEncryptionOptions serverOpts,
EncryptionOptions clientOpts) throws IOException
{
- validateSslContext("server_encryption_options", serverOpts, true,
false);
- validateSslContext("client_encryption_options", clientOpts,
clientOpts.require_client_auth, false);
+ validateSslContext("server_encryption_options", serverOpts, REQUIRED,
false);
+ validateSslContext("client_encryption_options", clientOpts,
clientOpts.getClientAuth(), false);
}
static class CacheKey
diff --git a/src/java/org/apache/cassandra/tools/BulkLoader.java
b/src/java/org/apache/cassandra/tools/BulkLoader.java
index 8802b9e837..367903a595 100644
--- a/src/java/org/apache/cassandra/tools/BulkLoader.java
+++ b/src/java/org/apache/cassandra/tools/BulkLoader.java
@@ -48,6 +48,7 @@ import org.apache.cassandra.utils.JVMStabilityInspector;
import org.apache.cassandra.utils.NativeSSTableLoaderClient;
import org.apache.cassandra.utils.OutputHandler;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
public class BulkLoader
@@ -264,7 +265,7 @@ public class BulkLoader
SSLContext sslContext;
try
{
- sslContext = SSLFactory.createSSLContext(clientEncryptionOptions,
true);
+ sslContext = SSLFactory.createSSLContext(clientEncryptionOptions,
REQUIRED);
}
catch (IOException e)
{
diff --git a/src/java/org/apache/cassandra/tools/LoaderOptions.java
b/src/java/org/apache/cassandra/tools/LoaderOptions.java
index 03c8ee6024..1f368168a7 100644
--- a/src/java/org/apache/cassandra/tools/LoaderOptions.java
+++ b/src/java/org/apache/cassandra/tools/LoaderOptions.java
@@ -52,6 +52,7 @@ import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.tools.BulkLoader.CmdLineOptions;
import static
org.apache.cassandra.config.DataRateSpec.DataRateUnit.MEBIBYTES_PER_SECOND;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
public class LoaderOptions
{
@@ -648,7 +649,7 @@ public class LoaderOptions
{
// if a keystore was provided, lets assume we'll need to
use
clientEncOptions =
clientEncOptions.withKeyStore(cmd.getOptionValue(SSL_KEYSTORE))
-
.withRequireClientAuth(true);
+
.withRequireClientAuth(REQUIRED);
}
if (cmd.hasOption(SSL_KEYSTORE_PW))
diff --git a/src/java/org/apache/cassandra/transport/PipelineConfigurator.java
b/src/java/org/apache/cassandra/transport/PipelineConfigurator.java
index d19af211e2..1bcebf96cf 100644
--- a/src/java/org/apache/cassandra/transport/PipelineConfigurator.java
+++ b/src/java/org/apache/cassandra/transport/PipelineConfigurator.java
@@ -168,7 +168,7 @@ public class PipelineConfigurator
logger.debug("Enabling optionally encrypted CQL connections
between client and server");
return channel -> {
SslContext sslContext =
SSLFactory.getOrCreateSslContext(encryptionOptions,
-
encryptionOptions.require_client_auth,
+
encryptionOptions.getClientAuth(),
ISslContextFactory.SocketType.SERVER,
SSL_FACTORY_CONTEXT_DESCRIPTION);
@@ -204,7 +204,7 @@ public class PipelineConfigurator
logger.debug("Enabling encrypted CQL connections between
client and server");
return channel -> {
SslContext sslContext =
SSLFactory.getOrCreateSslContext(encryptionOptions,
-
encryptionOptions.require_client_auth,
+
encryptionOptions.getClientAuth(),
ISslContextFactory.SocketType.SERVER,
SSL_FACTORY_CONTEXT_DESCRIPTION);
InetSocketAddress peer =
encryptionOptions.require_endpoint_verification ? (InetSocketAddress)
channel.remoteAddress() : null;
diff --git a/src/java/org/apache/cassandra/transport/SimpleClient.java
b/src/java/org/apache/cassandra/transport/SimpleClient.java
index f4a1b9fd3f..0fa3a7af31 100644
--- a/src/java/org/apache/cassandra/transport/SimpleClient.java
+++ b/src/java/org/apache/cassandra/transport/SimpleClient.java
@@ -623,7 +623,7 @@ public class SimpleClient implements Closeable
protected void initChannel(Channel channel) throws Exception
{
super.initChannel(channel);
- SslContext sslContext =
SSLFactory.getOrCreateSslContext(encryptionOptions,
encryptionOptions.require_client_auth,
+ SslContext sslContext =
SSLFactory.getOrCreateSslContext(encryptionOptions,
encryptionOptions.getClientAuth(),
ISslContextFactory.SocketType.CLIENT, SSL_FACTORY_CONTEXT_DESCRIPTION);
InetSocketAddress peer =
encryptionOptions.require_endpoint_verification ? new InetSocketAddress(host,
port) : null;
channel.pipeline().addFirst("ssl", newSslHandler(channel,
sslContext, peer));
diff --git
a/test/distributed/org/apache/cassandra/distributed/test/AbstractEncryptionOptionsImpl.java
b/test/distributed/org/apache/cassandra/distributed/test/AbstractEncryptionOptionsImpl.java
index b488867433..5f119c6bdf 100644
---
a/test/distributed/org/apache/cassandra/distributed/test/AbstractEncryptionOptionsImpl.java
+++
b/test/distributed/org/apache/cassandra/distributed/test/AbstractEncryptionOptionsImpl.java
@@ -54,6 +54,7 @@ import org.apache.cassandra.security.ISslContextFactory;
import org.apache.cassandra.security.SSLFactory;
import static java.util.concurrent.TimeUnit.SECONDS;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
import static
org.apache.cassandra.distributed.test.AbstractEncryptionOptionsImpl.ConnectResult.CONNECTING;
import static
org.apache.cassandra.distributed.test.AbstractEncryptionOptionsImpl.ConnectResult.UNINITIALIZED;
import static
org.apache.cassandra.utils.concurrent.Condition.newOneTimeCondition;
@@ -199,8 +200,8 @@ public class AbstractEncryptionOptionsImpl extends
TestBaseImpl
setProtocolAndCipher(null, null);
SslContext sslContext = SSLFactory.getOrCreateSslContext(
-
encryptionOptions.withAcceptedProtocols(acceptedProtocols).withCipherSuites(cipherSuites),
- true, ISslContextFactory.SocketType.CLIENT, "test");
+
encryptionOptions.withAcceptedProtocols(acceptedProtocols).withCipherSuites(cipherSuites),
+ REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
EventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
diff --git
a/test/distributed/org/apache/cassandra/distributed/test/NativeTransportEncryptionOptionsTest.java
b/test/distributed/org/apache/cassandra/distributed/test/NativeTransportEncryptionOptionsTest.java
index 3e8c926480..86d4e8fa26 100644
---
a/test/distributed/org/apache/cassandra/distributed/test/NativeTransportEncryptionOptionsTest.java
+++
b/test/distributed/org/apache/cassandra/distributed/test/NativeTransportEncryptionOptionsTest.java
@@ -41,6 +41,9 @@ import
com.datastax.shaded.netty.handler.ssl.SslContextBuilder;
import org.apache.cassandra.distributed.Cluster;
import org.apache.cassandra.distributed.api.Feature;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
public class NativeTransportEncryptionOptionsTest extends
AbstractEncryptionOptionsImpl
{
@Rule
@@ -154,9 +157,9 @@ public class NativeTransportEncryptionOptionsTest extends
AbstractEncryptionOpti
/**
* Tests that the negotiated protocol is the highest common protocol
between the client and server.
- * <p>
- * Note: This test uses TLSV1.1, which is disabled by default in JDK 8 and
higher. If the test fails with
- * FAILED_TO_NEGOTIATE, it may be necessary to check the java.security
file in your JDK installation and remove
+ * <p>
+ * Note: This test uses TLSV1.1, which is disabled by default in JDK 8 and
higher. If the test fails with
+ * FAILED_TO_NEGOTIATE, it may be necessary to check the java.security
file in your JDK installation and remove
* TLSv1.1 from the jdk.tls.disabledAlgorithms.
* @see <a
href="https://issues.apache.org/jira/browse/CASSANDRA-18540">CASSANDRA-18540</a>
* @see <a
href="https://senthilnayagan.medium.com/tlsv1-and-tlsv1-1-protocols-disabled-by-default-in-javas-latest-patch-released-on-april-20-2021-52c309f6b16d">
@@ -278,6 +281,138 @@ public class NativeTransportEncryptionOptionsTest extends
AbstractEncryptionOpti
testEndpointVerification(true, true);
}
+ @Test
+ public void testOptionalMtlsModeAllowNonSSLConnections() throws Exception
+ {
+ // When server is configured in optional mTLS mode and allowing ssl
connections, it should accept
+ // non-ssl, ssl, mTLS based connections
+ try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
+ c.with(Feature.NATIVE_PROTOCOL);
+ c.set("client_encryption_options",
+ ImmutableMap.builder().putAll(validKeystore)
+ .put("enabled", true)
+ .put("require_client_auth", "optional")
+ .put("optional", true)
+ .build());
+ }).start())
+ {
+ InetAddress address =
cluster.get(1).config().broadcastAddress().getAddress();
+
+ // non-ssl connections should succeed
+ com.datastax.driver.core.Cluster nonSSLDriver =
com.datastax.driver.core.Cluster.builder()
+
.addContactPoint(address.getHostAddress())
+
.build();
+ assertNotNull(nonSSLDriver.connect());
+
+ // ssl connections should succeed
+ com.datastax.driver.core.Cluster sslDriver =
com.datastax.driver.core.Cluster.builder()
+
.addContactPoint(address.getHostAddress())
+
.withSSL(sslOptions(false))
+
.build();
+ assertNotNull(sslDriver.connect());
+
+ // mtls connections should succeed
+ com.datastax.driver.core.Cluster mtlsDriver =
com.datastax.driver.core.Cluster.builder()
+
.addContactPoint(address.getHostAddress())
+
.withSSL(sslOptions(true))
+
.build();
+ assertNotNull(mtlsDriver.connect());
+ }
+ }
+
+ @Test
+ public void testOptionalMtlsModeDoNotAllowNonSSLConnections() throws
Exception
+ {
+ // When server is configured in optional mTLS mode restricting non-ssl
connections, it should accept
+ // ssl, mTLS based connections and reject any non-ssl based connections
+ try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
+ c.with(Feature.NATIVE_PROTOCOL);
+ c.set("client_encryption_options",
+ ImmutableMap.builder().putAll(validKeystore)
+ .put("enabled", true)
+ .put("require_client_auth", "optional")
+ .put("optional", false)
+ .build());
+ }).start())
+ {
+ InetAddress address =
cluster.get(1).config().broadcastAddress().getAddress();
+
+ // ssl connections should succeed
+ com.datastax.driver.core.Cluster sslDriver =
com.datastax.driver.core.Cluster.builder()
+
.addContactPoint(address.getHostAddress())
+
.withSSL(sslOptions(false))
+
.build();
+ assertNotNull(sslDriver.connect());
+
+ // mtls connections should succeed
+ com.datastax.driver.core.Cluster mtlsDriver =
com.datastax.driver.core.Cluster.builder()
+
.addContactPoint(address.getHostAddress())
+
.withSSL(sslOptions(true))
+
.build();
+ assertNotNull(mtlsDriver.connect());
+
+ // non-ssl connections should not succeed
+ com.datastax.driver.core.Cluster nonSSLDriver =
com.datastax.driver.core.Cluster.builder()
+
.addContactPoint(address.getHostAddress())
+
.build();
+ expectedException.expect(NoHostAvailableException.class);
+ assertNull(nonSSLDriver.connect());
+ }
+ }
+
+ @Test
+ public void testOptionalSSLMode() throws Exception
+ {
+ // When server is configured in optional ssl mode, it should accept
+ // non-ssl, ssl
+ try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
+ // Server configuration for optional mTLS mode
+ c.with(Feature.NATIVE_PROTOCOL);
+ c.set("client_encryption_options",
+ ImmutableMap.builder().putAll(validKeystore)
+ .put("enabled", true)
+ .put("require_client_auth", "false")
+ .put("optional", true)
+ .build());
+ }).start())
+ {
+ InetAddress address =
cluster.get(1).config().broadcastAddress().getAddress();
+
+ // non-ssl connections should succeed
+ com.datastax.driver.core.Cluster nonSSLDriver =
com.datastax.driver.core.Cluster.builder()
+
.addContactPoint(address.getHostAddress())
+
.build();
+ assertNotNull(nonSSLDriver.connect());
+
+ // ssl connections should succeed
+ com.datastax.driver.core.Cluster sslDriver =
com.datastax.driver.core.Cluster.builder()
+
.addContactPoint(address.getHostAddress())
+
.withSSL(sslOptions(false))
+
.build();
+ assertNotNull(sslDriver.connect());
+
+ // mtls connections should succeed
+ com.datastax.driver.core.Cluster mtlsDriver =
com.datastax.driver.core.Cluster.builder()
+
.addContactPoint(address.getHostAddress())
+
.withSSL(sslOptions(true))
+
.build();
+ assertNotNull(mtlsDriver.connect());
+ }
+ }
+
+ private SSLOptions sslOptions(boolean withKeyStore) throws Exception
+ {
+ SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
+
sslContextBuilder.trustManager(createTrustManagerFactory("test/conf/cassandra_ssl_test.truststore",
"cassandra"));
+ if (withKeyStore)
+ {
+
sslContextBuilder.keyManager(createKeyManagerFactory("test/conf/cassandra_ssl_test_outbound.keystore",
"cassandra"));
+ }
+
+ SslContext sslContext = sslContextBuilder.build();
+ return socketChannel -> sslContext.newHandler(socketChannel.alloc());
+ }
+
private void testEndpointVerification(boolean requireEndpointVerification,
boolean ipInSAN) throws Throwable
{
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
diff --git
a/test/unit/org/apache/cassandra/auth/MutualTlsAuthenticatorTest.java
b/test/unit/org/apache/cassandra/auth/MutualTlsAuthenticatorTest.java
index b30a8f8d0b..14b31a3c87 100644
--- a/test/unit/org/apache/cassandra/auth/MutualTlsAuthenticatorTest.java
+++ b/test/unit/org/apache/cassandra/auth/MutualTlsAuthenticatorTest.java
@@ -37,6 +37,7 @@ import org.junit.runners.Parameterized;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.config.Config;
import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.config.EncryptionOptions;
import org.apache.cassandra.exceptions.AuthenticationException;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.net.MessagingService;
@@ -47,6 +48,7 @@ import org.apache.cassandra.utils.MBeanWrapper;
import static org.apache.cassandra.auth.AuthTestUtils.getMockInetAddress;
import static
org.apache.cassandra.auth.AuthTestUtils.initializeIdentityRolesTable;
import static org.apache.cassandra.auth.AuthTestUtils.loadCertificateChain;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -78,7 +80,7 @@ public class MutualTlsAuthenticatorTest
((CassandraRoleManager)DatabaseDescriptor.getRoleManager()).loadIdentityStatement();
final Config config = DatabaseDescriptor.getRawConfig();
config.client_encryption_options =
config.client_encryption_options.withEnabled(true)
-
.withRequireClientAuth(true);
+
.withRequireClientAuth(REQUIRED);
}
@After
@@ -172,6 +174,22 @@ public class MutualTlsAuthenticatorTest
assertEquals("readonly_user", urnCache.get(identity2));
}
+ @Test
+ public void testValidateConfiguration()
+ {
+ // In strict mTLS mode mtls authenticator should check for
require_client_auth to be true
+ final Config config = DatabaseDescriptor.getRawConfig();
+ String msg = "MutualTlsAuthenticator requires
client_encryption_options.enabled to be true" +
+ " & client_encryption_options.require_client_auth to be
true";
+ MutualTlsAuthenticator mutualTlsAuthenticator =
createAndInitializeMtlsAuthenticator();
+
+ config.client_encryption_options =
config.client_encryption_options.withEnabled(true)
+
.withRequireClientAuth(EncryptionOptions.ClientAuth.NOT_REQUIRED);
+ expectedException.expect(ConfigurationException.class);
+ expectedException.expectMessage(msg);
+ mutualTlsAuthenticator.validateConfiguration();
+ }
+
MutualTlsAuthenticator createAndInitializeMtlsAuthenticator()
{
Map<String, String> parameters =
Collections.singletonMap("validator_class_name", getValidatorClass());
diff --git
a/test/unit/org/apache/cassandra/auth/MutualTlsWithPasswordFallbackAuthenticatorTest.java
b/test/unit/org/apache/cassandra/auth/MutualTlsWithPasswordFallbackAuthenticatorTest.java
index 6a522ca853..95f7c133ea 100644
---
a/test/unit/org/apache/cassandra/auth/MutualTlsWithPasswordFallbackAuthenticatorTest.java
+++
b/test/unit/org/apache/cassandra/auth/MutualTlsWithPasswordFallbackAuthenticatorTest.java
@@ -33,6 +33,7 @@ import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.config.Config;
import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.config.EncryptionOptions;
import static org.apache.cassandra.auth.AuthTestUtils.getMockInetAddress;
import static org.junit.Assert.assertNotNull;
@@ -50,7 +51,7 @@ public class MutualTlsWithPasswordFallbackAuthenticatorTest
SchemaLoader.loadSchema();
Config config = DatabaseDescriptor.getRawConfig();
config.client_encryption_options =
config.client_encryption_options.withEnabled(true)
-
.withRequireClientAuth(true);
+
.withRequireClientAuth(EncryptionOptions.ClientAuth.OPTIONAL);
Map<String, String> parameters =
Collections.singletonMap("validator_class_name",
"org.apache.cassandra.auth.SpiffeCertificateValidator");
fallbackAuthenticator = new
MutualTlsWithPasswordFallbackAuthenticator(parameters);
fallbackAuthenticator.setup();
@@ -88,4 +89,11 @@ public class MutualTlsWithPasswordFallbackAuthenticatorTest
IAuthenticator.SaslNegotiator mutualtlsAuthenticator =
fallbackAuthenticator.newSaslNegotiator(getMockInetAddress(),
clientCertificatesCorp);
assertTrue(mutualtlsAuthenticator instanceof
MutualTlsAuthenticator.CertificateNegotiator);
}
+
+ @Test
+ public void testValidateConfiguration()
+ {
+ // In optional mTLS mode fallback authenticator should not check for
require_client_auth to be true
+ fallbackAuthenticator.validateConfiguration();
+ }
}
diff --git a/test/unit/org/apache/cassandra/config/ConfigCompatibilityTest.java
b/test/unit/org/apache/cassandra/config/ConfigCompatibilityTest.java
index e6e714daaa..f965182915 100644
--- a/test/unit/org/apache/cassandra/config/ConfigCompatibilityTest.java
+++ b/test/unit/org/apache/cassandra/config/ConfigCompatibilityTest.java
@@ -104,6 +104,7 @@ public class ConfigCompatibilityTest
.add("authenticator types do not match;
org.apache.cassandra.config.ParameterizedClass != java.lang.String")
.add("Property internode_authenticator used to be a value-type, but now is
nested type class org.apache.cassandra.config.ParameterizedClass")
.add("Property authenticator used to be a value-type, but now is nested type
class org.apache.cassandra.config.ParameterizedClass")
+
.add("require_client_auth types do not match; java.lang.String !=
java.lang.Boolean")
.build();
/**
@@ -146,7 +147,7 @@ public class ConfigCompatibilityTest
public void diff_5_0() throws IOException
{
diff(TEST_DIR + "/version=5.0-alpha1.yml",
ImmutableSet.<String>builder()
- .build(),
ImmutableSet.of());
+ .build(),
EXPECTED_FOR_50);
}
private void diff(String original, Set<String> ignore, Set<String>
expectedErrors) throws IOException
diff --git
a/test/unit/org/apache/cassandra/config/EncryptionOptionsEqualityTest.java
b/test/unit/org/apache/cassandra/config/EncryptionOptionsEqualityTest.java
index e5aabc8ff3..7c86069a80 100644
--- a/test/unit/org/apache/cassandra/config/EncryptionOptionsEqualityTest.java
+++ b/test/unit/org/apache/cassandra/config/EncryptionOptionsEqualityTest.java
@@ -26,6 +26,8 @@ import org.junit.Test;
import org.apache.cassandra.security.DefaultSslContextFactory;
import org.apache.cassandra.security.DummySslContextFactoryImpl;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.NOT_REQUIRED;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@@ -46,7 +48,7 @@ public class EncryptionOptionsEqualityTest
.withOutboundKeystore("test/conf/cassandra_outbound.keystore")
.withOutboundKeystorePassword("cassandra")
.withProtocol("TLSv1.1")
- .withRequireClientAuth(true)
+ .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false);
}
@@ -60,7 +62,7 @@ public class EncryptionOptionsEqualityTest
.withTrustStore("test/conf/cassandra_ssl_test.truststore")
.withTrustStorePassword("cassandra")
.withProtocol("TLSv1.1")
- .withRequireClientAuth(true)
+ .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false);
EncryptionOptions encryptionOptions2 =
@@ -71,7 +73,7 @@ public class EncryptionOptionsEqualityTest
.withTrustStore("test/conf/cassandra_ssl_test.truststore")
.withTrustStorePassword("cassandra")
.withProtocol("TLSv1.1")
- .withRequireClientAuth(true)
+ .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false);
assertEquals(encryptionOptions1, encryptionOptions2);
@@ -88,7 +90,7 @@ public class EncryptionOptionsEqualityTest
new EncryptionOptions()
.withSslContextFactory(new
ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters1))
.withProtocol("TLSv1.1")
- .withRequireClientAuth(true)
+ .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false);
Map<String,String> parameters2 = new HashMap<>();
@@ -98,7 +100,7 @@ public class EncryptionOptionsEqualityTest
new EncryptionOptions()
.withSslContextFactory(new
ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters2))
.withProtocol("TLSv1.1")
- .withRequireClientAuth(true)
+ .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false);
assertEquals(encryptionOptions1, encryptionOptions2);
@@ -115,7 +117,7 @@ public class EncryptionOptionsEqualityTest
new EncryptionOptions()
.withSslContextFactory(new
ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters1))
.withProtocol("TLSv1.1")
- .withRequireClientAuth(false)
+ .withRequireClientAuth(NOT_REQUIRED)
.withRequireEndpointVerification(true);
Map<String,String> parameters2 = new HashMap<>();
@@ -125,7 +127,7 @@ public class EncryptionOptionsEqualityTest
new EncryptionOptions()
.withSslContextFactory(new
ParameterizedClass(DefaultSslContextFactory.class.getName(), parameters2))
.withProtocol("TLSv1.1")
- .withRequireClientAuth(false)
+ .withRequireClientAuth(NOT_REQUIRED)
.withRequireEndpointVerification(true);
assertNotEquals(encryptionOptions1, encryptionOptions2);
diff --git a/test/unit/org/apache/cassandra/config/EncryptionOptionsTest.java
b/test/unit/org/apache/cassandra/config/EncryptionOptionsTest.java
index 0ff2124794..f09527067f 100644
--- a/test/unit/org/apache/cassandra/config/EncryptionOptionsTest.java
+++ b/test/unit/org/apache/cassandra/config/EncryptionOptionsTest.java
@@ -61,7 +61,7 @@ public class EncryptionOptionsTest
new HashMap<>()),
keystorePath, "dummypass",
"dummytruststore", "dummypass",
-
Collections.emptyList(), null, null, null, "JKS", false, false, enabled,
optional)
+
Collections.emptyList(), null, null, null, "JKS", "false", false, enabled,
optional)
.applyConfig(),
expected,
String.format("optional=%s
keystore=%s enabled=%s", optional, keystorePath, enabled));
@@ -75,7 +75,7 @@ public class EncryptionOptionsTest
customSslContextFactoryParams),
keystorePath, "dummypass",
"dummytruststore", "dummypass",
-
Collections.emptyList(), null, null, null, "JKS", false, false, enabled,
optional)
+
Collections.emptyList(), null, null, null, "JKS", "false", false, enabled,
optional)
.applyConfig(),
expected,
String.format("optional=%s
keystore=%s enabled=%s", optional, keystorePath, enabled));
@@ -126,7 +126,7 @@ public class EncryptionOptionsTest
{
return new ServerEncryptionOptionsTestCase(new
EncryptionOptions.ServerEncryptionOptions(new
ParameterizedClass("org.apache.cassandra.security.DefaultSslContextFactory",
new HashMap<>()), keystorePath,
"dummypass", keystorePath, "dummypass", "dummytruststore", "dummypass",
-
Collections.emptyList(), null, null, null, "JKS", false, false,
optional, internodeEncryption, false)
+
Collections.emptyList(), null, null, null, "JKS", "false",
false, optional, internodeEncryption, false)
.applyConfig(),
expected,
String.format("optional=%s
keystore=%s internode=%s", optional, keystorePath, internodeEncryption));
diff --git a/test/unit/org/apache/cassandra/db/virtual/SettingsTableTest.java
b/test/unit/org/apache/cassandra/db/virtual/SettingsTableTest.java
index d9aabcb054..e52ec022ed 100644
--- a/test/unit/org/apache/cassandra/db/virtual/SettingsTableTest.java
+++ b/test/unit/org/apache/cassandra/db/virtual/SettingsTableTest.java
@@ -37,6 +37,8 @@ import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.security.SSLFactory;
import org.yaml.snakeyaml.introspector.Property;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
+
public class SettingsTableTest extends CQLTester
{
private static final String KS_NAME = "vts";
@@ -201,7 +203,7 @@ public class SettingsTableTest extends CQLTester
// name doesn't match yaml
check(pre + "client_auth", "false");
- config.server_encryption_options =
config.server_encryption_options.withRequireClientAuth(true);
+ config.server_encryption_options =
config.server_encryption_options.withRequireClientAuth(REQUIRED);
check(pre + "client_auth", "true");
// name doesn't match yaml
diff --git a/test/unit/org/apache/cassandra/net/ConnectionTest.java
b/test/unit/org/apache/cassandra/net/ConnectionTest.java
index ff11f9dbb0..1f8c6f612d 100644
--- a/test/unit/org/apache/cassandra/net/ConnectionTest.java
+++ b/test/unit/org/apache/cassandra/net/ConnectionTest.java
@@ -71,6 +71,7 @@ import org.apache.cassandra.utils.FBUtilities;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.NOT_REQUIRED;
import static org.apache.cassandra.net.MessagingService.VERSION_40;
import static org.apache.cassandra.net.NoPayload.noPayload;
import static org.apache.cassandra.net.MessagingService.current_version;
@@ -182,7 +183,7 @@ public class ConnectionTest
.withKeyStorePassword("cassandra")
.withTrustStore("test/conf/cassandra_ssl_test.truststore")
.withTrustStorePassword("cassandra")
- .withRequireClientAuth(false)
+ .withRequireClientAuth(NOT_REQUIRED)
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA");
static final List<Function<Settings, Settings>> MODIFIERS =
ImmutableList.of(
diff --git a/test/unit/org/apache/cassandra/net/HandshakeTest.java
b/test/unit/org/apache/cassandra/net/HandshakeTest.java
index f2fa960566..ec7e26b322 100644
--- a/test/unit/org/apache/cassandra/net/HandshakeTest.java
+++ b/test/unit/org/apache/cassandra/net/HandshakeTest.java
@@ -49,6 +49,8 @@ import org.apache.cassandra.security.DefaultSslContextFactory;
import org.apache.cassandra.utils.concurrent.AsyncPromise;
import static org.apache.cassandra.net.ConnectionType.SMALL_MESSAGES;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.NOT_REQUIRED;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
import static org.apache.cassandra.net.MessagingService.current_version;
import static org.apache.cassandra.net.MessagingService.minimum_version;
import static org.apache.cassandra.net.OutboundConnectionInitiator.Result;
@@ -285,12 +287,12 @@ public class HandshakeTest
if (sslConnectionType == SslFallbackConnectionType.MTLS)
{
serverEncryptionOptions =
serverEncryptionOptions.withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all)
-
.withRequireClientAuth(true);
+
.withRequireClientAuth(REQUIRED);
}
else if (sslConnectionType == SslFallbackConnectionType.SSL)
{
serverEncryptionOptions =
serverEncryptionOptions.withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all)
-
.withRequireClientAuth(false);
+
.withRequireClientAuth(NOT_REQUIRED);
}
return serverEncryptionOptions;
}
diff --git
a/test/unit/org/apache/cassandra/security/DefaultSslContextFactoryTest.java
b/test/unit/org/apache/cassandra/security/DefaultSslContextFactoryTest.java
index c169b9e775..1b7db326e1 100644
--- a/test/unit/org/apache/cassandra/security/DefaultSslContextFactoryTest.java
+++ b/test/unit/org/apache/cassandra/security/DefaultSslContextFactoryTest.java
@@ -37,6 +37,9 @@ import org.apache.cassandra.distributed.shared.WithProperties;
import static
org.apache.cassandra.config.CassandraRelevantProperties.DISABLE_TCACTIVE_OPENSSL;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.NOT_REQUIRED;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
+
public class DefaultSslContextFactoryTest
{
private Map<String,Object> commonConfig = new HashMap<>();
@@ -46,7 +49,7 @@ public class DefaultSslContextFactoryTest
{
commonConfig.put("truststore",
"test/conf/cassandra_ssl_test.truststore");
commonConfig.put("truststore_password", "cassandra");
- commonConfig.put("require_client_auth", Boolean.FALSE);
+ commonConfig.put("require_client_auth", "false");
commonConfig.put("cipher_suites",
Arrays.asList("TLS_RSA_WITH_AES_128_CBC_SHA"));
}
@@ -71,9 +74,9 @@ public class DefaultSslContextFactoryTest
.withKeyStorePassword("cassandra")
.withOutboundKeystore("test/conf/cassandra_ssl_test_outbound.keystore")
.withOutboundKeystorePassword("cassandra")
-
.withRequireClientAuth(false)
+
.withRequireClientAuth(NOT_REQUIRED)
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA");
- SslContext sslContext = SSLFactory.getOrCreateSslContext(options,
true, ISslContextFactory.SocketType.CLIENT, "test");
+ SslContext sslContext = SSLFactory.getOrCreateSslContext(options,
REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
Assert.assertNotNull(sslContext);
if (OpenSsl.isAvailable())
Assert.assertTrue(sslContext instanceof OpenSslContext);
diff --git
a/test/unit/org/apache/cassandra/security/DummySslContextFactoryImpl.java
b/test/unit/org/apache/cassandra/security/DummySslContextFactoryImpl.java
index 3a14ff2937..ca4f4e86f0 100644
--- a/test/unit/org/apache/cassandra/security/DummySslContextFactoryImpl.java
+++ b/test/unit/org/apache/cassandra/security/DummySslContextFactoryImpl.java
@@ -25,6 +25,7 @@ import javax.net.ssl.SSLException;
import io.netty.handler.ssl.CipherSuiteFilter;
import io.netty.handler.ssl.SslContext;
+import org.apache.cassandra.config.EncryptionOptions;
/**
* TEST ONLY Class. DON'T use it for anything else.
@@ -43,7 +44,19 @@ public class DummySslContextFactoryImpl implements
ISslContextFactory
}
@Override
- public SslContext createNettySslContext(boolean verifyPeerCertificate,
SocketType socketType,
+ public SSLContext createJSSESslContext(EncryptionOptions.ClientAuth
clientAuth) throws SSLException
+ {
+ return null;
+ }
+
+ @Override
+ public SslContext createNettySslContext(boolean verifyPeerCertificate,
SocketType socketType, CipherSuiteFilter cipherFilter) throws SSLException
+ {
+ return null;
+ }
+
+ @Override
+ public SslContext createNettySslContext(EncryptionOptions.ClientAuth
clientAuth, SocketType socketType,
CipherSuiteFilter cipherFilter)
throws SSLException
{
return null;
diff --git
a/test/unit/org/apache/cassandra/security/FileBasedSslContextFactoryTest.java
b/test/unit/org/apache/cassandra/security/FileBasedSslContextFactoryTest.java
index 98e0342797..73fe80b304 100644
---
a/test/unit/org/apache/cassandra/security/FileBasedSslContextFactoryTest.java
+++
b/test/unit/org/apache/cassandra/security/FileBasedSslContextFactoryTest.java
@@ -36,6 +36,8 @@ import org.apache.cassandra.distributed.shared.WithProperties;
import static
org.apache.cassandra.config.CassandraRelevantProperties.CASSANDRA_CONFIG;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.NOT_REQUIRED;
+
public class FileBasedSslContextFactoryTest
{
private static final Logger logger =
LoggerFactory.getLogger(FileBasedSslContextFactoryTest.class);
@@ -65,7 +67,7 @@ public class FileBasedSslContextFactoryTest
new
HashMap<>()))
.withTrustStore("test/conf/cassandra_ssl_test.truststore")
.withTrustStorePassword("cassandra")
- .withRequireClientAuth(false)
+ .withRequireClientAuth(NOT_REQUIRED)
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA")
.withKeyStore("test/conf/cassandra_ssl_test.keystore")
.withKeyStorePassword("cassandra")
diff --git
a/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java
b/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java
index a1fb37c1b9..ca319de5fd 100644
--- a/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java
+++ b/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java
@@ -38,6 +38,8 @@ import org.apache.cassandra.config.ParameterizedClass;
import org.apache.cassandra.distributed.shared.WithProperties;
import static
org.apache.cassandra.config.CassandraRelevantProperties.DISABLE_TCACTIVE_OPENSSL;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.NOT_REQUIRED;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
import static
org.apache.cassandra.security.PEMBasedSslContextFactory.ConfigKey.ENCODED_CERTIFICATES;
import static
org.apache.cassandra.security.PEMBasedSslContextFactory.ConfigKey.ENCODED_KEY;
import static
org.apache.cassandra.security.PEMBasedSslContextFactory.ConfigKey.KEY_PASSWORD;
@@ -176,7 +178,7 @@ public class PEMBasedSslContextFactoryTest
public void setup()
{
commonConfig.put(ENCODED_CERTIFICATES.getKeyName(),
trusted_certificates);
- commonConfig.put("require_client_auth", Boolean.FALSE);
+ commonConfig.put("require_client_auth", "false");
commonConfig.put("cipher_suites",
Arrays.asList("TLS_RSA_WITH_AES_128_CBC_SHA"));
}
@@ -215,10 +217,10 @@ public class PEMBasedSslContextFactoryTest
EncryptionOptions options = new
EncryptionOptions().withTrustStore("test/conf/cassandra_ssl_test.truststore.pem")
.withKeyStore("test/conf/cassandra_ssl_test.keystore.pem")
.withKeyStorePassword("cassandra")
-
.withRequireClientAuth(false)
+
.withRequireClientAuth(NOT_REQUIRED)
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA")
.withSslContextFactory(sslContextFactory);
- SslContext sslContext = SSLFactory.getOrCreateSslContext(options,
true, ISslContextFactory.SocketType.SERVER, "test");
+ SslContext sslContext = SSLFactory.getOrCreateSslContext(options,
REQUIRED, ISslContextFactory.SocketType.SERVER, "test");
Assert.assertNotNull(sslContext);
if (OpenSsl.isAvailable())
Assert.assertTrue(sslContext instanceof OpenSslContext);
@@ -236,10 +238,10 @@ public class PEMBasedSslContextFactoryTest
.withKeyStorePassword("cassandra")
.withOutboundKeystore("test/conf/cassandra_ssl_test.keystore.pem")
.withOutboundKeystorePassword("cassandra")
-
.withRequireClientAuth(false)
+
.withRequireClientAuth(NOT_REQUIRED)
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA")
.withSslContextFactory(sslContextFactory);
- SslContext sslContext = SSLFactory.getOrCreateSslContext(options,
true, ISslContextFactory.SocketType.CLIENT, "test");
+ SslContext sslContext = SSLFactory.getOrCreateSslContext(options,
REQUIRED, 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 b6e0b8e4b5..a7c14dfcfb 100644
--- a/test/unit/org/apache/cassandra/security/SSLFactoryTest.java
+++ b/test/unit/org/apache/cassandra/security/SSLFactoryTest.java
@@ -51,6 +51,8 @@ import
org.apache.cassandra.config.EncryptionOptions.ServerEncryptionOptions;
import org.apache.cassandra.config.ParameterizedClass;
import org.apache.cassandra.io.util.File;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.NOT_REQUIRED;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -81,7 +83,7 @@ public class SSLFactoryTest
encryptionOptions = new ServerEncryptionOptions()
.withTrustStore("test/conf/cassandra_ssl_test.truststore")
.withTrustStorePassword("cassandra")
- .withRequireClientAuth(false)
+ .withRequireClientAuth(NOT_REQUIRED)
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA")
.withSslContextFactory(new
ParameterizedClass(TestFileBasedSSLContextFactory.class.getName(),
new
HashMap<>()));
@@ -118,8 +120,8 @@ public class SSLFactoryTest
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");
+ SslContext oldCtx = SSLFactory.getOrCreateSslContext(options,
REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
+ SslContext oldLegacyCtx =
SSLFactory.getOrCreateSslContext(legacyOptions, REQUIRED,
ISslContextFactory.SocketType.CLIENT, "test legacy");
File keystoreFile = new File(options.keystore);
SSLFactory.checkCertFilesForHotReloading();
@@ -127,8 +129,8 @@ public class SSLFactoryTest
keystoreFile.trySetLastModified(System.currentTimeMillis() +
15000);
SSLFactory.checkCertFilesForHotReloading();
- SslContext newCtx = SSLFactory.getOrCreateSslContext(options,
true, ISslContextFactory.SocketType.CLIENT, "test");
- SslContext newLegacyCtx =
SSLFactory.getOrCreateSslContext(legacyOptions, true,
ISslContextFactory.SocketType.CLIENT, "test legacy");
+ SslContext newCtx = SSLFactory.getOrCreateSslContext(options,
REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
+ SslContext newLegacyCtx =
SSLFactory.getOrCreateSslContext(legacyOptions, REQUIRED,
ISslContextFactory.SocketType.CLIENT, "test legacy");
Assert.assertNotSame(oldCtx, newCtx);
Assert.assertNotSame(oldLegacyCtx, newLegacyCtx);
@@ -151,7 +153,7 @@ public class SSLFactoryTest
.withOutboundKeystorePassword("dummyPassword");
// Server socket type should create a keystore with keystore &
keystore password
- final OpenSslServerContext context = (OpenSslServerContext)
SSLFactory.createNettySslContext(options, true,
ISslContextFactory.SocketType.SERVER);
+ final OpenSslServerContext context = (OpenSslServerContext)
SSLFactory.createNettySslContext(options, REQUIRED,
ISslContextFactory.SocketType.SERVER);
assertNotNull(context);
// Verify if right certificate is loaded into SslContext
@@ -168,7 +170,7 @@ public class SSLFactoryTest
.withKeyStorePassword("dummyPassword");
// Client socket type should create a keystore with outbound Keystore
& outbound password
- final OpenSslClientContext context = (OpenSslClientContext)
SSLFactory.createNettySslContext(options, true,
ISslContextFactory.SocketType.CLIENT);
+ final OpenSslClientContext context = (OpenSslClientContext)
SSLFactory.createNettySslContext(options, REQUIRED,
ISslContextFactory.SocketType.CLIENT);
assertNotNull(context);
// Verify if right certificate is loaded into SslContext
@@ -189,8 +191,8 @@ public class SSLFactoryTest
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");
+ SslContext oldCtx = SSLFactory.getOrCreateSslContext(options,
REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
+ SslContext oldLegacyCtx =
SSLFactory.getOrCreateSslContext(legacyOptions, REQUIRED,
ISslContextFactory.SocketType.CLIENT, "test legacy");
File keystoreFile = new File(options.keystore);
SSLFactory.checkCertFilesForHotReloading();
@@ -198,8 +200,8 @@ public class SSLFactoryTest
keystoreFile.trySetLastModified(System.currentTimeMillis() +
15000);
SSLFactory.checkCertFilesForHotReloading();
- SslContext newCtx = SSLFactory.getOrCreateSslContext(options,
true, ISslContextFactory.SocketType.CLIENT, "test");
- SslContext newLegacyCtx =
SSLFactory.getOrCreateSslContext(legacyOptions, true,
ISslContextFactory.SocketType.CLIENT, "test legacy");
+ SslContext newCtx = SSLFactory.getOrCreateSslContext(options,
REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
+ SslContext newLegacyCtx =
SSLFactory.getOrCreateSslContext(legacyOptions, REQUIRED,
ISslContextFactory.SocketType.CLIENT, "test legacy");
Assert.assertNotSame(oldCtx, newCtx);
Assert.assertNotSame(oldLegacyCtx, newLegacyCtx);
@@ -221,7 +223,7 @@ public class SSLFactoryTest
.withKeyStorePassword("bad password")
.withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all);
-
SSLFactory.validateSslContext("testSslFactorySslInit_BadPassword_ThrowsException",
options, false, true);
+
SSLFactory.validateSslContext("testSslFactorySslInit_BadPassword_ThrowsException",
options, NOT_REQUIRED, true);
}
@Test
@@ -239,14 +241,14 @@ public class SSLFactoryTest
SSLFactory.initHotReloading(options, options, true); //
deliberately not initializing with legacyOptions to match
InboundSockets.addBindings
- SslContext oldCtx = SSLFactory.getOrCreateSslContext(options,
true, ISslContextFactory.SocketType.CLIENT, "test");
- SslContext oldLegacyCtx =
SSLFactory.getOrCreateSslContext(legacyOptions, true,
ISslContextFactory.SocketType.CLIENT, "test legacy");
+ SslContext oldCtx = SSLFactory.getOrCreateSslContext(options,
REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
+ SslContext oldLegacyCtx =
SSLFactory.getOrCreateSslContext(legacyOptions, REQUIRED,
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(legacyOptions, true,
ISslContextFactory.SocketType.CLIENT, "test legacy");
+ SslContext newCtx = SSLFactory.getOrCreateSslContext(options,
REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
+ SslContext newLegacyCtx =
SSLFactory.getOrCreateSslContext(legacyOptions, REQUIRED,
ISslContextFactory.SocketType.CLIENT, "test legacy");
Assert.assertSame(oldCtx, newCtx);
Assert.assertSame(oldLegacyCtx, newLegacyCtx);
@@ -270,14 +272,14 @@ public class SSLFactoryTest
SSLFactory.initHotReloading(options, options, true);
- SslContext oldCtx = SSLFactory.getOrCreateSslContext(options,
true, ISslContextFactory.SocketType.CLIENT, "test");
+ SslContext oldCtx = SSLFactory.getOrCreateSslContext(options,
REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
SSLFactory.checkCertFilesForHotReloading();
testKeystoreFile.trySetLastModified(System.currentTimeMillis() +
15000);
FileUtils.forceDelete(testKeystoreFile.toJavaIOFile());
SSLFactory.checkCertFilesForHotReloading();
- SslContext newCtx = SSLFactory.getOrCreateSslContext(options,
true, ISslContextFactory.SocketType.CLIENT, "test");
+ SslContext newCtx = SSLFactory.getOrCreateSslContext(options,
REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
Assert.assertSame(oldCtx, newCtx);
}
@@ -298,7 +300,7 @@ public class SSLFactoryTest
ServerEncryptionOptions options = addKeystoreOptions(encryptionOptions)
.withCipherSuites("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
- SslContext ctx1 = SSLFactory.getOrCreateSslContext(options, true,
+ SslContext ctx1 = SSLFactory.getOrCreateSslContext(options, REQUIRED,
ISslContextFactory.SocketType.SERVER, "test");
Assert.assertTrue(ctx1.isServer());
@@ -306,7 +308,7 @@ public class SSLFactoryTest
options =
options.withCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
- SslContext ctx2 = SSLFactory.getOrCreateSslContext(options, true,
+ SslContext ctx2 = SSLFactory.getOrCreateSslContext(options, REQUIRED,
ISslContextFactory.SocketType.CLIENT, "test");
Assert.assertTrue(ctx2.isClient());
@@ -323,7 +325,7 @@ public class SSLFactoryTest
new EncryptionOptions()
.withSslContextFactory(new
ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters1))
.withProtocol("TLSv1.1")
- .withRequireClientAuth(true)
+ .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false);
SSLFactory.CacheKey cacheKey1 = new
SSLFactory.CacheKey(encryptionOptions1, ISslContextFactory.SocketType.SERVER,
"test"
@@ -336,7 +338,7 @@ public class SSLFactoryTest
new EncryptionOptions()
.withSslContextFactory(new
ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters2))
.withProtocol("TLSv1.1")
- .withRequireClientAuth(true)
+ .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false);
SSLFactory.CacheKey cacheKey2 = new
SSLFactory.CacheKey(encryptionOptions2, ISslContextFactory.SocketType.SERVER,
"test"
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 f938cd9887..3d72828daf 100644
--- a/tools/stress/src/org/apache/cassandra/stress/util/JavaDriverClient.java
+++ b/tools/stress/src/org/apache/cassandra/stress/util/JavaDriverClient.java
@@ -41,6 +41,8 @@ import org.apache.cassandra.config.EncryptionOptions;
import org.apache.cassandra.security.SSLFactory;
import org.apache.cassandra.stress.settings.StressSettings;
+import static
org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
+
public class JavaDriverClient
{
@@ -161,7 +163,7 @@ public class JavaDriverClient
if (encryptionOptions.getEnabled())
{
SSLContext sslContext;
- sslContext = SSLFactory.createSSLContext(encryptionOptions, true);
+ sslContext = SSLFactory.createSSLContext(encryptionOptions,
REQUIRED);
// Temporarily override newSSLEngine to set accepted protocols
until it is added to
// RemoteEndpointAwareJdkSSLOptions. See CASSANDRA-13325 and
CASSANDRA-16362.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]