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
This commit is contained in:
Jyothsna Konisa 2023-08-30 11:58:21 -07:00 committed by Jon Meredith
parent 447d2bcee7
commit bfcb21fbeb
34 changed files with 528 additions and 126 deletions

View File

@ -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

View File

@ -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

View File

@ -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"));
}

View File

@ -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"));
}

View File

@ -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()

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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)

View File

@ -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();
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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.
*

View File

@ -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

View File

@ -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)
{

View File

@ -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))

View File

@ -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;

View File

@ -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));

View File

@ -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();

View File

@ -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 -> {

View File

@ -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());

View File

@ -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();
}
}

View File

@ -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

View File

@ -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);

View File

@ -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));

View File

@ -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

View File

@ -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(

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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")

View File

@ -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);

View File

@ -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"

View File

@ -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.