mirror of https://github.com/apache/cassandra
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:
parent
447d2bcee7
commit
bfcb21fbeb
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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 -> {
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue