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 # # Must be an instance of org.apache.cassandra.security.ISslContextFactory
# class_name: org.apache.cassandra.security.DefaultSslContextFactory # class_name: org.apache.cassandra.security.DefaultSslContextFactory
# Verify client certificates # 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_client_auth: false
# require_endpoint_verification: false # require_endpoint_verification: false
# Set trustore and truststore_password if require_client_auth is true # 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 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. 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 == Configuring mTLS authenticator for Internode connections
Internode authenticator trusts certificates whose identities are present in 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 * 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. * 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")); 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. * done to avoid having a dependency on env in the unit test.
*/ */
commonConfig.put("MY_TRUSTSTORE_PASSWORD", "cassandra"); 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")); 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.schema.SchemaConstants;
import org.apache.cassandra.utils.NoSpamLogger; 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 * 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 * 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), certificateValidator = ParameterizedClass.newInstance(new ParameterizedClass(certificateValidatorClassName),
Arrays.asList("", AuthConfig.class.getPackage().getName())); Arrays.asList("", AuthConfig.class.getPackage().getName()));
checkMtlsConfigurationIsValid(DatabaseDescriptor.getRawConfig());
AuthCacheService.instance.register(identityCache); AuthCacheService.instance.register(identityCache);
} }
@ -98,7 +99,14 @@ public class MutualTlsAuthenticator implements IAuthenticator
@Override @Override
public void validateConfiguration() throws ConfigurationException 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 @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> static class IdentityCache extends AuthCache<String, String>
{ {
IdentityCache() IdentityCache()

View File

@ -46,6 +46,8 @@ import org.apache.cassandra.exceptions.AuthenticationException;
import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.utils.NoSpamLogger; 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 * 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 * 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), certificateValidator = ParameterizedClass.newInstance(new ParameterizedClass(certificateValidatorClassName),
Arrays.asList("", AuthConfig.class.getPackage().getName())); Arrays.asList("", AuthConfig.class.getPackage().getName()));
Config config = DatabaseDescriptor.getRawConfig(); Config config = DatabaseDescriptor.getRawConfig();
checkInternodeMtlsConfigurationIsValid(config);
if (parameters.containsKey(TRUSTED_PEER_IDENTITIES)) if (parameters.containsKey(TRUSTED_PEER_IDENTITIES))
{ {
@ -146,6 +147,15 @@ public class MutualTlsInternodeAuthenticator implements IInternodeAuthenticator
@Override @Override
public void validateConfiguration() throws ConfigurationException 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; 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.security.cert.Certificate;
import java.util.Map; 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 authenticator can be used in optional mTLS mode, If the client doesn't make an mTLS connection
* this fallbacks to password authentication. * this fallbacks to password authentication.
@ -50,4 +55,15 @@ public class MutualTlsWithPasswordFallbackAuthenticator extends PasswordAuthenti
} }
return mutualTlsAuthenticator.newSaslNegotiator(clientAddress, certificates); 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.BYTES_PER_SECOND;
import static org.apache.cassandra.config.DataRateSpec.DataRateUnit.MEBIBYTES_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.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.ALL;
import static org.apache.cassandra.db.ConsistencyLevel.EACH_QUORUM; import static org.apache.cassandra.db.ConsistencyLevel.EACH_QUORUM;
import static org.apache.cassandra.db.ConsistencyLevel.LOCAL_QUORUM; import static org.apache.cassandra.db.ConsistencyLevel.LOCAL_QUORUM;
@ -1241,8 +1242,8 @@ public class DatabaseDescriptor
try try
{ {
SSLFactory.validateSslContext("Internode messaging", conf.server_encryption_options, true, true); SSLFactory.validateSslContext("Internode messaging", conf.server_encryption_options, REQUIRED, true);
SSLFactory.validateSslContext("Native transport", conf.client_encryption_options, conf.client_encryption_options.require_client_auth, 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); SSLFactory.initHotReloading(conf.server_encryption_options, conf.client_encryption_options, false);
} }
catch (IOException e) catch (IOException e)

View File

@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableList;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.locator.IEndpointSnitch; import org.apache.cassandra.locator.IEndpointSnitch;
import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.security.DisableSslContextFactory; 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 * 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 * 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; protected List<String> accepted_protocols;
public final String algorithm; public final String algorithm;
public final String store_type; public final String store_type;
public final boolean require_client_auth; public final String require_client_auth;
public final boolean require_endpoint_verification; public final boolean require_endpoint_verification;
// ServerEncryptionOptions does not use the enabled flag at all instead using the existing // 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 // internode_encryption option. So we force this private and expose through isEnabled
@ -159,7 +196,7 @@ public class EncryptionOptions
accepted_protocols = null; accepted_protocols = null;
algorithm = null; algorithm = null;
store_type = "JKS"; store_type = "JKS";
require_client_auth = false; require_client_auth = "false";
require_endpoint_verification = false; require_endpoint_verification = false;
enabled = null; enabled = null;
optional = null; optional = null;
@ -168,7 +205,7 @@ public class EncryptionOptions
public EncryptionOptions(ParameterizedClass ssl_context_factory, String keystore, String keystore_password, public EncryptionOptions(ParameterizedClass ssl_context_factory, String keystore, String keystore_password,
String truststore, String truststore_password, List<String> cipher_suites, String truststore, String truststore_password, List<String> cipher_suites,
String protocol, List<String> accepted_protocols, String algorithm, String store_type, 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) Boolean optional)
{ {
this.ssl_context_factory = ssl_context_factory; this.ssl_context_factory = ssl_context_factory;
@ -402,6 +439,11 @@ public class EncryptionOptions
return sslContextFactoryInstance == null ? null : sslContextFactoryInstance.getAcceptedProtocols(); 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() public String[] acceptedProtocolsArray()
{ {
List<String> ap = getAcceptedProtocols(); List<String> ap = getAcceptedProtocols();
@ -520,11 +562,11 @@ public class EncryptionOptions
optional).applyConfig(); 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, return new EncryptionOptions(ssl_context_factory, keystore, keystore_password, truststore,
truststore_password, cipher_suites, protocol, accepted_protocols, algorithm, 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(); optional).applyConfig();
} }
@ -567,7 +609,7 @@ public class EncryptionOptions
EncryptionOptions opt = (EncryptionOptions)o; EncryptionOptions opt = (EncryptionOptions)o;
return enabled == opt.enabled && return enabled == opt.enabled &&
optional == opt.optional && 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 && require_endpoint_verification == opt.require_endpoint_verification &&
Objects.equals(keystore, opt.keystore) && Objects.equals(keystore, opt.keystore) &&
Objects.equals(keystore_password, opt.keystore_password) && Objects.equals(keystore_password, opt.keystore_password) &&
@ -600,7 +642,7 @@ public class EncryptionOptions
result += 31 * (enabled == null ? 0 : Boolean.hashCode(enabled)); result += 31 * (enabled == null ? 0 : Boolean.hashCode(enabled));
result += 31 * (optional == null ? 0 : Boolean.hashCode(optional)); result += 31 * (optional == null ? 0 : Boolean.hashCode(optional));
result += 31 * (cipher_suites == null ? 0 : cipher_suites.hashCode()); 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 * Boolean.hashCode(require_endpoint_verification);
result += 31 * (ssl_context_factory == null ? 0 : ssl_context_factory.hashCode()); result += 31 * (ssl_context_factory == null ? 0 : ssl_context_factory.hashCode());
return result; return result;
@ -632,7 +674,7 @@ public class EncryptionOptions
String keystore_password,String outbound_keystore, String keystore_password,String outbound_keystore,
String outbound_keystore_password, String truststore, String truststore_password, String outbound_keystore_password, String truststore, String truststore_password,
List<String> cipher_suites, String protocol, List<String> accepted_protocols, 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, boolean require_endpoint_verification, Boolean optional,
InternodeEncryption internode_encryption, boolean legacy_ssl_storage_port_enabled) 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"); 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." 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" + " 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(); 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, return new ServerEncryptionOptions(ssl_context_factory, keystore, keystore_password,
outbound_keystore, outbound_keystore_password, truststore, outbound_keystore, outbound_keystore_password, truststore,
truststore_password, cipher_suites, protocol, accepted_protocols, 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, require_endpoint_verification, optional, internode_encryption,
legacy_ssl_storage_port_enabled).applyConfigInternal(); 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 java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.apache.cassandra.auth.IInternodeAuthenticator.InternodeConnectionDirection.INBOUND; import static org.apache.cassandra.auth.IInternodeAuthenticator.InternodeConnectionDirection.INBOUND;
import static org.apache.cassandra.concurrent.ExecutorFactory.Global.executorFactory; 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.DISCARD_HANDLER_NAME;
import static org.apache.cassandra.net.InternodeConnectionUtils.SSL_FACTORY_CONTEXT_DESCRIPTION; import static org.apache.cassandra.net.InternodeConnectionUtils.SSL_FACTORY_CONTEXT_DESCRIPTION;
import static org.apache.cassandra.net.InternodeConnectionUtils.SSL_HANDLER_NAME; 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 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, SslContext sslContext = SSLFactory.getOrCreateSslContext(encryptionOptions, verifyPeerCertificate,
ISslContextFactory.SocketType.SERVER, ISslContextFactory.SocketType.SERVER,
SSL_FACTORY_CONTEXT_DESCRIPTION); 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.Future; //checkstyle: permit this import
import io.netty.util.concurrent.Promise; //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.apache.cassandra.utils.concurrent.AsyncPromise;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -65,6 +66,9 @@ import org.apache.cassandra.utils.memory.BufferPools;
import static java.util.concurrent.TimeUnit.*; import static java.util.concurrent.TimeUnit.*;
import static org.apache.cassandra.auth.IInternodeAuthenticator.InternodeConnectionDirection.OUTBOUND; import static org.apache.cassandra.auth.IInternodeAuthenticator.InternodeConnectionDirection.OUTBOUND;
import static org.apache.cassandra.auth.IInternodeAuthenticator.InternodeConnectionDirection.OUTBOUND_PRECONNECT; 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.DISCARD_HANDLER_NAME;
import static org.apache.cassandra.net.InternodeConnectionUtils.SSL_FACTORY_CONTEXT_DESCRIPTION; import static org.apache.cassandra.net.InternodeConnectionUtils.SSL_FACTORY_CONTEXT_DESCRIPTION;
import static org.apache.cassandra.net.InternodeConnectionUtils.SSL_HANDLER_NAME; 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 private SslContext getSslContext(SslFallbackConnectionType connectionType) throws IOException
{ {
boolean requireClientAuth = false; EncryptionOptions.ClientAuth requireClientAuth = NOT_REQUIRED;
if (connectionType == SslFallbackConnectionType.MTLS || connectionType == SslFallbackConnectionType.SSL) if (connectionType == SslFallbackConnectionType.MTLS )
{ {
requireClientAuth = true; requireClientAuth = REQUIRED;
}
else if(connectionType == SslFallbackConnectionType.SSL)
{
requireClientAuth = OPTIONAL;
} }
else if (connectionType == SslFallbackConnectionType.SERVER_CONFIG) 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); 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.SslContext;
import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider; 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; 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 List<String> accepted_protocols;
protected final String algorithm; protected final String algorithm;
protected final String store_type; protected final String store_type;
protected final boolean require_client_auth; protected final EncryptionOptions.ClientAuth clientAuth;
protected final boolean require_endpoint_verification; protected final boolean require_endpoint_verification;
/* /*
ServerEncryptionOptions does not use the enabled flag at all instead using the existing 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; accepted_protocols = null;
algorithm = null; algorithm = null;
store_type = "JKS"; store_type = "JKS";
require_client_auth = false; clientAuth = NOT_REQUIRED;
require_endpoint_verification = false; require_endpoint_verification = false;
enabled = null; enabled = null;
optional = null; optional = null;
@ -101,7 +105,7 @@ abstract public class AbstractSslContextFactory implements ISslContextFactory
accepted_protocols = getStringList("accepted_protocols"); accepted_protocols = getStringList("accepted_protocols");
algorithm = getString("algorithm"); algorithm = getString("algorithm");
store_type = getString("store_type", "JKS"); 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); require_endpoint_verification = getBoolean("require_endpoint_verification", false);
enabled = getBoolean("enabled"); enabled = getBoolean("enabled");
optional = getBoolean("optional"); optional = getBoolean("optional");
@ -149,9 +153,15 @@ abstract public class AbstractSslContextFactory implements ISslContextFactory
@Override @Override
public SSLContext createJSSESslContext(boolean verifyPeerCertificate) throws SSLException 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; TrustManager[] trustManagers = null;
if (verifyPeerCertificate) if (clientAuth != NOT_REQUIRED)
trustManagers = buildTrustManagerFactory().getTrustManagers(); trustManagers = buildTrustManagerFactory().getTrustManagers();
KeyManagerFactory kmf = buildKeyManagerFactory(); KeyManagerFactory kmf = buildKeyManagerFactory();
@ -171,6 +181,13 @@ abstract public class AbstractSslContextFactory implements ISslContextFactory
@Override @Override
public SslContext createNettySslContext(boolean verifyPeerCertificate, SocketType socketType, public SslContext createNettySslContext(boolean verifyPeerCertificate, SocketType socketType,
CipherSuiteFilter cipherFilter) throws SSLException 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, 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) if (socketType == SocketType.SERVER)
{ {
KeyManagerFactory kmf = buildKeyManagerFactory(); KeyManagerFactory kmf = buildKeyManagerFactory();
builder = SslContextBuilder.forServer(kmf).clientAuth(this.require_client_auth ? ClientAuth.REQUIRE : builder = SslContextBuilder.forServer(kmf).clientAuth(toNettyClientAuth(this.clientAuth));
ClientAuth.NONE);
} }
else else
{ {
@ -200,7 +216,7 @@ abstract public class AbstractSslContextFactory implements ISslContextFactory
if (cipher_suites != null && !cipher_suites.isEmpty()) if (cipher_suites != null && !cipher_suites.isEmpty())
builder.ciphers(cipher_suites, cipherFilter); builder.ciphers(cipher_suites, cipherFilter);
if (verifyPeerCertificate) if (clientAuth != NOT_REQUIRED)
builder.trustManager(buildTrustManagerFactory()); builder.trustManager(buildTrustManagerFactory());
return builder.build(); return builder.build();
@ -274,4 +290,19 @@ abstract public class AbstractSslContextFactory implements ISslContextFactory
* @throws SSLException * @throws SSLException
*/ */
abstract protected KeyManagerFactory buildOutboundKeyManagerFactory() 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.CipherSuiteFilter;
import io.netty.handler.ssl.SslContext; 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 * 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 * @param verifyPeerCertificate {@code true} if SSL peer's certificate needs to be verified; {@code false} otherwise
* @return JSSE's {@link SSLContext} * @return JSSE's {@link SSLContext}
* @throws SSLException in case the Ssl Context creation fails for some reason * @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; 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. * Creates Netty's SslContext object.
* *
@ -69,10 +94,38 @@ public interface ISslContextFactory
* {@link io.netty.handler.ssl.SslContextBuilder#ciphers(Iterable, CipherSuiteFilter)} * {@link io.netty.handler.ssl.SslContextBuilder#ciphers(Iterable, CipherSuiteFilter)}
* @return Netty's {@link SslContext} * @return Netty's {@link SslContext}
* @throws SSLException in case the Ssl Context creation fails for some reason * @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, SslContext createNettySslContext(boolean verifyPeerCertificate, SocketType socketType,
CipherSuiteFilter cipherFilter) throws SSLException; 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. * 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.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 * 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. * 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}. * 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 * 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, SocketType socketType,
String contextDescription) throws IOException String contextDescription) throws IOException
{ {
@ -141,7 +143,7 @@ public final class SSLFactory
if (sslContext != null) if (sslContext != null)
return sslContext; return sslContext;
sslContext = createNettySslContext(options, verifyPeerCertificate, socketType); sslContext = createNettySslContext(options, clientAuth, socketType);
SslContext previous = cachedSslContexts.putIfAbsent(key, sslContext); SslContext previous = cachedSslContexts.putIfAbsent(key, sslContext);
if (previous == null) if (previous == null)
@ -154,20 +156,20 @@ public final class SSLFactory
/** /**
* Create a Netty {@link SslContext} * Create a Netty {@link SslContext}
*/ */
static SslContext createNettySslContext(EncryptionOptions options, boolean verifyPeerCertificate, static SslContext createNettySslContext(EncryptionOptions options, EncryptionOptions.ClientAuth clientAuth,
SocketType socketType) throws IOException SocketType socketType) throws IOException
{ {
return createNettySslContext(options, verifyPeerCertificate, socketType, return createNettySslContext(options, clientAuth, socketType,
LoggingCipherSuiteFilter.QUIET_FILTER); LoggingCipherSuiteFilter.QUIET_FILTER);
} }
/** /**
* Create a Netty {@link SslContext} with a supplied cipherFilter * 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 SocketType socketType, CipherSuiteFilter cipherFilter) throws IOException
{ {
return options.sslContextFactoryInstance.createNettySslContext(verifyPeerCertificate, socketType, return options.sslContextFactoryInstance.createNettySslContext(clientAuth, socketType,
cipherFilter); cipherFilter);
} }
@ -206,7 +208,7 @@ public final class SSLFactory
try try
{ {
validateSslContext(key.contextDescription, opts, 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 " + logger.info("SSL certificates have been updated for {}. Resetting the ssl contexts for new " +
"connections.", key.contextDescription); "connections.", key.contextDescription);
clearSslContextCache(key.encryptionOptions, keysToCheck); clearSslContextCache(key.encryptionOptions, keysToCheck);
@ -353,7 +355,7 @@ public final class SSLFactory
return !string.equals("SSLv2Hello"); 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) if (options != null && options.tlsEncryptionPolicy() != EncryptionOptions.TlsEncryptionPolicy.UNENCRYPTED)
{ {
@ -361,7 +363,7 @@ public final class SSLFactory
{ {
CipherSuiteFilter loggingCipherSuiteFilter = logProtocolAndCiphers ? new LoggingCipherSuiteFilter(contextDescription) CipherSuiteFilter loggingCipherSuiteFilter = logProtocolAndCiphers ? new LoggingCipherSuiteFilter(contextDescription)
: LoggingCipherSuiteFilter.QUIET_FILTER; : LoggingCipherSuiteFilter.QUIET_FILTER;
SslContext serverSslContext = createNettySslContext(options, verifyPeerCertificate, SocketType.SERVER, loggingCipherSuiteFilter); SslContext serverSslContext = createNettySslContext(options, clientAuth, SocketType.SERVER, loggingCipherSuiteFilter);
try try
{ {
SSLEngine engine = serverSslContext.newEngine(ByteBufAllocator.DEFAULT); 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 // 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); ReferenceCountUtil.release(clientSslContext);
} }
catch (Exception e) catch (Exception e)
@ -421,8 +423,8 @@ public final class SSLFactory
*/ */
public static void validateSslCerts(EncryptionOptions.ServerEncryptionOptions serverOpts, EncryptionOptions clientOpts) throws IOException public static void validateSslCerts(EncryptionOptions.ServerEncryptionOptions serverOpts, EncryptionOptions clientOpts) throws IOException
{ {
validateSslContext("server_encryption_options", serverOpts, true, false); validateSslContext("server_encryption_options", serverOpts, REQUIRED, false);
validateSslContext("client_encryption_options", clientOpts, clientOpts.require_client_auth, false); validateSslContext("client_encryption_options", clientOpts, clientOpts.getClientAuth(), false);
} }
static class CacheKey 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.NativeSSTableLoaderClient;
import org.apache.cassandra.utils.OutputHandler; import org.apache.cassandra.utils.OutputHandler;
import static org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
import static org.apache.cassandra.utils.Clock.Global.nanoTime; import static org.apache.cassandra.utils.Clock.Global.nanoTime;
public class BulkLoader public class BulkLoader
@ -264,7 +265,7 @@ public class BulkLoader
SSLContext sslContext; SSLContext sslContext;
try try
{ {
sslContext = SSLFactory.createSSLContext(clientEncryptionOptions, true); sslContext = SSLFactory.createSSLContext(clientEncryptionOptions, REQUIRED);
} }
catch (IOException e) catch (IOException e)
{ {

View File

@ -52,6 +52,7 @@ import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.tools.BulkLoader.CmdLineOptions; import org.apache.cassandra.tools.BulkLoader.CmdLineOptions;
import static org.apache.cassandra.config.DataRateSpec.DataRateUnit.MEBIBYTES_PER_SECOND; import static org.apache.cassandra.config.DataRateSpec.DataRateUnit.MEBIBYTES_PER_SECOND;
import static org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
public class LoaderOptions public class LoaderOptions
{ {
@ -648,7 +649,7 @@ public class LoaderOptions
{ {
// if a keystore was provided, lets assume we'll need to use // if a keystore was provided, lets assume we'll need to use
clientEncOptions = clientEncOptions.withKeyStore(cmd.getOptionValue(SSL_KEYSTORE)) clientEncOptions = clientEncOptions.withKeyStore(cmd.getOptionValue(SSL_KEYSTORE))
.withRequireClientAuth(true); .withRequireClientAuth(REQUIRED);
} }
if (cmd.hasOption(SSL_KEYSTORE_PW)) 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"); logger.debug("Enabling optionally encrypted CQL connections between client and server");
return channel -> { return channel -> {
SslContext sslContext = SSLFactory.getOrCreateSslContext(encryptionOptions, SslContext sslContext = SSLFactory.getOrCreateSslContext(encryptionOptions,
encryptionOptions.require_client_auth, encryptionOptions.getClientAuth(),
ISslContextFactory.SocketType.SERVER, ISslContextFactory.SocketType.SERVER,
SSL_FACTORY_CONTEXT_DESCRIPTION); SSL_FACTORY_CONTEXT_DESCRIPTION);
@ -204,7 +204,7 @@ public class PipelineConfigurator
logger.debug("Enabling encrypted CQL connections between client and server"); logger.debug("Enabling encrypted CQL connections between client and server");
return channel -> { return channel -> {
SslContext sslContext = SSLFactory.getOrCreateSslContext(encryptionOptions, SslContext sslContext = SSLFactory.getOrCreateSslContext(encryptionOptions,
encryptionOptions.require_client_auth, encryptionOptions.getClientAuth(),
ISslContextFactory.SocketType.SERVER, ISslContextFactory.SocketType.SERVER,
SSL_FACTORY_CONTEXT_DESCRIPTION); SSL_FACTORY_CONTEXT_DESCRIPTION);
InetSocketAddress peer = encryptionOptions.require_endpoint_verification ? (InetSocketAddress) channel.remoteAddress() : null; 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 protected void initChannel(Channel channel) throws Exception
{ {
super.initChannel(channel); 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); ISslContextFactory.SocketType.CLIENT, SSL_FACTORY_CONTEXT_DESCRIPTION);
InetSocketAddress peer = encryptionOptions.require_endpoint_verification ? new InetSocketAddress(host, port) : null; InetSocketAddress peer = encryptionOptions.require_endpoint_verification ? new InetSocketAddress(host, port) : null;
channel.pipeline().addFirst("ssl", newSslHandler(channel, sslContext, peer)); 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 org.apache.cassandra.security.SSLFactory;
import static java.util.concurrent.TimeUnit.SECONDS; 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.CONNECTING;
import static org.apache.cassandra.distributed.test.AbstractEncryptionOptionsImpl.ConnectResult.UNINITIALIZED; import static org.apache.cassandra.distributed.test.AbstractEncryptionOptionsImpl.ConnectResult.UNINITIALIZED;
import static org.apache.cassandra.utils.concurrent.Condition.newOneTimeCondition; import static org.apache.cassandra.utils.concurrent.Condition.newOneTimeCondition;
@ -199,8 +200,8 @@ public class AbstractEncryptionOptionsImpl extends TestBaseImpl
setProtocolAndCipher(null, null); setProtocolAndCipher(null, null);
SslContext sslContext = SSLFactory.getOrCreateSslContext( SslContext sslContext = SSLFactory.getOrCreateSslContext(
encryptionOptions.withAcceptedProtocols(acceptedProtocols).withCipherSuites(cipherSuites), encryptionOptions.withAcceptedProtocols(acceptedProtocols).withCipherSuites(cipherSuites),
true, ISslContextFactory.SocketType.CLIENT, "test"); REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
EventLoopGroup workerGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap b = new Bootstrap(); 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.Cluster;
import org.apache.cassandra.distributed.api.Feature; import org.apache.cassandra.distributed.api.Feature;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOptionsImpl public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOptionsImpl
{ {
@Rule @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. * Tests that the negotiated protocol is the highest common protocol between the client and server.
* <p> * <p>
* Note: This test uses TLSV1.1, which is disabled by default in JDK 8 and higher. If the test fails with * 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 * 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. * 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://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"> * @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); 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 private void testEndpointVerification(boolean requireEndpointVerification, boolean ipInSAN) throws Throwable
{ {
try (Cluster cluster = builder().withNodes(1).withConfig(c -> { 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.SchemaLoader;
import org.apache.cassandra.config.Config; import org.apache.cassandra.config.Config;
import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.EncryptionOptions;
import org.apache.cassandra.exceptions.AuthenticationException; import org.apache.cassandra.exceptions.AuthenticationException;
import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.net.MessagingService; 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.getMockInetAddress;
import static org.apache.cassandra.auth.AuthTestUtils.initializeIdentityRolesTable; import static org.apache.cassandra.auth.AuthTestUtils.initializeIdentityRolesTable;
import static org.apache.cassandra.auth.AuthTestUtils.loadCertificateChain; 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.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@ -78,7 +80,7 @@ public class MutualTlsAuthenticatorTest
((CassandraRoleManager)DatabaseDescriptor.getRoleManager()).loadIdentityStatement(); ((CassandraRoleManager)DatabaseDescriptor.getRoleManager()).loadIdentityStatement();
final Config config = DatabaseDescriptor.getRawConfig(); final Config config = DatabaseDescriptor.getRawConfig();
config.client_encryption_options = config.client_encryption_options.withEnabled(true) config.client_encryption_options = config.client_encryption_options.withEnabled(true)
.withRequireClientAuth(true); .withRequireClientAuth(REQUIRED);
} }
@After @After
@ -172,6 +174,22 @@ public class MutualTlsAuthenticatorTest
assertEquals("readonly_user", urnCache.get(identity2)); 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() MutualTlsAuthenticator createAndInitializeMtlsAuthenticator()
{ {
Map<String, String> parameters = Collections.singletonMap("validator_class_name", getValidatorClass()); 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.SchemaLoader;
import org.apache.cassandra.config.Config; import org.apache.cassandra.config.Config;
import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.EncryptionOptions;
import static org.apache.cassandra.auth.AuthTestUtils.getMockInetAddress; import static org.apache.cassandra.auth.AuthTestUtils.getMockInetAddress;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -50,7 +51,7 @@ public class MutualTlsWithPasswordFallbackAuthenticatorTest
SchemaLoader.loadSchema(); SchemaLoader.loadSchema();
Config config = DatabaseDescriptor.getRawConfig(); Config config = DatabaseDescriptor.getRawConfig();
config.client_encryption_options = config.client_encryption_options.withEnabled(true) 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"); Map<String, String> parameters = Collections.singletonMap("validator_class_name", "org.apache.cassandra.auth.SpiffeCertificateValidator");
fallbackAuthenticator = new MutualTlsWithPasswordFallbackAuthenticator(parameters); fallbackAuthenticator = new MutualTlsWithPasswordFallbackAuthenticator(parameters);
fallbackAuthenticator.setup(); fallbackAuthenticator.setup();
@ -88,4 +89,11 @@ public class MutualTlsWithPasswordFallbackAuthenticatorTest
IAuthenticator.SaslNegotiator mutualtlsAuthenticator = fallbackAuthenticator.newSaslNegotiator(getMockInetAddress(), clientCertificatesCorp); IAuthenticator.SaslNegotiator mutualtlsAuthenticator = fallbackAuthenticator.newSaslNegotiator(getMockInetAddress(), clientCertificatesCorp);
assertTrue(mutualtlsAuthenticator instanceof MutualTlsAuthenticator.CertificateNegotiator); 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("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 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("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(); .build();
/** /**
@ -146,7 +147,7 @@ public class ConfigCompatibilityTest
public void diff_5_0() throws IOException public void diff_5_0() throws IOException
{ {
diff(TEST_DIR + "/version=5.0-alpha1.yml", ImmutableSet.<String>builder() 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 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.DefaultSslContextFactory;
import org.apache.cassandra.security.DummySslContextFactoryImpl; 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.assertEquals;
import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotEquals;
@ -46,7 +48,7 @@ public class EncryptionOptionsEqualityTest
.withOutboundKeystore("test/conf/cassandra_outbound.keystore") .withOutboundKeystore("test/conf/cassandra_outbound.keystore")
.withOutboundKeystorePassword("cassandra") .withOutboundKeystorePassword("cassandra")
.withProtocol("TLSv1.1") .withProtocol("TLSv1.1")
.withRequireClientAuth(true) .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false); .withRequireEndpointVerification(false);
} }
@ -60,7 +62,7 @@ public class EncryptionOptionsEqualityTest
.withTrustStore("test/conf/cassandra_ssl_test.truststore") .withTrustStore("test/conf/cassandra_ssl_test.truststore")
.withTrustStorePassword("cassandra") .withTrustStorePassword("cassandra")
.withProtocol("TLSv1.1") .withProtocol("TLSv1.1")
.withRequireClientAuth(true) .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false); .withRequireEndpointVerification(false);
EncryptionOptions encryptionOptions2 = EncryptionOptions encryptionOptions2 =
@ -71,7 +73,7 @@ public class EncryptionOptionsEqualityTest
.withTrustStore("test/conf/cassandra_ssl_test.truststore") .withTrustStore("test/conf/cassandra_ssl_test.truststore")
.withTrustStorePassword("cassandra") .withTrustStorePassword("cassandra")
.withProtocol("TLSv1.1") .withProtocol("TLSv1.1")
.withRequireClientAuth(true) .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false); .withRequireEndpointVerification(false);
assertEquals(encryptionOptions1, encryptionOptions2); assertEquals(encryptionOptions1, encryptionOptions2);
@ -88,7 +90,7 @@ public class EncryptionOptionsEqualityTest
new EncryptionOptions() new EncryptionOptions()
.withSslContextFactory(new ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters1)) .withSslContextFactory(new ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters1))
.withProtocol("TLSv1.1") .withProtocol("TLSv1.1")
.withRequireClientAuth(true) .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false); .withRequireEndpointVerification(false);
Map<String,String> parameters2 = new HashMap<>(); Map<String,String> parameters2 = new HashMap<>();
@ -98,7 +100,7 @@ public class EncryptionOptionsEqualityTest
new EncryptionOptions() new EncryptionOptions()
.withSslContextFactory(new ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters2)) .withSslContextFactory(new ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters2))
.withProtocol("TLSv1.1") .withProtocol("TLSv1.1")
.withRequireClientAuth(true) .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false); .withRequireEndpointVerification(false);
assertEquals(encryptionOptions1, encryptionOptions2); assertEquals(encryptionOptions1, encryptionOptions2);
@ -115,7 +117,7 @@ public class EncryptionOptionsEqualityTest
new EncryptionOptions() new EncryptionOptions()
.withSslContextFactory(new ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters1)) .withSslContextFactory(new ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters1))
.withProtocol("TLSv1.1") .withProtocol("TLSv1.1")
.withRequireClientAuth(false) .withRequireClientAuth(NOT_REQUIRED)
.withRequireEndpointVerification(true); .withRequireEndpointVerification(true);
Map<String,String> parameters2 = new HashMap<>(); Map<String,String> parameters2 = new HashMap<>();
@ -125,7 +127,7 @@ public class EncryptionOptionsEqualityTest
new EncryptionOptions() new EncryptionOptions()
.withSslContextFactory(new ParameterizedClass(DefaultSslContextFactory.class.getName(), parameters2)) .withSslContextFactory(new ParameterizedClass(DefaultSslContextFactory.class.getName(), parameters2))
.withProtocol("TLSv1.1") .withProtocol("TLSv1.1")
.withRequireClientAuth(false) .withRequireClientAuth(NOT_REQUIRED)
.withRequireEndpointVerification(true); .withRequireEndpointVerification(true);
assertNotEquals(encryptionOptions1, encryptionOptions2); assertNotEquals(encryptionOptions1, encryptionOptions2);

View File

@ -61,7 +61,7 @@ public class EncryptionOptionsTest
new HashMap<>()), new HashMap<>()),
keystorePath, "dummypass", keystorePath, "dummypass",
"dummytruststore", "dummypass", "dummytruststore", "dummypass",
Collections.emptyList(), null, null, null, "JKS", false, false, enabled, optional) Collections.emptyList(), null, null, null, "JKS", "false", false, enabled, optional)
.applyConfig(), .applyConfig(),
expected, expected,
String.format("optional=%s keystore=%s enabled=%s", optional, keystorePath, enabled)); String.format("optional=%s keystore=%s enabled=%s", optional, keystorePath, enabled));
@ -75,7 +75,7 @@ public class EncryptionOptionsTest
customSslContextFactoryParams), customSslContextFactoryParams),
keystorePath, "dummypass", keystorePath, "dummypass",
"dummytruststore", "dummypass", "dummytruststore", "dummypass",
Collections.emptyList(), null, null, null, "JKS", false, false, enabled, optional) Collections.emptyList(), null, null, null, "JKS", "false", false, enabled, optional)
.applyConfig(), .applyConfig(),
expected, expected,
String.format("optional=%s keystore=%s enabled=%s", optional, keystorePath, enabled)); 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", return new ServerEncryptionOptionsTestCase(new EncryptionOptions.ServerEncryptionOptions(new ParameterizedClass("org.apache.cassandra.security.DefaultSslContextFactory",
new HashMap<>()), keystorePath, "dummypass", keystorePath, "dummypass", "dummytruststore", "dummypass", 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(), .applyConfig(),
expected, expected,
String.format("optional=%s keystore=%s internode=%s", optional, keystorePath, internodeEncryption)); 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.apache.cassandra.security.SSLFactory;
import org.yaml.snakeyaml.introspector.Property; import org.yaml.snakeyaml.introspector.Property;
import static org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
public class SettingsTableTest extends CQLTester public class SettingsTableTest extends CQLTester
{ {
private static final String KS_NAME = "vts"; private static final String KS_NAME = "vts";
@ -201,7 +203,7 @@ public class SettingsTableTest extends CQLTester
// name doesn't match yaml // name doesn't match yaml
check(pre + "client_auth", "false"); 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"); check(pre + "client_auth", "true");
// name doesn't match yaml // 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.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS; 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.MessagingService.VERSION_40;
import static org.apache.cassandra.net.NoPayload.noPayload; import static org.apache.cassandra.net.NoPayload.noPayload;
import static org.apache.cassandra.net.MessagingService.current_version; import static org.apache.cassandra.net.MessagingService.current_version;
@ -182,7 +183,7 @@ public class ConnectionTest
.withKeyStorePassword("cassandra") .withKeyStorePassword("cassandra")
.withTrustStore("test/conf/cassandra_ssl_test.truststore") .withTrustStore("test/conf/cassandra_ssl_test.truststore")
.withTrustStorePassword("cassandra") .withTrustStorePassword("cassandra")
.withRequireClientAuth(false) .withRequireClientAuth(NOT_REQUIRED)
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA"); .withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA");
static final List<Function<Settings, Settings>> MODIFIERS = ImmutableList.of( 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 org.apache.cassandra.utils.concurrent.AsyncPromise;
import static org.apache.cassandra.net.ConnectionType.SMALL_MESSAGES; 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.current_version;
import static org.apache.cassandra.net.MessagingService.minimum_version; import static org.apache.cassandra.net.MessagingService.minimum_version;
import static org.apache.cassandra.net.OutboundConnectionInitiator.Result; import static org.apache.cassandra.net.OutboundConnectionInitiator.Result;
@ -285,12 +287,12 @@ public class HandshakeTest
if (sslConnectionType == SslFallbackConnectionType.MTLS) if (sslConnectionType == SslFallbackConnectionType.MTLS)
{ {
serverEncryptionOptions = serverEncryptionOptions.withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all) serverEncryptionOptions = serverEncryptionOptions.withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all)
.withRequireClientAuth(true); .withRequireClientAuth(REQUIRED);
} }
else if (sslConnectionType == SslFallbackConnectionType.SSL) else if (sslConnectionType == SslFallbackConnectionType.SSL)
{ {
serverEncryptionOptions = serverEncryptionOptions.withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all) serverEncryptionOptions = serverEncryptionOptions.withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all)
.withRequireClientAuth(false); .withRequireClientAuth(NOT_REQUIRED);
} }
return serverEncryptionOptions; 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.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 public class DefaultSslContextFactoryTest
{ {
private Map<String,Object> commonConfig = new HashMap<>(); 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", "test/conf/cassandra_ssl_test.truststore");
commonConfig.put("truststore_password", "cassandra"); 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")); commonConfig.put("cipher_suites", Arrays.asList("TLS_RSA_WITH_AES_128_CBC_SHA"));
} }
@ -71,9 +74,9 @@ public class DefaultSslContextFactoryTest
.withKeyStorePassword("cassandra") .withKeyStorePassword("cassandra")
.withOutboundKeystore("test/conf/cassandra_ssl_test_outbound.keystore") .withOutboundKeystore("test/conf/cassandra_ssl_test_outbound.keystore")
.withOutboundKeystorePassword("cassandra") .withOutboundKeystorePassword("cassandra")
.withRequireClientAuth(false) .withRequireClientAuth(NOT_REQUIRED)
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA"); .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); Assert.assertNotNull(sslContext);
if (OpenSsl.isAvailable()) if (OpenSsl.isAvailable())
Assert.assertTrue(sslContext instanceof OpenSslContext); 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.CipherSuiteFilter;
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContext;
import org.apache.cassandra.config.EncryptionOptions;
/** /**
* TEST ONLY Class. DON'T use it for anything else. * TEST ONLY Class. DON'T use it for anything else.
@ -43,7 +44,19 @@ public class DummySslContextFactoryImpl implements ISslContextFactory
} }
@Override @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 CipherSuiteFilter cipherFilter) throws SSLException
{ {
return null; 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.CassandraRelevantProperties.CASSANDRA_CONFIG;
import static org.apache.cassandra.config.EncryptionOptions.ClientAuth.NOT_REQUIRED;
public class FileBasedSslContextFactoryTest public class FileBasedSslContextFactoryTest
{ {
private static final Logger logger = LoggerFactory.getLogger(FileBasedSslContextFactoryTest.class); private static final Logger logger = LoggerFactory.getLogger(FileBasedSslContextFactoryTest.class);
@ -65,7 +67,7 @@ public class FileBasedSslContextFactoryTest
new HashMap<>())) new HashMap<>()))
.withTrustStore("test/conf/cassandra_ssl_test.truststore") .withTrustStore("test/conf/cassandra_ssl_test.truststore")
.withTrustStorePassword("cassandra") .withTrustStorePassword("cassandra")
.withRequireClientAuth(false) .withRequireClientAuth(NOT_REQUIRED)
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA") .withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA")
.withKeyStore("test/conf/cassandra_ssl_test.keystore") .withKeyStore("test/conf/cassandra_ssl_test.keystore")
.withKeyStorePassword("cassandra") .withKeyStorePassword("cassandra")

View File

@ -38,6 +38,8 @@ import org.apache.cassandra.config.ParameterizedClass;
import org.apache.cassandra.distributed.shared.WithProperties; import org.apache.cassandra.distributed.shared.WithProperties;
import static org.apache.cassandra.config.CassandraRelevantProperties.DISABLE_TCACTIVE_OPENSSL; 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_CERTIFICATES;
import static org.apache.cassandra.security.PEMBasedSslContextFactory.ConfigKey.ENCODED_KEY; import static org.apache.cassandra.security.PEMBasedSslContextFactory.ConfigKey.ENCODED_KEY;
import static org.apache.cassandra.security.PEMBasedSslContextFactory.ConfigKey.KEY_PASSWORD; import static org.apache.cassandra.security.PEMBasedSslContextFactory.ConfigKey.KEY_PASSWORD;
@ -176,7 +178,7 @@ public class PEMBasedSslContextFactoryTest
public void setup() public void setup()
{ {
commonConfig.put(ENCODED_CERTIFICATES.getKeyName(), trusted_certificates); 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")); 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") EncryptionOptions options = new EncryptionOptions().withTrustStore("test/conf/cassandra_ssl_test.truststore.pem")
.withKeyStore("test/conf/cassandra_ssl_test.keystore.pem") .withKeyStore("test/conf/cassandra_ssl_test.keystore.pem")
.withKeyStorePassword("cassandra") .withKeyStorePassword("cassandra")
.withRequireClientAuth(false) .withRequireClientAuth(NOT_REQUIRED)
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA") .withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA")
.withSslContextFactory(sslContextFactory); .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); Assert.assertNotNull(sslContext);
if (OpenSsl.isAvailable()) if (OpenSsl.isAvailable())
Assert.assertTrue(sslContext instanceof OpenSslContext); Assert.assertTrue(sslContext instanceof OpenSslContext);
@ -236,10 +238,10 @@ public class PEMBasedSslContextFactoryTest
.withKeyStorePassword("cassandra") .withKeyStorePassword("cassandra")
.withOutboundKeystore("test/conf/cassandra_ssl_test.keystore.pem") .withOutboundKeystore("test/conf/cassandra_ssl_test.keystore.pem")
.withOutboundKeystorePassword("cassandra") .withOutboundKeystorePassword("cassandra")
.withRequireClientAuth(false) .withRequireClientAuth(NOT_REQUIRED)
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA") .withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA")
.withSslContextFactory(sslContextFactory); .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); Assert.assertNotNull(sslContext);
if (OpenSsl.isAvailable()) if (OpenSsl.isAvailable())
Assert.assertTrue(sslContext instanceof OpenSslContext); 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.config.ParameterizedClass;
import org.apache.cassandra.io.util.File; 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.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -81,7 +83,7 @@ public class SSLFactoryTest
encryptionOptions = new ServerEncryptionOptions() encryptionOptions = new ServerEncryptionOptions()
.withTrustStore("test/conf/cassandra_ssl_test.truststore") .withTrustStore("test/conf/cassandra_ssl_test.truststore")
.withTrustStorePassword("cassandra") .withTrustStorePassword("cassandra")
.withRequireClientAuth(false) .withRequireClientAuth(NOT_REQUIRED)
.withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA") .withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA")
.withSslContextFactory(new ParameterizedClass(TestFileBasedSSLContextFactory.class.getName(), .withSslContextFactory(new ParameterizedClass(TestFileBasedSSLContextFactory.class.getName(),
new HashMap<>())); new HashMap<>()));
@ -118,8 +120,8 @@ public class SSLFactoryTest
options.sslContextFactoryInstance.initHotReloading(); options.sslContextFactoryInstance.initHotReloading();
legacyOptions.sslContextFactoryInstance.initHotReloading(); legacyOptions.sslContextFactoryInstance.initHotReloading();
SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
SslContext oldLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, true, ISslContextFactory.SocketType.CLIENT, "test legacy"); SslContext oldLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, REQUIRED, ISslContextFactory.SocketType.CLIENT, "test legacy");
File keystoreFile = new File(options.keystore); File keystoreFile = new File(options.keystore);
SSLFactory.checkCertFilesForHotReloading(); SSLFactory.checkCertFilesForHotReloading();
@ -127,8 +129,8 @@ public class SSLFactoryTest
keystoreFile.trySetLastModified(System.currentTimeMillis() + 15000); keystoreFile.trySetLastModified(System.currentTimeMillis() + 15000);
SSLFactory.checkCertFilesForHotReloading(); SSLFactory.checkCertFilesForHotReloading();
SslContext newCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); SslContext newCtx = SSLFactory.getOrCreateSslContext(options, REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
SslContext newLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, true, ISslContextFactory.SocketType.CLIENT, "test legacy"); SslContext newLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, REQUIRED, ISslContextFactory.SocketType.CLIENT, "test legacy");
Assert.assertNotSame(oldCtx, newCtx); Assert.assertNotSame(oldCtx, newCtx);
Assert.assertNotSame(oldLegacyCtx, newLegacyCtx); Assert.assertNotSame(oldLegacyCtx, newLegacyCtx);
@ -151,7 +153,7 @@ public class SSLFactoryTest
.withOutboundKeystorePassword("dummyPassword"); .withOutboundKeystorePassword("dummyPassword");
// Server socket type should create a keystore with keystore & keystore password // 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); assertNotNull(context);
// Verify if right certificate is loaded into SslContext // Verify if right certificate is loaded into SslContext
@ -168,7 +170,7 @@ public class SSLFactoryTest
.withKeyStorePassword("dummyPassword"); .withKeyStorePassword("dummyPassword");
// Client socket type should create a keystore with outbound Keystore & outbound password // 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); assertNotNull(context);
// Verify if right certificate is loaded into SslContext // Verify if right certificate is loaded into SslContext
@ -189,8 +191,8 @@ public class SSLFactoryTest
options.sslContextFactoryInstance.initHotReloading(); options.sslContextFactoryInstance.initHotReloading();
legacyOptions.sslContextFactoryInstance.initHotReloading(); legacyOptions.sslContextFactoryInstance.initHotReloading();
SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
SslContext oldLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, true, ISslContextFactory.SocketType.CLIENT, "test legacy"); SslContext oldLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, REQUIRED, ISslContextFactory.SocketType.CLIENT, "test legacy");
File keystoreFile = new File(options.keystore); File keystoreFile = new File(options.keystore);
SSLFactory.checkCertFilesForHotReloading(); SSLFactory.checkCertFilesForHotReloading();
@ -198,8 +200,8 @@ public class SSLFactoryTest
keystoreFile.trySetLastModified(System.currentTimeMillis() + 15000); keystoreFile.trySetLastModified(System.currentTimeMillis() + 15000);
SSLFactory.checkCertFilesForHotReloading(); SSLFactory.checkCertFilesForHotReloading();
SslContext newCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); SslContext newCtx = SSLFactory.getOrCreateSslContext(options, REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
SslContext newLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, true, ISslContextFactory.SocketType.CLIENT, "test legacy"); SslContext newLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, REQUIRED, ISslContextFactory.SocketType.CLIENT, "test legacy");
Assert.assertNotSame(oldCtx, newCtx); Assert.assertNotSame(oldCtx, newCtx);
Assert.assertNotSame(oldLegacyCtx, newLegacyCtx); Assert.assertNotSame(oldLegacyCtx, newLegacyCtx);
@ -221,7 +223,7 @@ public class SSLFactoryTest
.withKeyStorePassword("bad password") .withKeyStorePassword("bad password")
.withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all); .withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all);
SSLFactory.validateSslContext("testSslFactorySslInit_BadPassword_ThrowsException", options, false, true); SSLFactory.validateSslContext("testSslFactorySslInit_BadPassword_ThrowsException", options, NOT_REQUIRED, true);
} }
@Test @Test
@ -239,14 +241,14 @@ public class SSLFactoryTest
SSLFactory.initHotReloading(options, options, true); // deliberately not initializing with legacyOptions to match InboundSockets.addBindings 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 oldCtx = SSLFactory.getOrCreateSslContext(options, REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
SslContext oldLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, true, ISslContextFactory.SocketType.CLIENT, "test legacy"); SslContext oldLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, REQUIRED, ISslContextFactory.SocketType.CLIENT, "test legacy");
changeKeystorePassword(options.keystore, options.keystore_password, "bad password"); changeKeystorePassword(options.keystore, options.keystore_password, "bad password");
SSLFactory.checkCertFilesForHotReloading(); SSLFactory.checkCertFilesForHotReloading();
SslContext newCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); SslContext newCtx = SSLFactory.getOrCreateSslContext(options, REQUIRED, ISslContextFactory.SocketType.CLIENT, "test");
SslContext newLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, true, ISslContextFactory.SocketType.CLIENT, "test legacy"); SslContext newLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, REQUIRED, ISslContextFactory.SocketType.CLIENT, "test legacy");
Assert.assertSame(oldCtx, newCtx); Assert.assertSame(oldCtx, newCtx);
Assert.assertSame(oldLegacyCtx, newLegacyCtx); Assert.assertSame(oldLegacyCtx, newLegacyCtx);
@ -270,14 +272,14 @@ public class SSLFactoryTest
SSLFactory.initHotReloading(options, options, true); 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(); SSLFactory.checkCertFilesForHotReloading();
testKeystoreFile.trySetLastModified(System.currentTimeMillis() + 15000); testKeystoreFile.trySetLastModified(System.currentTimeMillis() + 15000);
FileUtils.forceDelete(testKeystoreFile.toJavaIOFile()); FileUtils.forceDelete(testKeystoreFile.toJavaIOFile());
SSLFactory.checkCertFilesForHotReloading(); 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); Assert.assertSame(oldCtx, newCtx);
} }
@ -298,7 +300,7 @@ public class SSLFactoryTest
ServerEncryptionOptions options = addKeystoreOptions(encryptionOptions) ServerEncryptionOptions options = addKeystoreOptions(encryptionOptions)
.withCipherSuites("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"); .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"); ISslContextFactory.SocketType.SERVER, "test");
Assert.assertTrue(ctx1.isServer()); Assert.assertTrue(ctx1.isServer());
@ -306,7 +308,7 @@ public class SSLFactoryTest
options = options.withCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); 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"); ISslContextFactory.SocketType.CLIENT, "test");
Assert.assertTrue(ctx2.isClient()); Assert.assertTrue(ctx2.isClient());
@ -323,7 +325,7 @@ public class SSLFactoryTest
new EncryptionOptions() new EncryptionOptions()
.withSslContextFactory(new ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters1)) .withSslContextFactory(new ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters1))
.withProtocol("TLSv1.1") .withProtocol("TLSv1.1")
.withRequireClientAuth(true) .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false); .withRequireEndpointVerification(false);
SSLFactory.CacheKey cacheKey1 = new SSLFactory.CacheKey(encryptionOptions1, ISslContextFactory.SocketType.SERVER, "test" SSLFactory.CacheKey cacheKey1 = new SSLFactory.CacheKey(encryptionOptions1, ISslContextFactory.SocketType.SERVER, "test"
@ -336,7 +338,7 @@ public class SSLFactoryTest
new EncryptionOptions() new EncryptionOptions()
.withSslContextFactory(new ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters2)) .withSslContextFactory(new ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters2))
.withProtocol("TLSv1.1") .withProtocol("TLSv1.1")
.withRequireClientAuth(true) .withRequireClientAuth(REQUIRED)
.withRequireEndpointVerification(false); .withRequireEndpointVerification(false);
SSLFactory.CacheKey cacheKey2 = new SSLFactory.CacheKey(encryptionOptions2, ISslContextFactory.SocketType.SERVER, "test" 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.security.SSLFactory;
import org.apache.cassandra.stress.settings.StressSettings; import org.apache.cassandra.stress.settings.StressSettings;
import static org.apache.cassandra.config.EncryptionOptions.ClientAuth.REQUIRED;
public class JavaDriverClient public class JavaDriverClient
{ {
@ -161,7 +163,7 @@ public class JavaDriverClient
if (encryptionOptions.getEnabled()) if (encryptionOptions.getEnabled())
{ {
SSLContext sslContext; SSLContext sslContext;
sslContext = SSLFactory.createSSLContext(encryptionOptions, true); sslContext = SSLFactory.createSSLContext(encryptionOptions, REQUIRED);
// Temporarily override newSSLEngine to set accepted protocols until it is added to // Temporarily override newSSLEngine to set accepted protocols until it is added to
// RemoteEndpointAwareJdkSSLOptions. See CASSANDRA-13325 and CASSANDRA-16362. // RemoteEndpointAwareJdkSSLOptions. See CASSANDRA-13325 and CASSANDRA-16362.