mirror of https://github.com/apache/cassandra
Make JMX SSL configurable in cassandra.yaml
patch by Maulin Vasavada; reviewed by Stefan Miklosovic, Doug Rohrer for CASSANDRA-18508
This commit is contained in:
parent
8b126e97bb
commit
9131be9fa5
|
|
@ -1,4 +1,5 @@
|
||||||
5.1
|
5.1
|
||||||
|
* Make JMX SSL configurable in cassandra.yaml (CASSANDRA-18508)
|
||||||
* Fix cqlsh CAPTURE command to save query results without trace details when TRACING is ON (CASSANDRA-19105)
|
* Fix cqlsh CAPTURE command to save query results without trace details when TRACING is ON (CASSANDRA-19105)
|
||||||
* Optionally prevent tombstone purging during repair (CASSANDRA-20071)
|
* Optionally prevent tombstone purging during repair (CASSANDRA-20071)
|
||||||
* Add post-filtering support for the IN operator in SAI queries (CASSANDRA-20025)
|
* Add post-filtering support for the IN operator in SAI queries (CASSANDRA-20025)
|
||||||
|
|
|
||||||
1
NEWS.txt
1
NEWS.txt
|
|
@ -88,6 +88,7 @@ New features
|
||||||
generate a password of configured password strength policy upon role creation or alteration
|
generate a password of configured password strength policy upon role creation or alteration
|
||||||
when 'GENERATED PASSWORD' clause is used. Character sets supported are: English, Cyrillic, modern Cyrillic,
|
when 'GENERATED PASSWORD' clause is used. Character sets supported are: English, Cyrillic, modern Cyrillic,
|
||||||
German, Polish and Czech.
|
German, Polish and Czech.
|
||||||
|
- JMX SSL configuration can be now done in cassandra.yaml via jmx_encryption_options section instead of cassandra-env.sh
|
||||||
|
|
||||||
|
|
||||||
Upgrading
|
Upgrading
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,11 @@ else
|
||||||
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=true"
|
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=true"
|
||||||
|
|
||||||
# jmx ssl options
|
# jmx ssl options
|
||||||
|
# Consider using the jmx_encryption_options section of cassandra.yaml instead
|
||||||
|
# to prevent sensitive information being exposed.
|
||||||
|
# In case jmx ssl options are configured in both the places - this file and cassandra.yaml, and
|
||||||
|
# if com.sun.management.jmxremote.ssl is configured to be true here and encryption_options are marked enabled in
|
||||||
|
# cassandra.yaml, then we will get exception at the startup
|
||||||
#JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl=true"
|
#JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl=true"
|
||||||
#JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl.need.client.auth=true"
|
#JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl.need.client.auth=true"
|
||||||
#JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl.enabled.protocols=<enabled-protocols>"
|
#JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl.enabled.protocols=<enabled-protocols>"
|
||||||
|
|
|
||||||
|
|
@ -1712,6 +1712,24 @@ client_encryption_options:
|
||||||
# warnings will be reported during the session establishment.
|
# warnings will be reported during the session establishment.
|
||||||
# certificate_validity_warn_threshold: 10d
|
# certificate_validity_warn_threshold: 10d
|
||||||
|
|
||||||
|
# Configure SSL for JMX
|
||||||
|
#
|
||||||
|
# Using system properties via cassandra-env.sh to configure JMX SSL is
|
||||||
|
# considered legacy and only supported for backward compatibility. If SSL
|
||||||
|
# is enabled via both methods, a configuration error will occur at
|
||||||
|
# startup. Hot reloading of the `SSLContext` is not yet supported for the
|
||||||
|
# JMX SSL.
|
||||||
|
# Similar to `client/server_encryption_options`, you can specify PEM-based
|
||||||
|
# key material or customize the SSL configuration using `ssl_context_factory` in `jmx_encryption_options`.
|
||||||
|
#jmx_encryption_options:
|
||||||
|
# enabled: true
|
||||||
|
# cipher_suites: [TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]
|
||||||
|
# accepted_protocols: [TLSv1.2,TLSv1.3,TLSv1.1]
|
||||||
|
# keystore: conf/cassandra_ssl.keystore
|
||||||
|
# keystore_password: cassandra
|
||||||
|
# truststore: conf/cassandra_ssl.truststore
|
||||||
|
# truststore_password: cassandra
|
||||||
|
|
||||||
# internode_compression controls whether traffic between nodes is
|
# internode_compression controls whether traffic between nodes is
|
||||||
# compressed.
|
# compressed.
|
||||||
# Can be:
|
# Can be:
|
||||||
|
|
|
||||||
|
|
@ -1676,6 +1676,24 @@ client_encryption_options:
|
||||||
# TLS_RSA_WITH_AES_256_CBC_SHA
|
# TLS_RSA_WITH_AES_256_CBC_SHA
|
||||||
# ]
|
# ]
|
||||||
|
|
||||||
|
# Configure SSL for JMX
|
||||||
|
#
|
||||||
|
# Using system properties via cassandra-env.sh to configure JMX SSL is
|
||||||
|
# considered legacy and only supported for backward compatibility. If SSL
|
||||||
|
# is enabled via both methods, a configuration error will occur at
|
||||||
|
# startup. Hot reloading of the `SSLContext` is not yet supported for the
|
||||||
|
# JMX SSL.
|
||||||
|
# Similar to `client/server_encryption_options`, you can specify PEM-based
|
||||||
|
# key material or customize the SSL configuration using `ssl_context_factory` in `jmx_encryption_options`.
|
||||||
|
#jmx_encryption_options:
|
||||||
|
# enabled: true
|
||||||
|
# cipher_suites: [TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]
|
||||||
|
# accepted_protocols: [TLSv1.2,TLSv1.3,TLSv1.1]
|
||||||
|
# keystore: conf/cassandra_ssl.keystore
|
||||||
|
# keystore_password: cassandra
|
||||||
|
# truststore: conf/cassandra_ssl.truststore
|
||||||
|
# truststore_password: cassandra
|
||||||
|
|
||||||
# internode_compression controls whether traffic between nodes is
|
# internode_compression controls whether traffic between nodes is
|
||||||
# compressed.
|
# compressed.
|
||||||
# Can be:
|
# Can be:
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,8 @@ files having the required PEM data as shown below,
|
||||||
* Configuration: PEM keys/certs defined in-line (mind the spaces in the
|
* Configuration: PEM keys/certs defined in-line (mind the spaces in the
|
||||||
YAML!)
|
YAML!)
|
||||||
|
|
||||||
....
|
[source,yaml]
|
||||||
|
----
|
||||||
client/server_encryption_options:
|
client/server_encryption_options:
|
||||||
ssl_context_factory:
|
ssl_context_factory:
|
||||||
class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
|
class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
|
||||||
|
|
@ -97,17 +98,19 @@ YAML!)
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
<your base64 encoded certificate>
|
<your base64 encoded certificate>
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
....
|
----
|
||||||
|
|
||||||
* Configuration: PEM keys/certs defined in files
|
* Configuration: PEM keys/certs defined in files
|
||||||
....
|
|
||||||
|
[source,yaml]
|
||||||
|
----
|
||||||
client/server_encryption_options:
|
client/server_encryption_options:
|
||||||
ssl_context_factory:
|
ssl_context_factory:
|
||||||
class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
|
class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
|
||||||
keystore: <file path to the keystore file in the PEM format with the private key and the certificate chain>
|
keystore: <file path to the keystore file in the PEM format with the private key and the certificate chain>
|
||||||
keystore_password: "<your password if the private key is encrypted with a password>"
|
keystore_password: "<your password if the private key is encrypted with a password>"
|
||||||
truststore: <file path to the truststore file in the PEM format>
|
truststore: <file path to the truststore file in the PEM format>
|
||||||
....
|
----
|
||||||
|
|
||||||
== SSL Certificate Hot Reloading
|
== SSL Certificate Hot Reloading
|
||||||
|
|
||||||
|
|
@ -564,8 +567,61 @@ See also: xref:cassandra:developing/cql/security.adoc#permissions[`Permissions`]
|
||||||
|
|
||||||
=== JMX With SSL
|
=== JMX With SSL
|
||||||
|
|
||||||
JMX SSL configuration is controlled by a number of system properties,
|
JMX SSL configuration should be specified in `cassandra.yaml` under
|
||||||
some of which are optional. To turn on SSL, edit the relevant lines in
|
`encryption_options`, similar to how `client/server_encryption_options`
|
||||||
|
are configured. Using system properties to configure JMX SSL is
|
||||||
|
considered legacy and only supported for backward compatibility. If SSL
|
||||||
|
is enabled via both methods, a configuration error will occur at
|
||||||
|
startup. Hot reloading of the `SSLContext` is not yet supported for the
|
||||||
|
JMX SSL.
|
||||||
|
|
||||||
|
==== JMX SSL configuration in cassandra.yaml
|
||||||
|
Below is an example of configuring JMX SSL in `cassandra.yaml`
|
||||||
|
|
||||||
|
[source,yaml]
|
||||||
|
----
|
||||||
|
jmx_encryption_options:
|
||||||
|
enabled: true
|
||||||
|
cipher_suites: [TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]
|
||||||
|
accepted_protocols: [TLSv1.2,TLSv1.3,TLSv1.1]
|
||||||
|
keystore: test/conf/cassandra_ssl_test.keystore
|
||||||
|
keystore_password: cassandra
|
||||||
|
truststore: test/conf/cassandra_ssl_test.truststore
|
||||||
|
truststore_password: cassandra
|
||||||
|
----
|
||||||
|
|
||||||
|
Similar to `client/server_encryption_options`, you can specify PEM-based
|
||||||
|
key material or customize the SSL configuration using
|
||||||
|
`ssl_context_factory` in `jmx_encryption_options`.
|
||||||
|
|
||||||
|
Below is an example of configuring PEM based key material,
|
||||||
|
[source,yaml]
|
||||||
|
----
|
||||||
|
jmx_encryption_options:
|
||||||
|
enabled: true
|
||||||
|
cipher_suites: [TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]
|
||||||
|
accepted_protocols: [TLSv1.2,TLSv1.3,TLSv1.1]
|
||||||
|
ssl_context_factory:
|
||||||
|
class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
|
||||||
|
parameters:
|
||||||
|
private_key: |
|
||||||
|
-----BEGIN ENCRYPTED PRIVATE KEY----- OR -----BEGIN PRIVATE KEY-----
|
||||||
|
<your base64 encoded private key>
|
||||||
|
-----END ENCRYPTED PRIVATE KEY----- OR -----END PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
<your base64 encoded certificate chain>
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
private_key_password: "<your password if the private key is encrypted with a password>"
|
||||||
|
|
||||||
|
trusted_certificates: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
<your base64 encoded certificate>
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
----
|
||||||
|
|
||||||
|
==== (Legacy) JMX SSL configuration with system properties
|
||||||
|
To turn on SSL this way, edit the relevant lines in
|
||||||
`cassandra-env.sh` to uncomment and
|
`cassandra-env.sh` to uncomment and
|
||||||
set the values of these properties as required:
|
set the values of these properties as required:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ COMPLEX_OPTIONS = (
|
||||||
'hints_compression',
|
'hints_compression',
|
||||||
'server_encryption_options',
|
'server_encryption_options',
|
||||||
'client_encryption_options',
|
'client_encryption_options',
|
||||||
|
'jmx_encryption_options',
|
||||||
'transparent_data_encryption_options',
|
'transparent_data_encryption_options',
|
||||||
'hinted_handoff_disabled_datacenters',
|
'hinted_handoff_disabled_datacenters',
|
||||||
'startup_checks',
|
'startup_checks',
|
||||||
|
|
|
||||||
|
|
@ -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", "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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -434,6 +434,7 @@ public class Config
|
||||||
|
|
||||||
public EncryptionOptions.ServerEncryptionOptions server_encryption_options = new EncryptionOptions.ServerEncryptionOptions();
|
public EncryptionOptions.ServerEncryptionOptions server_encryption_options = new EncryptionOptions.ServerEncryptionOptions();
|
||||||
public EncryptionOptions client_encryption_options = new EncryptionOptions();
|
public EncryptionOptions client_encryption_options = new EncryptionOptions();
|
||||||
|
public EncryptionOptions jmx_encryption_options = new EncryptionOptions();
|
||||||
|
|
||||||
public InternodeCompression internode_compression = InternodeCompression.none;
|
public InternodeCompression internode_compression = InternodeCompression.none;
|
||||||
|
|
||||||
|
|
@ -1320,6 +1321,7 @@ public class Config
|
||||||
private static final Set<String> SENSITIVE_KEYS = new HashSet<String>() {{
|
private static final Set<String> SENSITIVE_KEYS = new HashSet<String>() {{
|
||||||
add("client_encryption_options");
|
add("client_encryption_options");
|
||||||
add("server_encryption_options");
|
add("server_encryption_options");
|
||||||
|
add("jmx_encryption_options");
|
||||||
}};
|
}};
|
||||||
|
|
||||||
public static void log(Config config)
|
public static void log(Config config)
|
||||||
|
|
|
||||||
|
|
@ -960,6 +960,9 @@ public class DatabaseDescriptor
|
||||||
if (conf.client_encryption_options != null)
|
if (conf.client_encryption_options != null)
|
||||||
conf.client_encryption_options.applyConfig();
|
conf.client_encryption_options.applyConfig();
|
||||||
|
|
||||||
|
if (conf.jmx_encryption_options != null)
|
||||||
|
conf.jmx_encryption_options.applyConfig();
|
||||||
|
|
||||||
if (conf.snapshot_links_per_second < 0)
|
if (conf.snapshot_links_per_second < 0)
|
||||||
throw new ConfigurationException("snapshot_links_per_second must be >= 0");
|
throw new ConfigurationException("snapshot_links_per_second must be >= 0");
|
||||||
|
|
||||||
|
|
@ -1306,7 +1309,14 @@ public class DatabaseDescriptor
|
||||||
{
|
{
|
||||||
SSLFactory.validateSslContext("Internode messaging", conf.server_encryption_options, REQUIRED, true);
|
SSLFactory.validateSslContext("Internode messaging", conf.server_encryption_options, REQUIRED, true);
|
||||||
SSLFactory.validateSslContext("Native transport", conf.client_encryption_options, conf.client_encryption_options.getClientAuth(), true);
|
SSLFactory.validateSslContext("Native transport", conf.client_encryption_options, conf.client_encryption_options.getClientAuth(), true);
|
||||||
|
// For JMX SSL the validation is pretty much the same as the Native transport
|
||||||
|
SSLFactory.validateSslContext("JMX transport", conf.jmx_encryption_options, conf.jmx_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);
|
||||||
|
/*
|
||||||
|
For JMX SSL, the hot reloading of the SSLContext is out of scope for CASSANDRA-18508.
|
||||||
|
Since JMXServerUtil that initializes the JMX Server is used statically, it may require significant
|
||||||
|
effort to change that behavior unlike SSLFactory used for Native transport/Internode messaging.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
|
@ -3653,6 +3663,11 @@ public class DatabaseDescriptor
|
||||||
return conf.client_encryption_options;
|
return conf.client_encryption_options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static EncryptionOptions getJmxEncryptionOptions()
|
||||||
|
{
|
||||||
|
return conf.jmx_encryption_options;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static void updateNativeProtocolEncryptionOptions(Function<EncryptionOptions, EncryptionOptions> update)
|
public static void updateNativeProtocolEncryptionOptions(Function<EncryptionOptions, EncryptionOptions> update)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -172,10 +172,10 @@ public class EncryptionOptions
|
||||||
|
|
||||||
ConfigKey(String keyName)
|
ConfigKey(String keyName)
|
||||||
{
|
{
|
||||||
this.keyName=keyName;
|
this.keyName = keyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getKeyName()
|
public String toString()
|
||||||
{
|
{
|
||||||
return keyName;
|
return keyName;
|
||||||
}
|
}
|
||||||
|
|
@ -184,8 +184,9 @@ public class EncryptionOptions
|
||||||
{
|
{
|
||||||
Set<String> valueSet = new HashSet<>();
|
Set<String> valueSet = new HashSet<>();
|
||||||
ConfigKey[] values = values();
|
ConfigKey[] values = values();
|
||||||
for(ConfigKey key: values) {
|
for (ConfigKey key : values)
|
||||||
valueSet.add(toLowerCaseLocalized(key.getKeyName()));
|
{
|
||||||
|
valueSet.add(toLowerCaseLocalized(key.toString()));
|
||||||
}
|
}
|
||||||
return valueSet;
|
return valueSet;
|
||||||
}
|
}
|
||||||
|
|
@ -357,7 +358,7 @@ public class EncryptionOptions
|
||||||
protected static void putSslContextFactoryParameter(Map<String, Object> existingParameters, ConfigKey configKey, Object value)
|
protected static void putSslContextFactoryParameter(Map<String, Object> existingParameters, ConfigKey configKey, Object value)
|
||||||
{
|
{
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
existingParameters.put(configKey.getKeyName(), value);
|
existingParameters.put(configKey.toString(), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,24 +37,25 @@ import java.rmi.registry.Registry;
|
||||||
import java.rmi.server.RMIClientSocketFactory;
|
import java.rmi.server.RMIClientSocketFactory;
|
||||||
import java.rmi.server.RMIServerSocketFactory;
|
import java.rmi.server.RMIServerSocketFactory;
|
||||||
import java.rmi.server.UnicastRemoteObject;
|
import java.rmi.server.UnicastRemoteObject;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import javax.management.remote.JMXAuthenticator;
|
||||||
import javax.management.remote.*;
|
import javax.management.remote.JMXConnectorServer;
|
||||||
|
import javax.management.remote.JMXServiceURL;
|
||||||
|
import javax.management.remote.MBeanServerForwarder;
|
||||||
import javax.management.remote.rmi.RMIConnectorServer;
|
import javax.management.remote.rmi.RMIConnectorServer;
|
||||||
import javax.management.remote.rmi.RMIJRMPServerImpl;
|
import javax.management.remote.rmi.RMIJRMPServerImpl;
|
||||||
import javax.rmi.ssl.SslRMIClientSocketFactory;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.rmi.ssl.SslRMIServerSocketFactory;
|
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.apache.cassandra.auth.jmx.AuthenticationProxy;
|
import org.apache.cassandra.auth.jmx.AuthenticationProxy;
|
||||||
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
|
import org.apache.cassandra.utils.jmx.DefaultJmxSocketFactory;
|
||||||
|
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.CASSANDRA_JMX_AUTHORIZER;
|
import static org.apache.cassandra.config.CassandraRelevantProperties.CASSANDRA_JMX_AUTHORIZER;
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.CASSANDRA_JMX_REMOTE_LOGIN_CONFIG;
|
import static org.apache.cassandra.config.CassandraRelevantProperties.CASSANDRA_JMX_REMOTE_LOGIN_CONFIG;
|
||||||
|
|
@ -62,12 +63,6 @@ import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MA
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_AUTHENTICATE;
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_AUTHENTICATE;
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_PASSWORD_FILE;
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_PASSWORD_FILE;
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_RMI_PORT;
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_RMI_PORT;
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL;
|
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_CIPHER_SUITES;
|
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_PROTOCOLS;
|
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL_NEED_CLIENT_AUTH;
|
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES;
|
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS;
|
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVA_RMI_SERVER_HOSTNAME;
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVA_RMI_SERVER_HOSTNAME;
|
||||||
|
|
||||||
public class JMXServerUtils
|
public class JMXServerUtils
|
||||||
|
|
@ -92,6 +87,7 @@ public class JMXServerUtils
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the RMI client & server socket factories, including SSL config.
|
// Configure the RMI client & server socket factories, including SSL config.
|
||||||
|
// CASSANDRA-18508: Make JMX SSL to be configured in cassandra.yaml
|
||||||
env.putAll(configureJmxSocketFactories(serverAddress, local));
|
env.putAll(configureJmxSocketFactories(serverAddress, local));
|
||||||
|
|
||||||
// configure the RMI registry
|
// configure the RMI registry
|
||||||
|
|
@ -226,46 +222,33 @@ public class JMXServerUtils
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, Object> configureJmxSocketFactories(InetAddress serverAddress, boolean localOnly)
|
/**
|
||||||
|
* Configures the client and server socket factories for the JMX connection. It uses {@link DefaultJmxSocketFactory}
|
||||||
|
* for configuring this.
|
||||||
|
*
|
||||||
|
* @param serverAddress the JMX server is bound to
|
||||||
|
* @param localOnly {@code true} if the JMX server only allows local connections; {@code false} if the JMX server
|
||||||
|
* allows the remote connections.
|
||||||
|
* @return Map<String, Object@gt; containing {@code jmx.remote.rmi.client.socket.factory}, {@code jmx.remote.rmi.server.socket.factory}
|
||||||
|
* and {@code com.sun.jndi.rmi.factory.socket} properties for the client and server socket factories.
|
||||||
|
* @throws SSLException if it fails to configure the socket factories with the given input
|
||||||
|
*
|
||||||
|
* @see DefaultJmxSocketFactory
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public static Map<String, Object> configureJmxSocketFactories(InetAddress serverAddress, boolean localOnly) throws SSLException
|
||||||
{
|
{
|
||||||
Map<String, Object> env = new HashMap<>();
|
return new DefaultJmxSocketFactory().configure(serverAddress, localOnly, DatabaseDescriptor.getJmxEncryptionOptions());
|
||||||
if (COM_SUN_MANAGEMENT_JMXREMOTE_SSL.getBoolean())
|
|
||||||
{
|
|
||||||
boolean requireClientAuth = COM_SUN_MANAGEMENT_JMXREMOTE_SSL_NEED_CLIENT_AUTH.getBoolean();
|
|
||||||
String[] protocols = null;
|
|
||||||
String protocolList = COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_PROTOCOLS.getString();
|
|
||||||
if (protocolList != null)
|
|
||||||
{
|
|
||||||
JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS.setString(protocolList);
|
|
||||||
protocols = StringUtils.split(protocolList, ',');
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] ciphers = null;
|
|
||||||
String cipherList = COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_CIPHER_SUITES.getString();
|
|
||||||
if (cipherList != null)
|
|
||||||
{
|
|
||||||
JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES.setString(cipherList);
|
|
||||||
ciphers = StringUtils.split(cipherList, ',');
|
|
||||||
}
|
|
||||||
|
|
||||||
SslRMIClientSocketFactory clientFactory = new SslRMIClientSocketFactory();
|
|
||||||
SslRMIServerSocketFactory serverFactory = new SslRMIServerSocketFactory(ciphers, protocols, requireClientAuth);
|
|
||||||
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverFactory);
|
|
||||||
env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, clientFactory);
|
|
||||||
env.put("com.sun.jndi.rmi.factory.socket", clientFactory);
|
|
||||||
logJmxSslConfig(serverFactory);
|
|
||||||
}
|
|
||||||
else if (localOnly)
|
|
||||||
{
|
|
||||||
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
|
|
||||||
new RMIServerSocketFactoryImpl(serverAddress));
|
|
||||||
}
|
|
||||||
|
|
||||||
return env;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static void logJmxServiceUrl(InetAddress serverAddress, int port)
|
public static void logJmxServiceUrl(InetAddress serverAddress, int port)
|
||||||
|
{
|
||||||
|
logger.info("Configured JMX server at: {}", getJmxServiceUrl(serverAddress, port));
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
private static String getJmxServiceUrl(InetAddress serverAddress, int port)
|
||||||
{
|
{
|
||||||
String urlTemplate = "service:jmx:rmi://%1$s/jndi/rmi://%1$s:%2$d/jmxrmi";
|
String urlTemplate = "service:jmx:rmi://%1$s/jndi/rmi://%1$s:%2$d/jmxrmi";
|
||||||
String hostName;
|
String hostName;
|
||||||
|
|
@ -280,23 +263,9 @@ public class JMXServerUtils
|
||||||
? '[' + serverAddress.getHostAddress() + ']'
|
? '[' + serverAddress.getHostAddress() + ']'
|
||||||
: serverAddress.getHostAddress();
|
: serverAddress.getHostAddress();
|
||||||
}
|
}
|
||||||
String url = String.format(urlTemplate, hostName, port);
|
return String.format(urlTemplate, hostName, port);
|
||||||
logger.info("Configured JMX server at: {}", url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void logJmxSslConfig(SslRMIServerSocketFactory serverFactory)
|
|
||||||
{
|
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
logger.debug("JMX SSL configuration. { protocols: [{}], cipher_suites: [{}], require_client_auth: {} }",
|
|
||||||
serverFactory.getEnabledProtocols() == null
|
|
||||||
? "'JVM defaults'"
|
|
||||||
: Arrays.stream(serverFactory.getEnabledProtocols()).collect(Collectors.joining("','", "'", "'")),
|
|
||||||
serverFactory.getEnabledCipherSuites() == null
|
|
||||||
? "'JVM defaults'"
|
|
||||||
: Arrays.stream(serverFactory.getEnabledCipherSuites()).collect(Collectors.joining("','", "'", "'")),
|
|
||||||
serverFactory.getNeedClientAuth());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class JMXPluggableAuthenticatorWrapper implements JMXAuthenticator
|
private static class JMXPluggableAuthenticatorWrapper implements JMXAuthenticator
|
||||||
{
|
{
|
||||||
private static final MethodHandle ctorHandle;
|
private static final MethodHandle ctorHandle;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.rmi.server.RMIClientSocketFactory;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
@ -32,8 +31,9 @@ import java.util.Objects;
|
||||||
* which can otherwise be influenced by the system property "java.rmi.server.hostname" in strange and
|
* which can otherwise be influenced by the system property "java.rmi.server.hostname" in strange and
|
||||||
* unpredictable ways.
|
* unpredictable ways.
|
||||||
*/
|
*/
|
||||||
public class RMIClientSocketFactoryImpl implements RMIClientSocketFactory, Serializable
|
public class RMIClientSocketFactoryImpl implements Serializable, RMICloseableClientSocketFactory
|
||||||
{
|
{
|
||||||
|
private static final long serialVersionUID = 955153017775496366L;
|
||||||
List<Socket> sockets = new ArrayList<>();
|
List<Socket> sockets = new ArrayList<>();
|
||||||
private final InetAddress localAddress;
|
private final InetAddress localAddress;
|
||||||
|
|
||||||
|
|
@ -50,6 +50,7 @@ public class RMIClientSocketFactoryImpl implements RMIClientSocketFactory, Seria
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void close() throws IOException
|
public void close() throws IOException
|
||||||
{
|
{
|
||||||
for (Socket socket: sockets)
|
for (Socket socket: sockets)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.utils;
|
||||||
|
|
||||||
|
import java.rmi.server.RMIClientSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This represents closeable RMI Client Socket factory. It extends {@link AutoCloseable} and can be used with
|
||||||
|
* {@code try-with-resources}.
|
||||||
|
*/
|
||||||
|
public interface RMICloseableClientSocketFactory extends RMIClientSocketFactory, AutoCloseable
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.utils;
|
||||||
|
|
||||||
|
import java.rmi.server.RMIServerSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This represents closeable RMI Server Socket factory. It extends {@link AutoCloseable} and can be used with
|
||||||
|
* {@code try-with-resources}.
|
||||||
|
*/
|
||||||
|
public interface RMICloseableServerSocketFactory extends RMIServerSocketFactory, AutoCloseable
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,208 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.utils.jmx;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.apache.cassandra.config.EncryptionOptions;
|
||||||
|
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_CIPHER_SUITES;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_PROTOCOLS;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL_NEED_CLIENT_AUTH;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstracts out the most common workflow in setting up the SSL client and server socket factorires for JMX.
|
||||||
|
* First, it checks the system properties (see <a href="https://docs.oracle.com/en/java/javase/17/management/monitoring-and-management-using-jmx-technology.html#GUID-F08985BB-629A-4FBF-A0CB-8762DF7590E0">Java Documentation</a> to read the SSL configuration.
|
||||||
|
* Next, it checks the provided {@code jmxEncryptionOptions} to read the SSL configuration.
|
||||||
|
* If none of them is enabled, it checks the provided {@code localOnly} flag to configure the JMX server socket
|
||||||
|
* factory for the local JMX connection.
|
||||||
|
*/
|
||||||
|
abstract public class AbstractJmxSocketFactory
|
||||||
|
{
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AbstractJmxSocketFactory.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the client and server socket factories for the JMX connection.
|
||||||
|
* Specifically it configures below properties as applicable,
|
||||||
|
* <pre>
|
||||||
|
* jmx.remote.rmi.client.socket.factory
|
||||||
|
* jmx.remote.rmi.server.socket.factory
|
||||||
|
* com.sun.jndi.rmi.factory.socket
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* In case of remote connection enabled, this also sets the following system properties,
|
||||||
|
* <pre>
|
||||||
|
* com.sun.management.jmxremote.ssl=true
|
||||||
|
* javax.rmi.ssl.client.enabledCipherSuites=<applicable cipher suites provided in the configuration>
|
||||||
|
* javax.rmi.ssl.client.enabledProtocols=<applicable protocols provided in the configuration>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param serverAddress the JMX server is bound to
|
||||||
|
* @param localOnly {@code true} if the JMX server only allows local connections; {@code false} if the JMX server
|
||||||
|
* allows the remote connections.
|
||||||
|
* @param jmxEncryptionOptions {@link EncryptionOptions} used for the SSL configuration in case of the remote
|
||||||
|
* connections. Could be {@code null} if system properties are
|
||||||
|
* used instead as per <a href="https://docs.oracle.com/en/java/javase/17/management/monitoring-and-management-using-jmx-technology.html#GUID-F08985BB-629A-4FBF-A0CB-8762DF7590E0">Java Documentation</a>
|
||||||
|
* @return Map<String, Object@gt; containing {@code jmx.remote.rmi.client.socket.factory}, {@code jmx.remote.rmi.server.socket.factory}
|
||||||
|
* and {@code com.sun.jndi.rmi.factory.socket} properties for the client and server socket factories.
|
||||||
|
* @throws SSLException if it fails to configure the socket factories with the given input
|
||||||
|
*/
|
||||||
|
public Map<String, Object> configure(InetAddress serverAddress, boolean localOnly,
|
||||||
|
EncryptionOptions jmxEncryptionOptions) throws SSLException
|
||||||
|
{
|
||||||
|
Map<String, Object> env = new HashMap<>();
|
||||||
|
boolean jmxRemoteSslSystemConfigProvided = COM_SUN_MANAGEMENT_JMXREMOTE_SSL.getBoolean();
|
||||||
|
// We check for the enabled jmx_encryption_options here because in case of no configuration provided in cassandra.yaml
|
||||||
|
// it will default to empty/non-null encryption options. Hence, we consider it set only if 'enabled' flag is set to true
|
||||||
|
boolean jmxEncryptionOptionsProvided = jmxEncryptionOptions != null
|
||||||
|
&& jmxEncryptionOptions.getEnabled() != null
|
||||||
|
&& jmxEncryptionOptions.getEnabled();
|
||||||
|
|
||||||
|
if (jmxRemoteSslSystemConfigProvided && jmxEncryptionOptionsProvided)
|
||||||
|
{
|
||||||
|
throw new ConfigurationException("Please specify JMX SSL configuration in either cassandra-env.sh or " +
|
||||||
|
"cassandra.yaml, not in both locations");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean requireClientAuth = false;
|
||||||
|
String[] ciphers = null;
|
||||||
|
String[] protocols = null;
|
||||||
|
SSLContext sslContext = null;
|
||||||
|
|
||||||
|
if (jmxRemoteSslSystemConfigProvided)
|
||||||
|
{
|
||||||
|
logger.info("Enabling JMX SSL using environment file properties");
|
||||||
|
logger.warn("Consider using the jmx_encryption_options section of cassandra.yaml instead to prevent " +
|
||||||
|
"sensitive information being exposed");
|
||||||
|
requireClientAuth = COM_SUN_MANAGEMENT_JMXREMOTE_SSL_NEED_CLIENT_AUTH.getBoolean();
|
||||||
|
String protocolList = COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_PROTOCOLS.getString();
|
||||||
|
if (protocolList != null)
|
||||||
|
{
|
||||||
|
JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS.setString(protocolList);
|
||||||
|
protocols = StringUtils.split(protocolList, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
String cipherList = COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_CIPHER_SUITES.getString();
|
||||||
|
if (cipherList != null)
|
||||||
|
{
|
||||||
|
JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES.setString(cipherList);
|
||||||
|
ciphers = StringUtils.split(cipherList, ',');
|
||||||
|
}
|
||||||
|
configureSslClientSocketFactory(env, serverAddress);
|
||||||
|
configureSslServerSocketFactory(env, serverAddress, ciphers, protocols, requireClientAuth);
|
||||||
|
}
|
||||||
|
else if (jmxEncryptionOptionsProvided)
|
||||||
|
{
|
||||||
|
logger.info("Enabling JMX SSL using jmx_encryption_options from cassandra.yaml");
|
||||||
|
// Here we can continue to use the SslRMIClientSocketFactory for client sockets.
|
||||||
|
// However, we should still set System properties for cipher_suites and enabled_protocols
|
||||||
|
// to have the same behavior as cassandra-env.sh based JMX SSL settings
|
||||||
|
setJmxSystemProperties(jmxEncryptionOptions);
|
||||||
|
|
||||||
|
requireClientAuth = jmxEncryptionOptions.getClientAuth() == EncryptionOptions.ClientAuth.REQUIRED;
|
||||||
|
ciphers = jmxEncryptionOptions.cipherSuitesArray();
|
||||||
|
protocols = jmxEncryptionOptions.acceptedProtocolsArray();
|
||||||
|
sslContext = jmxEncryptionOptions.sslContextFactoryInstance
|
||||||
|
.createJSSESslContext(jmxEncryptionOptions.getClientAuth());
|
||||||
|
configureSslClientSocketFactory(env, serverAddress);
|
||||||
|
configureSslServerSocketFactory(env, serverAddress, ciphers, protocols, requireClientAuth, sslContext);
|
||||||
|
}
|
||||||
|
else if (localOnly)
|
||||||
|
{
|
||||||
|
configureLocalSocketFactories(env, serverAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the non-SSL socket factories for the local JMX.
|
||||||
|
*
|
||||||
|
* @param env output param containing the configured socket factories
|
||||||
|
* @param serverAddress the JMX server is bound to
|
||||||
|
*/
|
||||||
|
abstract public void configureLocalSocketFactories(Map<String, Object> env, InetAddress serverAddress);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures SSL based client socket factory.
|
||||||
|
*
|
||||||
|
* @param env output param containing the configured socket factories
|
||||||
|
* @param serverAddress the JMX server is bound to
|
||||||
|
*/
|
||||||
|
abstract public void configureSslClientSocketFactory(Map<String, Object> env, InetAddress serverAddress);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures SSL based server socket factory based on system config for key/trust stores.
|
||||||
|
*
|
||||||
|
* @param env output param containing the configured socket factories
|
||||||
|
* @param serverAddress the JMX server is bound to
|
||||||
|
* @param enabledCipherSuites for the SSL communication
|
||||||
|
* @param enabledProtocols for the SSL communication
|
||||||
|
* @param needClientAuth {@code true} if it requires the client-auth; {@code false} otherwise
|
||||||
|
*/
|
||||||
|
abstract public void configureSslServerSocketFactory(Map<String, Object> env, InetAddress serverAddress,
|
||||||
|
String[] enabledCipherSuites, String[] enabledProtocols,
|
||||||
|
boolean needClientAuth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures SSL based server socket factory based on custom SSLContext.
|
||||||
|
*
|
||||||
|
* @param env output param containing the configured socket factories
|
||||||
|
* @param serverAddress the JMX server is bound to
|
||||||
|
* @param enabledCipherSuites for the SSL communication
|
||||||
|
* @param enabledProtocols for the SSL communication
|
||||||
|
* @param needClientAuth {@code true} if it requires the client-auth; {@code false} otherwise
|
||||||
|
* @param sslContext for the SSL communication
|
||||||
|
*/
|
||||||
|
abstract public void configureSslServerSocketFactory(Map<String, Object> env, InetAddress serverAddress,
|
||||||
|
String[] enabledCipherSuites, String[] enabledProtocols,
|
||||||
|
boolean needClientAuth, SSLContext sslContext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the following JMX system properties.
|
||||||
|
* <pre>
|
||||||
|
* com.sun.management.jmxremote.ssl=true
|
||||||
|
* javax.rmi.ssl.client.enabledCipherSuites=<applicable cipher suites provided in the configuration>
|
||||||
|
* javax.rmi.ssl.client.enabledProtocols=<applicable protocols provided in the configuration>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param jmxEncryptionOptions for the SSL communication
|
||||||
|
*/
|
||||||
|
private void setJmxSystemProperties(EncryptionOptions jmxEncryptionOptions)
|
||||||
|
{
|
||||||
|
COM_SUN_MANAGEMENT_JMXREMOTE_SSL.setBoolean(true);
|
||||||
|
if (jmxEncryptionOptions.getAcceptedProtocols() != null)
|
||||||
|
JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS.setString(StringUtils.join(jmxEncryptionOptions.getAcceptedProtocols(), ","));
|
||||||
|
|
||||||
|
if (jmxEncryptionOptions.cipherSuitesArray() != null)
|
||||||
|
JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES.setString(StringUtils.join(jmxEncryptionOptions.cipherSuitesArray(), ","));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.utils.jmx;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.management.remote.rmi.RMIConnectorServer;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.rmi.ssl.SslRMIClientSocketFactory;
|
||||||
|
import javax.rmi.ssl.SslRMIServerSocketFactory;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.apache.cassandra.utils.RMIServerSocketFactoryImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of the JMX Socket factory.
|
||||||
|
*/
|
||||||
|
public final class DefaultJmxSocketFactory extends AbstractJmxSocketFactory
|
||||||
|
{
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(DefaultJmxSocketFactory.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureLocalSocketFactories(Map<String, Object> env, InetAddress serverAddress)
|
||||||
|
{
|
||||||
|
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, new RMIServerSocketFactoryImpl(serverAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureSslClientSocketFactory(Map<String, Object> env, InetAddress serverAddress)
|
||||||
|
{
|
||||||
|
SslRMIClientSocketFactory clientFactory = new SslRMIClientSocketFactory();
|
||||||
|
env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, clientFactory);
|
||||||
|
env.put("com.sun.jndi.rmi.factory.socket", clientFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureSslServerSocketFactory(Map<String, Object> env, InetAddress serverAddress, String[] enabledCipherSuites,
|
||||||
|
String[] enabledProtocols, boolean needClientAuth)
|
||||||
|
{
|
||||||
|
SslRMIServerSocketFactory serverFactory = new SslRMIServerSocketFactory(enabledCipherSuites, enabledProtocols, needClientAuth);
|
||||||
|
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverFactory);
|
||||||
|
logJmxSslConfig(serverFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureSslServerSocketFactory(Map<String, Object> env, InetAddress serverAddress, String[] enabledCipherSuites,
|
||||||
|
String[] enabledProtocols, boolean needClientAuth, SSLContext sslContext)
|
||||||
|
{
|
||||||
|
SslRMIServerSocketFactory serverFactory = new SslRMIServerSocketFactory(sslContext, enabledCipherSuites, enabledProtocols, needClientAuth);
|
||||||
|
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverFactory);
|
||||||
|
logJmxSslConfig(serverFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logJmxSslConfig(SslRMIServerSocketFactory serverFactory)
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
logger.debug("JMX SSL configuration. { protocols: [{}], cipher_suites: [{}], require_client_auth: {} }",
|
||||||
|
serverFactory.getEnabledProtocols() == null ? "'JVM defaults'" : Arrays.stream(serverFactory.getEnabledProtocols()).collect(Collectors.joining("','", "'", "'")),
|
||||||
|
serverFactory.getEnabledCipherSuites() == null ? "'JVM defaults'" : Arrays.stream(serverFactory.getEnabledCipherSuites()).collect(Collectors.joining("','", "'", "'")),
|
||||||
|
serverFactory.getNeedClientAuth());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
#
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Testing for pluggable ssl_context_factory option for client and server encryption options with a valid and a missing
|
||||||
|
# implementation classes.
|
||||||
|
#
|
||||||
|
cluster_name: Test Cluster
|
||||||
|
# memtable_allocation_type: heap_buffers
|
||||||
|
memtable_allocation_type: offheap_objects
|
||||||
|
commitlog_sync: batch
|
||||||
|
commitlog_segment_size: 5MiB
|
||||||
|
commitlog_directory: build/test/cassandra/commitlog
|
||||||
|
# commitlog_compression:
|
||||||
|
# - class_name: LZ4Compressor
|
||||||
|
cdc_raw_directory: build/test/cassandra/cdc_raw
|
||||||
|
cdc_enabled: false
|
||||||
|
hints_directory: build/test/cassandra/hints
|
||||||
|
partitioner: org.apache.cassandra.dht.ByteOrderedPartitioner
|
||||||
|
listen_address: 127.0.0.1
|
||||||
|
storage_port: 7012
|
||||||
|
ssl_storage_port: 17012
|
||||||
|
start_native_transport: true
|
||||||
|
native_transport_port: 9042
|
||||||
|
column_index_size: 4KiB
|
||||||
|
saved_caches_directory: build/test/cassandra/saved_caches
|
||||||
|
data_file_directories:
|
||||||
|
- build/test/cassandra/data
|
||||||
|
disk_access_mode: mmap_index_only
|
||||||
|
seed_provider:
|
||||||
|
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
|
||||||
|
parameters:
|
||||||
|
- seeds: "127.0.0.1:7012"
|
||||||
|
endpoint_snitch: org.apache.cassandra.locator.SimpleSnitch
|
||||||
|
dynamic_snitch: true
|
||||||
|
jmx_encryption_options:
|
||||||
|
enabled: false
|
||||||
|
cipher_suites: [TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]
|
||||||
|
accepted_protocols: [TLSv1.2,TLSv1.3,TLSv1.1]
|
||||||
|
ssl_context_factory:
|
||||||
|
class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
|
||||||
|
parameters:
|
||||||
|
private_key: |
|
||||||
|
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||||
|
MIIE6jAcBgoqhkiG9w0BDAEDMA4ECOWqSzq5PBIdAgIFxQSCBMjXsCK30J0aT3J/
|
||||||
|
g5kcbmevTOY1pIhJGbf5QYYrMUPiuDK2ydxIbiPzoTE4/S+OkCeHhlqwn/YydpBl
|
||||||
|
xgjZZ1Z5rLJHO27d2biuESqanDiBVXYuVmHmaifRnFy0uUTFkStB5mjVZEiJgO29
|
||||||
|
L83hL60uWru71EVuVriC2WCfmZ/EXp6wyYszOqCFQ8Quk/rDO6XuaBl467MJbx5V
|
||||||
|
sucGT6E9XKNd9hB14/Izb2jtVM5kqKxoiHpz1na6yhEYJiE5D1uOonznWjBnjwB/
|
||||||
|
f0x+acpDfVDoJKTlRdz+DEcbOF7mb9lBVVjP6P/AAsmQzz6JKwHjvCrjYfQmyyN8
|
||||||
|
RI4KRQnWgm4L3dtByLqY8HFU4ogisCMCgI+hZQ+OKMz/hoRO540YGiPcTRY3EOUR
|
||||||
|
0bd5JxU6tCJDMTqKP9aSL2KmLoiLowdMkSPz7TCzLsZ2bGJemuCfpAs4XT1vXCHs
|
||||||
|
evrUbOnh8et1IA8mZ9auThfqsZtNagJLEXA6hWIKp1FfVL3Q49wvMKZt4eTn/zwU
|
||||||
|
tLL0m5yPo6/HAaOA3hbm/oghZS0dseshXl7PZrmZQtvYnIvjyoxEL7ducYDQCDP6
|
||||||
|
wZ7Nzyh1QZAauSS15hl3vLFRZCA9hWAVgwQAviTvhB342O0i9qI7TQkcHk+qcTPN
|
||||||
|
K+iGNbFZ8ma1izXNKSJ2PgI/QqFNIeJWvZrb9PhJRmaZVsTJ9fERm1ewpebZqkVv
|
||||||
|
zMqMhlKgx9ggAaSKgnGZkwXwB6GrSbbzUrwRCKm3FieD1QE4VVYevaadVUU75GG5
|
||||||
|
mrFKorJEH7kFZlic8OTjDksYnHbcgU36XZrGEXa2+ldVeGKL3CsXWciaQRcJg8yo
|
||||||
|
WQDjZpcutGI0eMJWCqUkv8pYZC2/wZU4htCve5nVJUU4t9uuo9ex7lnwlLWPvheQ
|
||||||
|
jUBMgzSRsZ+zwaIusvufAAxiKK/cJm4ubZSZPIjBbfd4U7VPxtirP4Accydu7EK6
|
||||||
|
eG/MZwtAMFNJxfxUR+/aYzJU/q1ePw7fWVHrpt58t/22CX2SJBEiUGmSmuyER4Ny
|
||||||
|
DPw6d6mhvPUS1jRhIZ9A81ht8MOX7VL5uVp307rt7o5vRpV1mo0iPiRHzGscMpJn
|
||||||
|
AP36klEAUNTf0uLTKZa7KHiwhn5iPmsCrENHkOKJjxhRrqHjD2wy3YHs3ow2voyY
|
||||||
|
Ua4Cids+c1hvRkNEDGNHm4+rKGFOGOsG/ZU7uj/6gflO4JXxNGiyTLflqMdWBvow
|
||||||
|
Zd7hk1zCaGAAn8nZ0hPweGxQ4Q30I9IBZrimGxB0vjiUqNio9+qMf33dCHFJEuut
|
||||||
|
ZGJMaUGVaPhXQcTy4uD5hzsPZV5xcsU4H3vBYyBcZgrusJ6OOgkuZQaU7p8rWQWr
|
||||||
|
bUEVbXuZdwEmxsCe7H/vEVv5+aA4sF4kWnMMFL7/LIYaiEzkTqdJlRv/KyJJgcAH
|
||||||
|
hg2BvR3XTAq8wiX0C98CdmTbsx2eyQdj5tCU606rEohFLKUxWkJYAKxCiUbxGGpI
|
||||||
|
RheVmxkef9ErxJiq7hsAsGrSJvMtJuDKIasnD14SOEwD/7jRAq6WdL9VLpxtzlOw
|
||||||
|
pWnIl8kUCO3WoaG9Jf+ZTIv2hnxJhaSzYrdXzGPNnaWKhBlwnXJRvQEdrIxZOimP
|
||||||
|
FujZhqbKUDbYAcqTkoQ=
|
||||||
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
|
||||||
|
bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
|
||||||
|
VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
|
||||||
|
Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
|
||||||
|
EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
|
||||||
|
a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
|
||||||
|
FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
|
MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
|
||||||
|
ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
|
||||||
|
q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
|
||||||
|
TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
|
||||||
|
TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
|
||||||
|
YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
|
||||||
|
N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
|
||||||
|
iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
|
||||||
|
IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
|
||||||
|
6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
|
||||||
|
qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
|
||||||
|
HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
|
||||||
|
n3MVF9w=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
private_key_password: "cassandra"
|
||||||
|
trusted_certificates: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
|
||||||
|
bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
|
||||||
|
VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
|
||||||
|
Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
|
||||||
|
EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
|
||||||
|
a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
|
||||||
|
FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
|
MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
|
||||||
|
ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
|
||||||
|
q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
|
||||||
|
TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
|
||||||
|
TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
|
||||||
|
YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
|
||||||
|
N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
|
||||||
|
iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
|
||||||
|
IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
|
||||||
|
6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
|
||||||
|
qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
|
||||||
|
HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
|
||||||
|
n3MVF9w=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
incremental_backups: true
|
||||||
|
concurrent_compactors: 4
|
||||||
|
compaction_throughput: 0MiB/s
|
||||||
|
row_cache_class_name: org.apache.cassandra.cache.OHCProvider
|
||||||
|
row_cache_size: 16MiB
|
||||||
|
user_defined_functions_enabled: true
|
||||||
|
scripted_user_defined_functions_enabled: false
|
||||||
|
prepared_statements_cache_size: 1MiB
|
||||||
|
corrupted_tombstone_strategy: exception
|
||||||
|
stream_entire_sstables: true
|
||||||
|
stream_throughput_outbound: 24MiB/s
|
||||||
|
sasi_indexes_enabled: true
|
||||||
|
materialized_views_enabled: true
|
||||||
|
file_cache_enabled: true
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
#
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Testing for pluggable ssl_context_factory option for client and server encryption options with a valid and a missing
|
||||||
|
# implementation classes.
|
||||||
|
#
|
||||||
|
cluster_name: Test Cluster
|
||||||
|
# memtable_allocation_type: heap_buffers
|
||||||
|
memtable_allocation_type: offheap_objects
|
||||||
|
commitlog_sync: batch
|
||||||
|
commitlog_segment_size: 5MiB
|
||||||
|
commitlog_directory: build/test/cassandra/commitlog
|
||||||
|
# commitlog_compression:
|
||||||
|
# - class_name: LZ4Compressor
|
||||||
|
cdc_raw_directory: build/test/cassandra/cdc_raw
|
||||||
|
cdc_enabled: false
|
||||||
|
hints_directory: build/test/cassandra/hints
|
||||||
|
partitioner: org.apache.cassandra.dht.ByteOrderedPartitioner
|
||||||
|
listen_address: 127.0.0.1
|
||||||
|
storage_port: 7012
|
||||||
|
ssl_storage_port: 17012
|
||||||
|
start_native_transport: true
|
||||||
|
native_transport_port: 9042
|
||||||
|
column_index_size: 4KiB
|
||||||
|
saved_caches_directory: build/test/cassandra/saved_caches
|
||||||
|
data_file_directories:
|
||||||
|
- build/test/cassandra/data
|
||||||
|
disk_access_mode: mmap_index_only
|
||||||
|
seed_provider:
|
||||||
|
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
|
||||||
|
parameters:
|
||||||
|
- seeds: "127.0.0.1:7012"
|
||||||
|
endpoint_snitch: org.apache.cassandra.locator.SimpleSnitch
|
||||||
|
dynamic_snitch: true
|
||||||
|
jmx_encryption_options:
|
||||||
|
enabled: true
|
||||||
|
cipher_suites: [TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]
|
||||||
|
accepted_protocols: [TLSv1.2,TLSv1.3,TLSv1.1]
|
||||||
|
ssl_context_factory:
|
||||||
|
class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
|
||||||
|
parameters:
|
||||||
|
private_key: |
|
||||||
|
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||||
|
MIIE6jAcBgoqhkiG9w0BDAEDMA4ECOWqSzq5PBIdAgIFxQSCBMjXsCK30J0aT3J/
|
||||||
|
g5kcbmevTOY1pIhJGbf5QYYrMUPiuDK2ydxIbiPzoTE4/S+OkCeHhlqwn/YydpBl
|
||||||
|
xgjZZ1Z5rLJHO27d2biuESqanDiBVXYuVmHmaifRnFy0uUTFkStB5mjVZEiJgO29
|
||||||
|
L83hL60uWru71EVuVriC2WCfmZ/EXp6wyYszOqCFQ8Quk/rDO6XuaBl467MJbx5V
|
||||||
|
sucGT6E9XKNd9hB14/Izb2jtVM5kqKxoiHpz1na6yhEYJiE5D1uOonznWjBnjwB/
|
||||||
|
f0x+acpDfVDoJKTlRdz+DEcbOF7mb9lBVVjP6P/AAsmQzz6JKwHjvCrjYfQmyyN8
|
||||||
|
RI4KRQnWgm4L3dtByLqY8HFU4ogisCMCgI+hZQ+OKMz/hoRO540YGiPcTRY3EOUR
|
||||||
|
0bd5JxU6tCJDMTqKP9aSL2KmLoiLowdMkSPz7TCzLsZ2bGJemuCfpAs4XT1vXCHs
|
||||||
|
evrUbOnh8et1IA8mZ9auThfqsZtNagJLEXA6hWIKp1FfVL3Q49wvMKZt4eTn/zwU
|
||||||
|
tLL0m5yPo6/HAaOA3hbm/oghZS0dseshXl7PZrmZQtvYnIvjyoxEL7ducYDQCDP6
|
||||||
|
wZ7Nzyh1QZAauSS15hl3vLFRZCA9hWAVgwQAviTvhB342O0i9qI7TQkcHk+qcTPN
|
||||||
|
K+iGNbFZ8ma1izXNKSJ2PgI/QqFNIeJWvZrb9PhJRmaZVsTJ9fERm1ewpebZqkVv
|
||||||
|
zMqMhlKgx9ggAaSKgnGZkwXwB6GrSbbzUrwRCKm3FieD1QE4VVYevaadVUU75GG5
|
||||||
|
mrFKorJEH7kFZlic8OTjDksYnHbcgU36XZrGEXa2+ldVeGKL3CsXWciaQRcJg8yo
|
||||||
|
WQDjZpcutGI0eMJWCqUkv8pYZC2/wZU4htCve5nVJUU4t9uuo9ex7lnwlLWPvheQ
|
||||||
|
jUBMgzSRsZ+zwaIusvufAAxiKK/cJm4ubZSZPIjBbfd4U7VPxtirP4Accydu7EK6
|
||||||
|
eG/MZwtAMFNJxfxUR+/aYzJU/q1ePw7fWVHrpt58t/22CX2SJBEiUGmSmuyER4Ny
|
||||||
|
DPw6d6mhvPUS1jRhIZ9A81ht8MOX7VL5uVp307rt7o5vRpV1mo0iPiRHzGscMpJn
|
||||||
|
AP36klEAUNTf0uLTKZa7KHiwhn5iPmsCrENHkOKJjxhRrqHjD2wy3YHs3ow2voyY
|
||||||
|
Ua4Cids+c1hvRkNEDGNHm4+rKGFOGOsG/ZU7uj/6gflO4JXxNGiyTLflqMdWBvow
|
||||||
|
Zd7hk1zCaGAAn8nZ0hPweGxQ4Q30I9IBZrimGxB0vjiUqNio9+qMf33dCHFJEuut
|
||||||
|
ZGJMaUGVaPhXQcTy4uD5hzsPZV5xcsU4H3vBYyBcZgrusJ6OOgkuZQaU7p8rWQWr
|
||||||
|
bUEVbXuZdwEmxsCe7H/vEVv5+aA4sF4kWnMMFL7/LIYaiEzkTqdJlRv/KyJJgcAH
|
||||||
|
hg2BvR3XTAq8wiX0C98CdmTbsx2eyQdj5tCU606rEohFLKUxWkJYAKxCiUbxGGpI
|
||||||
|
RheVmxkef9ErxJiq7hsAsGrSJvMtJuDKIasnD14SOEwD/7jRAq6WdL9VLpxtzlOw
|
||||||
|
pWnIl8kUCO3WoaG9Jf+ZTIv2hnxJhaSzYrdXzGPNnaWKhBlwnXJRvQEdrIxZOimP
|
||||||
|
FujZhqbKUDbYAcqTkoQ=
|
||||||
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
|
||||||
|
bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
|
||||||
|
VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
|
||||||
|
Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
|
||||||
|
EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
|
||||||
|
a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
|
||||||
|
FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
|
MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
|
||||||
|
ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
|
||||||
|
q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
|
||||||
|
TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
|
||||||
|
TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
|
||||||
|
YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
|
||||||
|
N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
|
||||||
|
iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
|
||||||
|
IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
|
||||||
|
6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
|
||||||
|
qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
|
||||||
|
HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
|
||||||
|
n3MVF9w=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
private_key_password: "cassandra"
|
||||||
|
trusted_certificates: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDkTCCAnmgAwIBAgIETxH5JDANBgkqhkiG9w0BAQsFADB5MRAwDgYDVQQGEwdV
|
||||||
|
bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
|
||||||
|
VQQKEwdVbmtub3duMRQwEgYDVQQLDAtzc2xfdGVzdGluZzEZMBcGA1UEAxMQQXBh
|
||||||
|
Y2hlIENhc3NhbmRyYTAeFw0xNjAzMTgyMTI4MDJaFw0xNjA2MTYyMTI4MDJaMHkx
|
||||||
|
EDAOBgNVBAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vu
|
||||||
|
a25vd24xEDAOBgNVBAoTB1Vua25vd24xFDASBgNVBAsMC3NzbF90ZXN0aW5nMRkw
|
||||||
|
FwYDVQQDExBBcGFjaGUgQ2Fzc2FuZHJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
|
MIIBCgKCAQEAjkmVX/HS49cS8Hn6o26IGwMIcEV3d7ZhH0GNcx8rnSRd10dU9F6d
|
||||||
|
ugSjbwGFMcWUQzYNejN6az0Wb8JIQyXRPTWjfgaWTyVGr0bGTnxg6vwhzfI/9jzy
|
||||||
|
q59xv29OuSY1dxmY31f0pZ9OOw3mabWksjoO2TexfKoxqsRHJ8PrM1f8E84Z4xo2
|
||||||
|
TJXGzpuIxRkAJ+sVDqKEAhrKAfRYMSgdJ7zRt8VXv9ngjX20uA2m092NcH0Kmeto
|
||||||
|
TmuWUtK8E/qcN7ULN8xRWNUn4hu6mG6mayk4XliGRqI1VZupqh+MgNqHznuTd0bA
|
||||||
|
YrQsFPw9HaZ2hvVnJffJ5l7njAekZNOL+wIDAQABoyEwHzAdBgNVHQ4EFgQUcdiD
|
||||||
|
N6aylI91kAd34Hl2AzWY51QwDQYJKoZIhvcNAQELBQADggEBAG9q29ilUgCWQP5v
|
||||||
|
iHkZHj10gXGEoMkdfrPBf8grC7dpUcaw1Qfku/DJ7kPvMALeEsmFDk/t78roeNbh
|
||||||
|
IYBLJlzI1HZN6VPtpWQGsqxltAy5XN9Xw9mQM/tu70ShgsodGmE1UoW6eE5+/GMv
|
||||||
|
6Fg+zLuICPvs2cFNmWUvukN5LW146tJSYCv0Q/rCPB3m9dNQ9pBxrzPUHXw4glwG
|
||||||
|
qGnGddXmOC+tSW5lDLLG1BRbKv4zxv3UlrtIjqlJtZb/sQMT6WtG2ihAz7SKOBHa
|
||||||
|
HOWUwuPTetWIuJCKP7P4mWWtmSmjLy+BFX5seNEngn3RzJ2L8uuTJQ/88OsqgGru
|
||||||
|
n3MVF9w=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
incremental_backups: true
|
||||||
|
concurrent_compactors: 4
|
||||||
|
compaction_throughput: 0MiB/s
|
||||||
|
row_cache_class_name: org.apache.cassandra.cache.OHCProvider
|
||||||
|
row_cache_size: 16MiB
|
||||||
|
user_defined_functions_enabled: true
|
||||||
|
scripted_user_defined_functions_enabled: false
|
||||||
|
prepared_statements_cache_size: 1MiB
|
||||||
|
corrupted_tombstone_strategy: exception
|
||||||
|
stream_entire_sstables: true
|
||||||
|
stream_throughput_outbound: 24MiB/s
|
||||||
|
sasi_indexes_enabled: true
|
||||||
|
materialized_views_enabled: true
|
||||||
|
file_cache_enabled: true
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
#
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Testing for pluggable ssl_context_factory option for client and server encryption options with a valid and a missing
|
||||||
|
# implementation classes.
|
||||||
|
#
|
||||||
|
cluster_name: Test Cluster
|
||||||
|
# memtable_allocation_type: heap_buffers
|
||||||
|
memtable_allocation_type: offheap_objects
|
||||||
|
commitlog_sync: batch
|
||||||
|
commitlog_segment_size: 5MiB
|
||||||
|
commitlog_directory: build/test/cassandra/commitlog
|
||||||
|
# commitlog_compression:
|
||||||
|
# - class_name: LZ4Compressor
|
||||||
|
cdc_raw_directory: build/test/cassandra/cdc_raw
|
||||||
|
cdc_enabled: false
|
||||||
|
hints_directory: build/test/cassandra/hints
|
||||||
|
partitioner: org.apache.cassandra.dht.ByteOrderedPartitioner
|
||||||
|
listen_address: 127.0.0.1
|
||||||
|
storage_port: 7012
|
||||||
|
ssl_storage_port: 17012
|
||||||
|
start_native_transport: true
|
||||||
|
native_transport_port: 9042
|
||||||
|
column_index_size: 4KiB
|
||||||
|
saved_caches_directory: build/test/cassandra/saved_caches
|
||||||
|
data_file_directories:
|
||||||
|
- build/test/cassandra/data
|
||||||
|
disk_access_mode: mmap_index_only
|
||||||
|
seed_provider:
|
||||||
|
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
|
||||||
|
parameters:
|
||||||
|
- seeds: "127.0.0.1:7012"
|
||||||
|
endpoint_snitch: org.apache.cassandra.locator.SimpleSnitch
|
||||||
|
dynamic_snitch: true
|
||||||
|
jmx_encryption_options:
|
||||||
|
enabled: true
|
||||||
|
cipher_suites: [TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]
|
||||||
|
accepted_protocols: [TLSv1.2,TLSv1.3,TLSv1.1]
|
||||||
|
keystore: test/conf/cassandra_ssl_test.keystore
|
||||||
|
keystore_password: cassandra
|
||||||
|
truststore: test/conf/cassandra_ssl_test.truststore
|
||||||
|
truststore_password: cassandra
|
||||||
|
incremental_backups: true
|
||||||
|
concurrent_compactors: 4
|
||||||
|
compaction_throughput: 0MiB/s
|
||||||
|
row_cache_class_name: org.apache.cassandra.cache.OHCProvider
|
||||||
|
row_cache_size: 16MiB
|
||||||
|
user_defined_functions_enabled: true
|
||||||
|
scripted_user_defined_functions_enabled: false
|
||||||
|
prepared_statements_cache_size: 1MiB
|
||||||
|
corrupted_tombstone_strategy: exception
|
||||||
|
stream_entire_sstables: true
|
||||||
|
stream_throughput_outbound: 24MiB/s
|
||||||
|
sasi_indexes_enabled: true
|
||||||
|
materialized_views_enabled: true
|
||||||
|
file_cache_enabled: true
|
||||||
|
|
@ -22,20 +22,21 @@ import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.rmi.server.RMIServerSocketFactory;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.net.ServerSocketFactory;
|
import javax.net.ServerSocketFactory;
|
||||||
|
|
||||||
|
import org.apache.cassandra.utils.RMICloseableServerSocketFactory;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is used to keep track of RMI servers created during a cluster creation so we can
|
* This class is used to keep track of RMI servers created during a cluster creation so we can
|
||||||
* later close the sockets, which would otherwise be left with a thread running waiting for
|
* later close the sockets, which would otherwise be left with a thread running waiting for
|
||||||
* connections that would never show up as the server was otherwise closed.
|
* connections that would never show up as the server was otherwise closed.
|
||||||
*/
|
*/
|
||||||
class CollectingRMIServerSocketFactoryImpl implements RMIServerSocketFactory
|
class CollectingRMIServerSocketFactoryImpl implements RMICloseableServerSocketFactory
|
||||||
{
|
{
|
||||||
private final InetAddress bindAddress;
|
private final InetAddress bindAddress;
|
||||||
List<ServerSocket> sockets = new ArrayList<>();
|
List<ServerSocket> sockets = new ArrayList<>();
|
||||||
|
|
@ -62,7 +63,7 @@ class CollectingRMIServerSocketFactoryImpl implements RMIServerSocketFactory
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void close() throws IOException
|
public void close() throws IOException
|
||||||
{
|
{
|
||||||
for (ServerSocket socket : sockets)
|
for (ServerSocket socket : sockets)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.distributed.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
|
||||||
|
import org.apache.cassandra.utils.RMICloseableServerSocketFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to keep track of SSL based RMI servers created during a cluster creation to
|
||||||
|
* later close the sockets, which would otherwise be left with a thread running waiting for
|
||||||
|
* connections that would never show up as the server was otherwise closed.
|
||||||
|
*/
|
||||||
|
class CollectingSslRMIServerSocketFactoryImpl implements RMICloseableServerSocketFactory
|
||||||
|
{
|
||||||
|
private final InetAddress bindAddress;
|
||||||
|
private final String[] enabledCipherSuites;
|
||||||
|
private final String[] enabledProtocols;
|
||||||
|
private final boolean needClientAuth;
|
||||||
|
private final SSLSocketFactory sslSocketFactory;
|
||||||
|
List<ServerSocket> sockets = new ArrayList<>();
|
||||||
|
|
||||||
|
public CollectingSslRMIServerSocketFactoryImpl(InetAddress bindAddress, String[] enabledCipherSuites,
|
||||||
|
String[] enabledProtocols, boolean needClientAuth, SSLContext sslContext)
|
||||||
|
{
|
||||||
|
this.bindAddress = bindAddress;
|
||||||
|
this.enabledCipherSuites = enabledCipherSuites;
|
||||||
|
this.enabledProtocols = enabledProtocols;
|
||||||
|
this.needClientAuth = needClientAuth;
|
||||||
|
this.sslSocketFactory = sslContext.getSocketFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CollectingSslRMIServerSocketFactoryImpl(InetAddress bindAddress, String[] enabledCipherSuites,
|
||||||
|
String[] enabledProtocols, boolean needClientAuth)
|
||||||
|
{
|
||||||
|
this.bindAddress = bindAddress;
|
||||||
|
this.enabledCipherSuites = enabledCipherSuites;
|
||||||
|
this.enabledProtocols = enabledProtocols;
|
||||||
|
this.needClientAuth = needClientAuth;
|
||||||
|
this.sslSocketFactory = getDefaultSSLSocketFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getEnabledCipherSuites()
|
||||||
|
{
|
||||||
|
return enabledCipherSuites;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getEnabledProtocols()
|
||||||
|
{
|
||||||
|
return enabledProtocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNeedClientAuth()
|
||||||
|
{
|
||||||
|
return needClientAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerSocket createServerSocket(int pPort) throws IOException
|
||||||
|
{
|
||||||
|
ServerSocket result = createSslServerSocket(pPort);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result.setReuseAddress(true);
|
||||||
|
}
|
||||||
|
catch (SocketException e)
|
||||||
|
{
|
||||||
|
result.close();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
sockets.add(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServerSocket createSslServerSocket(int pPort) throws IOException
|
||||||
|
{
|
||||||
|
return new ServerSocket(pPort, 0, bindAddress)
|
||||||
|
{
|
||||||
|
public Socket accept() throws IOException
|
||||||
|
{
|
||||||
|
Socket socket = super.accept();
|
||||||
|
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
|
||||||
|
socket, socket.getInetAddress().getHostName(),
|
||||||
|
socket.getPort(), true);
|
||||||
|
sslSocket.setUseClientMode(false);
|
||||||
|
if (enabledCipherSuites != null)
|
||||||
|
{
|
||||||
|
sslSocket.setEnabledCipherSuites(enabledCipherSuites);
|
||||||
|
}
|
||||||
|
if (enabledProtocols != null)
|
||||||
|
{
|
||||||
|
sslSocket.setEnabledProtocols(enabledProtocols);
|
||||||
|
}
|
||||||
|
sslSocket.setNeedClientAuth(needClientAuth);
|
||||||
|
return sslSocket;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException
|
||||||
|
{
|
||||||
|
for (ServerSocket socket : sockets)
|
||||||
|
{
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
CollectingSslRMIServerSocketFactoryImpl that = (CollectingSslRMIServerSocketFactoryImpl) o;
|
||||||
|
return Objects.equals(bindAddress, that.bindAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(bindAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SSLSocketFactory defaultSSLSocketFactory = null;
|
||||||
|
|
||||||
|
private static synchronized SSLSocketFactory getDefaultSSLSocketFactory()
|
||||||
|
{
|
||||||
|
if (defaultSSLSocketFactory == null)
|
||||||
|
defaultSSLSocketFactory =
|
||||||
|
(SSLSocketFactory) SSLSocketFactory.getDefault();
|
||||||
|
return defaultSSLSocketFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -708,9 +708,9 @@ public class Instance extends IsolatedExecutor implements IInvokableInstance
|
||||||
assert config.networkTopology().contains(config.broadcastAddress()) : String.format("Network topology %s doesn't contain the address %s",
|
assert config.networkTopology().contains(config.broadcastAddress()) : String.format("Network topology %s doesn't contain the address %s",
|
||||||
config.networkTopology(), config.broadcastAddress());
|
config.networkTopology(), config.broadcastAddress());
|
||||||
DistributedTestSnitch.assign(config.networkTopology());
|
DistributedTestSnitch.assign(config.networkTopology());
|
||||||
|
DatabaseDescriptor.daemonInitialization();
|
||||||
if (config.has(JMX))
|
if (config.has(JMX))
|
||||||
startJmx();
|
startJmx();
|
||||||
DatabaseDescriptor.daemonInitialization();
|
|
||||||
LoggingSupportFactory.getLoggingSupport().onStartup();
|
LoggingSupportFactory.getLoggingSupport().onStartup();
|
||||||
logSystemInfo(inInstancelogger);
|
logSystemInfo(inInstancelogger);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.management.remote.JMXConnector;
|
import javax.management.remote.JMXConnector;
|
||||||
import javax.management.remote.JMXConnectorServer;
|
import javax.management.remote.JMXConnectorServer;
|
||||||
import javax.management.remote.JMXServiceURL;
|
import javax.management.remote.JMXServiceURL;
|
||||||
|
|
@ -34,12 +34,14 @@ import javax.management.remote.rmi.RMIJRMPServerImpl;
|
||||||
import com.google.common.util.concurrent.Uninterruptibles;
|
import com.google.common.util.concurrent.Uninterruptibles;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import org.apache.cassandra.config.EncryptionOptions;
|
||||||
import org.apache.cassandra.distributed.api.IInstance;
|
import org.apache.cassandra.distributed.api.IInstance;
|
||||||
import org.apache.cassandra.distributed.api.IInstanceConfig;
|
import org.apache.cassandra.distributed.api.IInstanceConfig;
|
||||||
import org.apache.cassandra.distributed.shared.JMXUtil;
|
import org.apache.cassandra.distributed.shared.JMXUtil;
|
||||||
import org.apache.cassandra.utils.JMXServerUtils;
|
import org.apache.cassandra.utils.JMXServerUtils;
|
||||||
import org.apache.cassandra.utils.MBeanWrapper;
|
import org.apache.cassandra.utils.MBeanWrapper;
|
||||||
import org.apache.cassandra.utils.RMIClientSocketFactoryImpl;
|
import org.apache.cassandra.utils.RMICloseableClientSocketFactory;
|
||||||
|
import org.apache.cassandra.utils.RMICloseableServerSocketFactory;
|
||||||
import sun.rmi.transport.tcp.TCPEndpoint;
|
import sun.rmi.transport.tcp.TCPEndpoint;
|
||||||
|
|
||||||
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVA_RMI_DGC_LEASE_VALUE_IN_JVM_DTEST;
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVA_RMI_DGC_LEASE_VALUE_IN_JVM_DTEST;
|
||||||
|
|
@ -57,17 +59,20 @@ public class IsolatedJmx
|
||||||
private JMXServerUtils.JmxRegistry registry;
|
private JMXServerUtils.JmxRegistry registry;
|
||||||
private RMIJRMPServerImpl jmxRmiServer;
|
private RMIJRMPServerImpl jmxRmiServer;
|
||||||
private MBeanWrapper.InstanceMBeanWrapper wrapper;
|
private MBeanWrapper.InstanceMBeanWrapper wrapper;
|
||||||
private RMIClientSocketFactoryImpl clientSocketFactory;
|
private RMICloseableClientSocketFactory clientSocketFactory;
|
||||||
private CollectingRMIServerSocketFactoryImpl serverSocketFactory;
|
private RMICloseableServerSocketFactory serverSocketFactory;
|
||||||
private Logger inInstancelogger;
|
private Logger inInstancelogger;
|
||||||
private IInstanceConfig config;
|
private IInstanceConfig config;
|
||||||
|
|
||||||
public IsolatedJmx(IInstance instance, Logger inInstanceLogger) {
|
public IsolatedJmx(IInstance instance, Logger inInstanceLogger)
|
||||||
|
{
|
||||||
this.config = instance.config();
|
this.config = instance.config();
|
||||||
this.inInstancelogger = inInstanceLogger;
|
this.inInstancelogger = inInstanceLogger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startJmx() {
|
@SuppressWarnings("unchecked")
|
||||||
|
public void startJmx()
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Several RMI threads hold references to in-jvm dtest objects, and are, by default, kept
|
// Several RMI threads hold references to in-jvm dtest objects, and are, by default, kept
|
||||||
|
|
@ -84,14 +89,17 @@ public class IsolatedJmx
|
||||||
String hostname = addr.getHostAddress();
|
String hostname = addr.getHostAddress();
|
||||||
wrapper = new MBeanWrapper.InstanceMBeanWrapper(hostname + ":" + jmxPort);
|
wrapper = new MBeanWrapper.InstanceMBeanWrapper(hostname + ":" + jmxPort);
|
||||||
((MBeanWrapper.DelegatingMbeanWrapper) MBeanWrapper.instance).setDelegate(wrapper);
|
((MBeanWrapper.DelegatingMbeanWrapper) MBeanWrapper.instance).setDelegate(wrapper);
|
||||||
Map<String, Object> env = new HashMap<>();
|
|
||||||
|
|
||||||
serverSocketFactory = new CollectingRMIServerSocketFactoryImpl(addr);
|
// CASSANDRA-18508: Sensitive JMX SSL configuration options can be easily exposed
|
||||||
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
|
Map<String, Object> encryptionOptionsMap = (Map<String, Object>) config.getParams().get("jmx_encryption_options");
|
||||||
serverSocketFactory);
|
EncryptionOptions jmxEncryptionOptions = getJmxEncryptionOptions(encryptionOptionsMap);
|
||||||
clientSocketFactory = new RMIClientSocketFactoryImpl(addr);
|
// Here the `localOnly` is always passed as true as it is for the local isolated JMX testing
|
||||||
env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
|
// However if the `jmxEncryptionOptions` are provided or JMX SSL configuration is set it will configure
|
||||||
clientSocketFactory);
|
// the socket factories appropriately.
|
||||||
|
Map<String, Object> socketFactories = new IsolatedJmxSocketFactory().configure(addr, true, jmxEncryptionOptions);
|
||||||
|
serverSocketFactory = (RMICloseableServerSocketFactory) socketFactories.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE);
|
||||||
|
clientSocketFactory = (RMICloseableClientSocketFactory) socketFactories.get(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE);
|
||||||
|
Map<String, Object> env = new HashMap<>(socketFactories);
|
||||||
|
|
||||||
// configure the RMI registry
|
// configure the RMI registry
|
||||||
registry = new JMXServerUtils.JmxRegistry(jmxPort,
|
registry = new JMXServerUtils.JmxRegistry(jmxPort,
|
||||||
|
|
@ -137,6 +145,49 @@ public class IsolatedJmx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds {@code EncryptionOptions} from the map based SSL configuration properties.
|
||||||
|
*
|
||||||
|
* @param encryptionOptionsMap of SSL configuration properties
|
||||||
|
* @return EncryptionOptions built object
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private EncryptionOptions getJmxEncryptionOptions(Map<String, Object> encryptionOptionsMap)
|
||||||
|
{
|
||||||
|
if (encryptionOptionsMap == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
EncryptionOptions jmxEncryptionOptions = new EncryptionOptions();
|
||||||
|
String[] cipherSuitesArray = (String[]) encryptionOptionsMap.get(EncryptionOptions.ConfigKey.CIPHER_SUITES.toString());
|
||||||
|
if (cipherSuitesArray != null)
|
||||||
|
{
|
||||||
|
jmxEncryptionOptions = jmxEncryptionOptions.withCipherSuites(cipherSuitesArray);
|
||||||
|
}
|
||||||
|
List<String> acceptedProtocols = (List<String>) encryptionOptionsMap.get(EncryptionOptions.ConfigKey.ACCEPTED_PROTOCOLS.toString());
|
||||||
|
if (acceptedProtocols != null)
|
||||||
|
{
|
||||||
|
jmxEncryptionOptions = jmxEncryptionOptions.withAcceptedProtocols(acceptedProtocols);
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean requireClientAuthValue = (Boolean) encryptionOptionsMap.get(EncryptionOptions.ConfigKey.REQUIRE_CLIENT_AUTH.toString());
|
||||||
|
EncryptionOptions.ClientAuth requireClientAuth = requireClientAuthValue == null ?
|
||||||
|
EncryptionOptions.ClientAuth.NOT_REQUIRED :
|
||||||
|
EncryptionOptions.ClientAuth.from(String.valueOf(requireClientAuthValue));
|
||||||
|
Object enabledOption = encryptionOptionsMap.get(EncryptionOptions.ConfigKey.ENABLED.toString());
|
||||||
|
boolean enabled = enabledOption != null ? (Boolean) encryptionOptionsMap.get(EncryptionOptions.ConfigKey.ENABLED.toString()) : false;
|
||||||
|
|
||||||
|
//CASSANDRA-18508 NOTE - We do not populate sslContextFactory configuration here for tests, it could be enhanced
|
||||||
|
jmxEncryptionOptions = jmxEncryptionOptions
|
||||||
|
.withKeyStore((String) encryptionOptionsMap.get(EncryptionOptions.ConfigKey.KEYSTORE.toString()))
|
||||||
|
.withKeyStorePassword((String) encryptionOptionsMap.get(EncryptionOptions.ConfigKey.KEYSTORE_PASSWORD.toString()))
|
||||||
|
.withTrustStore((String) encryptionOptionsMap.get(EncryptionOptions.ConfigKey.TRUSTSTORE.toString()))
|
||||||
|
.withTrustStorePassword((String) encryptionOptionsMap.get(EncryptionOptions.ConfigKey.TRUSTSTORE_PASSWORD.toString()))
|
||||||
|
.withRequireClientAuth(requireClientAuth)
|
||||||
|
.withEnabled(enabled);
|
||||||
|
return jmxEncryptionOptions;
|
||||||
|
}
|
||||||
|
|
||||||
private void waitForJmxAvailability(Map<String, ?> env)
|
private void waitForJmxAvailability(Map<String, ?> env)
|
||||||
{
|
{
|
||||||
try (JMXConnector ignored = JMXUtil.getJmxConnector(config, 20, env))
|
try (JMXConnector ignored = JMXUtil.getJmxConnector(config, 20, env))
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.distributed.impl;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.management.remote.rmi.RMIConnectorServer;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.apache.cassandra.config.CassandraRelevantProperties;
|
||||||
|
import org.apache.cassandra.utils.RMIClientSocketFactoryImpl;
|
||||||
|
import org.apache.cassandra.utils.jmx.AbstractJmxSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JMX Socket factory used for the isolated JMX testing.
|
||||||
|
*/
|
||||||
|
public class IsolatedJmxSocketFactory extends AbstractJmxSocketFactory
|
||||||
|
{
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(IsolatedJmxSocketFactory.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureLocalSocketFactories(Map<String, Object> env, InetAddress serverAddress)
|
||||||
|
{
|
||||||
|
CollectingRMIServerSocketFactoryImpl serverSocketFactory = new CollectingRMIServerSocketFactoryImpl(serverAddress);
|
||||||
|
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
|
||||||
|
serverSocketFactory);
|
||||||
|
RMIClientSocketFactoryImpl clientSocketFactory = new RMIClientSocketFactoryImpl(serverAddress);
|
||||||
|
env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
|
||||||
|
clientSocketFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureSslClientSocketFactory(Map<String, Object> env, InetAddress serverAddress)
|
||||||
|
{
|
||||||
|
RMISslClientSocketFactoryImpl clientFactory = new RMISslClientSocketFactoryImpl(serverAddress,
|
||||||
|
CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES.getString(),
|
||||||
|
CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS.getString());
|
||||||
|
env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, clientFactory);
|
||||||
|
env.put("com.sun.jndi.rmi.factory.socket", clientFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureSslServerSocketFactory(Map<String, Object> env, InetAddress serverAddress, String[] enabledCipherSuites,
|
||||||
|
String[] enabledProtocols, boolean needClientAuth)
|
||||||
|
{
|
||||||
|
CollectingSslRMIServerSocketFactoryImpl serverFactory = new CollectingSslRMIServerSocketFactoryImpl(serverAddress,
|
||||||
|
enabledCipherSuites,
|
||||||
|
enabledProtocols,
|
||||||
|
needClientAuth);
|
||||||
|
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverFactory);
|
||||||
|
logJmxSslConfig(serverFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureSslServerSocketFactory(Map<String, Object> env, InetAddress serverAddress, String[] enabledCipherSuites,
|
||||||
|
String[] enabledProtocols, boolean needClientAuth, SSLContext sslContext)
|
||||||
|
{
|
||||||
|
CollectingSslRMIServerSocketFactoryImpl serverFactory = new CollectingSslRMIServerSocketFactoryImpl(serverAddress,
|
||||||
|
enabledCipherSuites,
|
||||||
|
enabledProtocols,
|
||||||
|
needClientAuth,
|
||||||
|
sslContext);
|
||||||
|
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, serverFactory);
|
||||||
|
logJmxSslConfig(serverFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logJmxSslConfig(CollectingSslRMIServerSocketFactoryImpl serverFactory)
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
logger.debug("JMX SSL configuration. { protocols: [{}], cipher_suites: [{}], require_client_auth: {} }",
|
||||||
|
serverFactory.getEnabledProtocols() == null ? "'JVM defaults'" : Arrays.stream(serverFactory.getEnabledProtocols()).collect(Collectors.joining("','", "'", "'")),
|
||||||
|
serverFactory.getEnabledCipherSuites() == null ? "'JVM defaults'" : Arrays.stream(serverFactory.getEnabledCipherSuites()).collect(Collectors.joining("','", "'", "'")),
|
||||||
|
serverFactory.isNeedClientAuth());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.distributed.impl;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import org.apache.cassandra.config.EncryptionOptions;
|
||||||
|
import org.apache.cassandra.io.util.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplified and independent version of {@link org.apache.cassandra.security.FileBasedSslContextFactory} for
|
||||||
|
* testing SSL based JMX clients that require configuring keystore and/or truststore.
|
||||||
|
*/
|
||||||
|
public class JmxTestClientSslContextFactory
|
||||||
|
{
|
||||||
|
private final Map<String, Object> parameters;
|
||||||
|
// keystore is not needed when the JMX server does not require client-auth
|
||||||
|
private final String keystore;
|
||||||
|
// keystore could be null in case JMX server does not require client-auth
|
||||||
|
private final String keystore_password;
|
||||||
|
private final String truststore;
|
||||||
|
private final String truststore_password;
|
||||||
|
private final String protocol;
|
||||||
|
private final String algorithm;
|
||||||
|
private final String store_type;
|
||||||
|
|
||||||
|
public JmxTestClientSslContextFactory(Map<String, Object> parameters)
|
||||||
|
{
|
||||||
|
this.parameters = parameters;
|
||||||
|
keystore = getString(EncryptionOptions.ConfigKey.KEYSTORE.toString());
|
||||||
|
keystore_password = getString(EncryptionOptions.ConfigKey.KEYSTORE_PASSWORD.toString());
|
||||||
|
truststore = getString(EncryptionOptions.ConfigKey.TRUSTSTORE.toString());
|
||||||
|
truststore_password = getString(EncryptionOptions.ConfigKey.TRUSTSTORE_PASSWORD.toString());
|
||||||
|
protocol = getString(EncryptionOptions.ConfigKey.PROTOCOL.toString(), "TLS");
|
||||||
|
algorithm = getString(EncryptionOptions.ConfigKey.ALGORITHM.toString());
|
||||||
|
store_type = getString(EncryptionOptions.ConfigKey.STORE_TYPE.toString(), "JKS");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getString(String key, String defaultValue)
|
||||||
|
{
|
||||||
|
return parameters.get(key) == null ? defaultValue : (String) parameters.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getString(String key)
|
||||||
|
{
|
||||||
|
return (String) parameters.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TrustManagerFactory buildTrustManagerFactory() throws SSLException
|
||||||
|
{
|
||||||
|
try (InputStream tsf = Files.newInputStream(File.getPath(truststore)))
|
||||||
|
{
|
||||||
|
final String algorithm = this.algorithm == null ? TrustManagerFactory.getDefaultAlgorithm() : this.algorithm;
|
||||||
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
|
||||||
|
KeyStore ts = KeyStore.getInstance(store_type);
|
||||||
|
|
||||||
|
final char[] truststorePassword = StringUtils.isEmpty(truststore_password) ? null : truststore_password.toCharArray();
|
||||||
|
ts.load(tsf, truststorePassword);
|
||||||
|
tmf.init(ts);
|
||||||
|
return tmf;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new SSLException("failed to build trust manager store for secure connections", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyManagerFactory buildKeyManagerFactory() throws SSLException
|
||||||
|
{
|
||||||
|
final String algorithm = this.algorithm == null ? KeyManagerFactory.getDefaultAlgorithm() : this.algorithm;
|
||||||
|
|
||||||
|
if (keystore != null)
|
||||||
|
{
|
||||||
|
try (InputStream ksf = Files.newInputStream(File.getPath(keystore)))
|
||||||
|
{
|
||||||
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
|
||||||
|
KeyStore ks = KeyStore.getInstance(store_type);
|
||||||
|
final char[] password = keystore_password.toCharArray();
|
||||||
|
ks.load(ksf, password);
|
||||||
|
kmf.init(ks, password);
|
||||||
|
return kmf;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new SSLException("failed to build key manager store for secure connections", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SSLContext createSSLContext() throws SSLException
|
||||||
|
{
|
||||||
|
TrustManager[] trustManagers = buildTrustManagerFactory().getTrustManagers();
|
||||||
|
KeyManagerFactory kmf = buildKeyManagerFactory();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SSLContext ctx = SSLContext.getInstance(protocol);
|
||||||
|
ctx.init(kmf != null ? kmf.getKeyManagers() : null, trustManagers, null);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new SSLException("Error creating/initializing the SSL Context", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.distributed.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.net.Socket;
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import javax.rmi.ssl.SslRMIClientSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code RMIClientSocketFactory} for testing SSL based JMX clients.
|
||||||
|
*/
|
||||||
|
public class JmxTestClientSslSocketFactory extends SslRMIClientSocketFactory implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 818579127759449333L;
|
||||||
|
private final SocketFactory defaultSocketFactory;
|
||||||
|
private final String[] cipherSuites;
|
||||||
|
private final String[] acceptedProtocols;
|
||||||
|
|
||||||
|
public JmxTestClientSslSocketFactory(SSLContext sslContext, String[] cipherSuites, String[] acceptedProtocols)
|
||||||
|
{
|
||||||
|
this.cipherSuites = cipherSuites;
|
||||||
|
this.acceptedProtocols = acceptedProtocols;
|
||||||
|
defaultSocketFactory = sslContext == null ? SSLSocketFactory.getDefault() : sslContext.getSocketFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(String host, int port) throws IOException
|
||||||
|
{
|
||||||
|
final SSLSocket sslSocket = (SSLSocket) defaultSocketFactory.createSocket(host, port);
|
||||||
|
|
||||||
|
if (cipherSuites != null)
|
||||||
|
sslSocket.setEnabledCipherSuites(cipherSuites);
|
||||||
|
|
||||||
|
if (acceptedProtocols != null)
|
||||||
|
sslSocket.setEnabledProtocols(acceptedProtocols);
|
||||||
|
|
||||||
|
return sslSocket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.distributed.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
|
||||||
|
import org.apache.cassandra.utils.RMICloseableClientSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code RMIClientSocketFactory} for testing SSL based JMX clients.
|
||||||
|
* This class is used to override the local address the JMX client calculates when trying to connect,
|
||||||
|
* which can otherwise be influenced by the system property "java.rmi.server.hostname" in strange and
|
||||||
|
* unpredictable ways.
|
||||||
|
*/
|
||||||
|
public class RMISslClientSocketFactoryImpl implements Serializable, RMICloseableClientSocketFactory
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 9054380061905145241L;
|
||||||
|
private static final Pattern COMMA_SPLITTER = Pattern.compile(",");
|
||||||
|
private static final List<Socket> sockets = new ArrayList<>();
|
||||||
|
private final InetAddress localAddress;
|
||||||
|
private final String[] enabledCipherSuites;
|
||||||
|
private final String[] enabledProtocols;
|
||||||
|
|
||||||
|
public RMISslClientSocketFactoryImpl(InetAddress localAddress, String enabledCipherSuites, String enabledProtocls)
|
||||||
|
{
|
||||||
|
this.localAddress = localAddress;
|
||||||
|
this.enabledCipherSuites = splitCommaSeparatedString(enabledCipherSuites);
|
||||||
|
this.enabledProtocols = splitCommaSeparatedString(enabledProtocls);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(String host, int port) throws IOException
|
||||||
|
{
|
||||||
|
Socket socket = createSslSocket(port);
|
||||||
|
sockets.add(socket);
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Socket createSslSocket(int port) throws IOException
|
||||||
|
{
|
||||||
|
final SocketFactory sslSocketFactory = SSLSocketFactory.getDefault();
|
||||||
|
final SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(localAddress, port);
|
||||||
|
if (enabledCipherSuites != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sslSocket.setEnabledCipherSuites(enabledCipherSuites);
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e)
|
||||||
|
{
|
||||||
|
throw new IOException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (enabledProtocols != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sslSocket.setEnabledProtocols(enabledProtocols);
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e)
|
||||||
|
{
|
||||||
|
throw new IOException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sslSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException
|
||||||
|
{
|
||||||
|
for (Socket socket : sockets)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
catch (IOException ignored)
|
||||||
|
{
|
||||||
|
// intentionally ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
RMISslClientSocketFactoryImpl that = (RMISslClientSocketFactoryImpl) o;
|
||||||
|
return Objects.equals(localAddress, that.localAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(localAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] splitCommaSeparatedString(String stringToSplit)
|
||||||
|
{
|
||||||
|
if (stringToSplit == null)
|
||||||
|
return null;
|
||||||
|
return COMMA_SPLITTER.split(stringToSplit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -72,6 +72,13 @@ public final class WithProperties implements AutoCloseable
|
||||||
return set(prop, () -> prop.setLong(value));
|
return set(prop, () -> prop.setLong(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WithProperties preserve(CassandraRelevantProperties prop)
|
||||||
|
{
|
||||||
|
String previous = prop.getString(); // because all properties are strings
|
||||||
|
rollback.add(previous == null ? prop::clearValue : () -> prop.setString(previous));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private void with(String key, String value)
|
private void with(String key, String value)
|
||||||
{
|
{
|
||||||
String previous = System.setProperty(key, value); // checkstyle: suppress nearby 'blockSystemPropertyUsage'
|
String previous = System.setProperty(key, value); // checkstyle: suppress nearby 'blockSystemPropertyUsage'
|
||||||
|
|
|
||||||
|
|
@ -70,19 +70,19 @@ public class AbstractEncryptionOptionsImpl extends TestBaseImpl
|
||||||
final static String validTrustStorePassword = TlsTestUtils.SERVER_TRUSTSTORE_PASSWORD;
|
final static String validTrustStorePassword = TlsTestUtils.SERVER_TRUSTSTORE_PASSWORD;
|
||||||
|
|
||||||
// Base configuration map for a valid keystore that can be opened
|
// Base configuration map for a valid keystore that can be opened
|
||||||
final static Map<String,Object> validKeystore = ImmutableMap.of("keystore", validKeyStorePath,
|
protected final static Map<String,Object> validFileBasedKeystores = ImmutableMap.of("keystore", validKeyStorePath,
|
||||||
"keystore_password", validKeyStorePassword,
|
"keystore_password", validKeyStorePassword,
|
||||||
"truststore", validTrustStorePath,
|
"truststore", validTrustStorePath,
|
||||||
"truststore_password", validTrustStorePassword);
|
"truststore_password", validTrustStorePassword);
|
||||||
|
|
||||||
// Configuration with a valid keystore, but an unknown protocol
|
// Configuration with a valid keystore, but an unknown protocol
|
||||||
final static Map<String,Object> nonExistantProtocol = ImmutableMap.<String,Object>builder()
|
final static Map<String,Object> nonExistantProtocol = ImmutableMap.<String,Object>builder()
|
||||||
.putAll(validKeystore)
|
.putAll(validFileBasedKeystores)
|
||||||
.put("accepted_protocols", Collections.singletonList("NoProtocolIKnow"))
|
.put("accepted_protocols", Collections.singletonList("NoProtocolIKnow"))
|
||||||
.build();
|
.build();
|
||||||
// Configuration with a valid keystore, but an unknown cipher suite
|
// Configuration with a valid keystore, but an unknown cipher suite
|
||||||
final static Map<String,Object> nonExistantCipher = ImmutableMap.<String,Object>builder()
|
final static Map<String,Object> nonExistantCipher = ImmutableMap.<String,Object>builder()
|
||||||
.putAll(validKeystore)
|
.putAll(validFileBasedKeystores)
|
||||||
.put("cipher_suites", Collections.singletonList("NoCipherIKnow"))
|
.put("cipher_suites", Collections.singletonList("NoCipherIKnow"))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
@ -340,7 +340,7 @@ public class AbstractEncryptionOptionsImpl extends TestBaseImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Provde the cluster cannot start with the configured options */
|
/* Provde the cluster cannot start with the configured options */
|
||||||
void assertCannotStartDueToConfigurationException(Cluster cluster)
|
protected void assertCannotStartDueToConfigurationException(Cluster cluster)
|
||||||
{
|
{
|
||||||
Throwable tr = null;
|
Throwable tr = null;
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
||||||
c.with(Feature.NETWORK);
|
c.with(Feature.NETWORK);
|
||||||
c.set("ssl_storage_port", 7013);
|
c.set("ssl_storage_port", 7013);
|
||||||
c.set("server_encryption_options",
|
c.set("server_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("internode_encryption", "none")
|
.put("internode_encryption", "none")
|
||||||
.put("optional", false)
|
.put("optional", false)
|
||||||
.put("legacy_ssl_storage_port_enabled", "true")
|
.put("legacy_ssl_storage_port_enabled", "true")
|
||||||
|
|
@ -87,7 +87,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
||||||
{
|
{
|
||||||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
c.with(Feature.NETWORK);
|
c.with(Feature.NETWORK);
|
||||||
c.set("server_encryption_options", validKeystore);
|
c.set("server_encryption_options", validFileBasedKeystores);
|
||||||
}).createWithoutStarting())
|
}).createWithoutStarting())
|
||||||
{
|
{
|
||||||
InetAddress address = cluster.get(1).config().broadcastAddress().getAddress();
|
InetAddress address = cluster.get(1).config().broadcastAddress().getAddress();
|
||||||
|
|
@ -111,7 +111,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
||||||
c.set("storage_port", 7012);
|
c.set("storage_port", 7012);
|
||||||
c.set("ssl_storage_port", 7013);
|
c.set("ssl_storage_port", 7013);
|
||||||
c.set("server_encryption_options",
|
c.set("server_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("internode_encryption", "none")
|
.put("internode_encryption", "none")
|
||||||
.put("optional", true)
|
.put("optional", true)
|
||||||
.put("legacy_ssl_storage_port_enabled", "true")
|
.put("legacy_ssl_storage_port_enabled", "true")
|
||||||
|
|
@ -146,7 +146,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
||||||
c.set("storage_port", 7012); // must match in-jvm dtest assigned ports
|
c.set("storage_port", 7012); // must match in-jvm dtest assigned ports
|
||||||
c.set("ssl_storage_port", 7012);
|
c.set("ssl_storage_port", 7012);
|
||||||
c.set("server_encryption_options",
|
c.set("server_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("internode_encryption", "none")
|
.put("internode_encryption", "none")
|
||||||
.put("optional", true)
|
.put("optional", true)
|
||||||
.put("legacy_ssl_storage_port_enabled", "true")
|
.put("legacy_ssl_storage_port_enabled", "true")
|
||||||
|
|
@ -174,7 +174,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
||||||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
c.with(Feature.NETWORK);
|
c.with(Feature.NETWORK);
|
||||||
c.set("server_encryption_options",
|
c.set("server_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("internode_encryption", "none")
|
.put("internode_encryption", "none")
|
||||||
.put("optional", false)
|
.put("optional", false)
|
||||||
.build());
|
.build());
|
||||||
|
|
@ -202,7 +202,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
||||||
.with(Feature.GOSSIP) // To make sure AllMembersAliveMonitor checks gossip (which uses internode conns)
|
.with(Feature.GOSSIP) // To make sure AllMembersAliveMonitor checks gossip (which uses internode conns)
|
||||||
.with(Feature.NATIVE_PROTOCOL); // For virtual tables
|
.with(Feature.NATIVE_PROTOCOL); // For virtual tables
|
||||||
c.set("server_encryption_options",
|
c.set("server_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("internode_encryption", "all")
|
.put("internode_encryption", "all")
|
||||||
.build());
|
.build());
|
||||||
}).start())
|
}).start())
|
||||||
|
|
@ -234,7 +234,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
||||||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
c.with(Feature.NETWORK);
|
c.with(Feature.NETWORK);
|
||||||
c.set("server_encryption_options",
|
c.set("server_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("internode_encryption", "all")
|
.put("internode_encryption", "all")
|
||||||
.put("accepted_protocols", ImmutableList.of("TLSv1.1", "TLSv1.2", "TLSv1.3"))
|
.put("accepted_protocols", ImmutableList.of("TLSv1.1", "TLSv1.2", "TLSv1.3"))
|
||||||
.build());
|
.build());
|
||||||
|
|
@ -272,7 +272,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
||||||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
c.with(Feature.NETWORK);
|
c.with(Feature.NETWORK);
|
||||||
c.set("server_encryption_options",
|
c.set("server_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("internode_encryption", "all")
|
.put("internode_encryption", "all")
|
||||||
.put("accepted_protocols", Collections.singletonList("TLSv1.2"))
|
.put("accepted_protocols", Collections.singletonList("TLSv1.2"))
|
||||||
.put("cipher_suites", Collections.singletonList("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"))
|
.put("cipher_suites", Collections.singletonList("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"))
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOpti
|
||||||
{
|
{
|
||||||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
c.with(Feature.NATIVE_PROTOCOL);
|
c.with(Feature.NATIVE_PROTOCOL);
|
||||||
c.set("client_encryption_options", validKeystore);
|
c.set("client_encryption_options", validFileBasedKeystores);
|
||||||
}).createWithoutStarting())
|
}).createWithoutStarting())
|
||||||
{
|
{
|
||||||
InetAddress address = cluster.get(1).config().broadcastAddress().getAddress();
|
InetAddress address = cluster.get(1).config().broadcastAddress().getAddress();
|
||||||
|
|
@ -119,7 +119,7 @@ public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOpti
|
||||||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
c.with(Feature.NATIVE_PROTOCOL);
|
c.with(Feature.NATIVE_PROTOCOL);
|
||||||
c.set("client_encryption_options",
|
c.set("client_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("enabled", true)
|
.put("enabled", true)
|
||||||
.put("accepted_protocols", ImmutableList.of("TLSv1.1", "TLSv1.2", "TLSv1.3"))
|
.put("accepted_protocols", ImmutableList.of("TLSv1.1", "TLSv1.2", "TLSv1.3"))
|
||||||
.build());
|
.build());
|
||||||
|
|
@ -156,7 +156,7 @@ public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOpti
|
||||||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
c.with(Feature.NATIVE_PROTOCOL);
|
c.with(Feature.NATIVE_PROTOCOL);
|
||||||
c.set("client_encryption_options",
|
c.set("client_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("enabled", true)
|
.put("enabled", true)
|
||||||
.put("accepted_protocols", Collections.singletonList("TLSv1.2"))
|
.put("accepted_protocols", Collections.singletonList("TLSv1.2"))
|
||||||
.put("cipher_suites", Collections.singletonList("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"))
|
.put("cipher_suites", Collections.singletonList("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"))
|
||||||
|
|
@ -237,7 +237,7 @@ public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOpti
|
||||||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
c.with(Feature.NATIVE_PROTOCOL);
|
c.with(Feature.NATIVE_PROTOCOL);
|
||||||
c.set("client_encryption_options",
|
c.set("client_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("enabled", true)
|
.put("enabled", true)
|
||||||
.put("require_client_auth", "optional")
|
.put("require_client_auth", "optional")
|
||||||
.put("optional", true)
|
.put("optional", true)
|
||||||
|
|
@ -282,7 +282,7 @@ public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOpti
|
||||||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
c.with(Feature.NATIVE_PROTOCOL);
|
c.with(Feature.NATIVE_PROTOCOL);
|
||||||
c.set("client_encryption_options",
|
c.set("client_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("enabled", true)
|
.put("enabled", true)
|
||||||
.put("require_client_auth", "optional")
|
.put("require_client_auth", "optional")
|
||||||
.put("optional", false)
|
.put("optional", false)
|
||||||
|
|
@ -329,7 +329,7 @@ public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOpti
|
||||||
// Server configuration for optional mTLS mode
|
// Server configuration for optional mTLS mode
|
||||||
c.with(Feature.NATIVE_PROTOCOL);
|
c.with(Feature.NATIVE_PROTOCOL);
|
||||||
c.set("client_encryption_options",
|
c.set("client_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("enabled", true)
|
.put("enabled", true)
|
||||||
.put("require_client_auth", "false")
|
.put("require_client_auth", "false")
|
||||||
.put("optional", true)
|
.put("optional", true)
|
||||||
|
|
@ -384,7 +384,7 @@ public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOpti
|
||||||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
c.with(Feature.NATIVE_PROTOCOL);
|
c.with(Feature.NATIVE_PROTOCOL);
|
||||||
c.set("client_encryption_options",
|
c.set("client_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("enabled", true)
|
.put("enabled", true)
|
||||||
.put("require_client_auth", true)
|
.put("require_client_auth", true)
|
||||||
.put("require_endpoint_verification", requireEndpointVerification)
|
.put("require_endpoint_verification", requireEndpointVerification)
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ import static org.apache.cassandra.distributed.api.Feature.GOSSIP;
|
||||||
import static org.apache.cassandra.distributed.api.Feature.JMX;
|
import static org.apache.cassandra.distributed.api.Feature.JMX;
|
||||||
import static org.apache.cassandra.distributed.api.Feature.NATIVE_PROTOCOL;
|
import static org.apache.cassandra.distributed.api.Feature.NATIVE_PROTOCOL;
|
||||||
import static org.apache.cassandra.distributed.api.Feature.NETWORK;
|
import static org.apache.cassandra.distributed.api.Feature.NETWORK;
|
||||||
import static org.apache.cassandra.distributed.test.jmx.JMXGetterCheckTest.testAllValidGetters;
|
import static org.apache.cassandra.distributed.test.jmx.JMXTestsUtil.testAllValidGetters;
|
||||||
import static org.apache.cassandra.utils.FBUtilities.now;
|
import static org.apache.cassandra.utils.FBUtilities.now;
|
||||||
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
|
|
@ -167,7 +167,7 @@ public class ResourceLeakTest extends TestBaseImpl
|
||||||
Assert.assertThat(defaultDomain, startsWith(JMXUtil.getJmxHost(config) + ":" + config.jmxPort()));
|
Assert.assertThat(defaultDomain, startsWith(JMXUtil.getJmxHost(config) + ":" + config.jmxPort()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
testAllValidGetters(cluster);
|
testAllValidGetters(cluster, null);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -61,12 +61,12 @@ public class SSTableLoaderEncryptionOptionsTest extends AbstractEncryptionOption
|
||||||
CLUSTER = Cluster.build().withNodes(1).withConfig(c -> {
|
CLUSTER = Cluster.build().withNodes(1).withConfig(c -> {
|
||||||
c.with(Feature.NATIVE_PROTOCOL, Feature.NETWORK, Feature.GOSSIP); // need gossip to get hostid for java driver
|
c.with(Feature.NATIVE_PROTOCOL, Feature.NETWORK, Feature.GOSSIP); // need gossip to get hostid for java driver
|
||||||
c.set("server_encryption_options",
|
c.set("server_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("internode_encryption", "all")
|
.put("internode_encryption", "all")
|
||||||
.put("optional", false)
|
.put("optional", false)
|
||||||
.build());
|
.build());
|
||||||
c.set("client_encryption_options",
|
c.set("client_encryption_options",
|
||||||
ImmutableMap.builder().putAll(validKeystore)
|
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||||
.put("enabled", true)
|
.put("enabled", true)
|
||||||
.put("optional", false)
|
.put("optional", false)
|
||||||
.put("accepted_protocols", Collections.singletonList("TLSv1.2"))
|
.put("accepted_protocols", Collections.singletonList("TLSv1.2"))
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.distributed.test;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.cassandra.config.CassandraRelevantProperties;
|
||||||
|
import org.apache.cassandra.distributed.shared.WithProperties;
|
||||||
|
|
||||||
|
public class WithPropertiesTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void testPreserveBeforeSet()
|
||||||
|
{
|
||||||
|
CassandraRelevantProperties booleanProperty = CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL;
|
||||||
|
boolean defaultPropertyValue = Boolean.parseBoolean(booleanProperty.getDefaultValue());
|
||||||
|
boolean newPropertyValue = true;
|
||||||
|
|
||||||
|
try (WithProperties properties = new WithProperties()
|
||||||
|
.preserve(booleanProperty)
|
||||||
|
.set(booleanProperty, newPropertyValue))
|
||||||
|
{
|
||||||
|
Assert.assertEquals("Property value must match", newPropertyValue, booleanProperty.getBoolean());
|
||||||
|
}
|
||||||
|
Assert.assertEquals("Property value must revert to the default value", defaultPropertyValue, booleanProperty.getBoolean());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ import org.apache.cassandra.distributed.shared.ClusterUtils;
|
||||||
import org.apache.cassandra.distributed.shared.JMXUtil;
|
import org.apache.cassandra.distributed.shared.JMXUtil;
|
||||||
import org.apache.cassandra.distributed.test.TestBaseImpl;
|
import org.apache.cassandra.distributed.test.TestBaseImpl;
|
||||||
|
|
||||||
import static org.apache.cassandra.distributed.test.jmx.JMXGetterCheckTest.testAllValidGetters;
|
import static org.apache.cassandra.distributed.test.jmx.JMXTestsUtil.testAllValidGetters;
|
||||||
import static org.hamcrest.Matchers.blankOrNullString;
|
import static org.hamcrest.Matchers.blankOrNullString;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
@ -85,7 +85,7 @@ public class JMXFeatureTest extends TestBaseImpl
|
||||||
allInstances.addAll(instancesContacted);
|
allInstances.addAll(instancesContacted);
|
||||||
// Make sure we actually exercise the mbeans by testing a bunch of getters.
|
// Make sure we actually exercise the mbeans by testing a bunch of getters.
|
||||||
// Without this it's possible for the test to pass as we don't touch any mBeans that we register.
|
// Without this it's possible for the test to pass as we don't touch any mBeans that we register.
|
||||||
testAllValidGetters(cluster);
|
testAllValidGetters(cluster, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Assert.assertEquals("Each instance from each cluster should have been unique", iterations * 2, allInstances.size());
|
Assert.assertEquals("Each instance from each cluster should have been unique", iterations * 2, allInstances.size());
|
||||||
|
|
@ -122,7 +122,7 @@ public class JMXFeatureTest extends TestBaseImpl
|
||||||
Assert.assertThat(statusResult.getStderr(), is(blankOrNullString()));
|
Assert.assertThat(statusResult.getStderr(), is(blankOrNullString()));
|
||||||
Assert.assertThat(statusResult.getStdout(), containsString("UN 127.0.0.1"));
|
Assert.assertThat(statusResult.getStdout(), containsString("UN 127.0.0.1"));
|
||||||
testInstance(instances, cluster.get(1));
|
testInstance(instances, cluster.get(1));
|
||||||
testAllValidGetters(cluster);
|
testAllValidGetters(cluster, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,53 +17,14 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.cassandra.distributed.test.jmx;
|
package org.apache.cassandra.distributed.test.jmx;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
import javax.management.JMRuntimeException;
|
|
||||||
import javax.management.MBeanAttributeInfo;
|
|
||||||
import javax.management.MBeanInfo;
|
|
||||||
import javax.management.MBeanOperationInfo;
|
|
||||||
import javax.management.MBeanServerConnection;
|
|
||||||
import javax.management.ObjectName;
|
|
||||||
import javax.management.remote.JMXConnector;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
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 org.apache.cassandra.distributed.api.IInstanceConfig;
|
|
||||||
import org.apache.cassandra.distributed.api.IInvokableInstance;
|
|
||||||
import org.apache.cassandra.distributed.shared.JMXUtil;
|
|
||||||
import org.apache.cassandra.distributed.test.TestBaseImpl;
|
import org.apache.cassandra.distributed.test.TestBaseImpl;
|
||||||
|
|
||||||
public class JMXGetterCheckTest extends TestBaseImpl
|
public class JMXGetterCheckTest extends TestBaseImpl
|
||||||
{
|
{
|
||||||
private static final Set<String> IGNORE_ATTRIBUTES = ImmutableSet.of(
|
|
||||||
"org.apache.cassandra.net:type=MessagingService:BackPressurePerHost", // throws unsupported saying the feature was removed... dropped in CASSANDRA-15375
|
|
||||||
"org.apache.cassandra.db:type=DynamicEndpointSnitch:Scores", // when running in multiple-port-one-IP mode, this fails
|
|
||||||
"org.apache.cassandra.db:type=StorageService:OutstandingSchemaVersions", // deprecated (TCM)
|
|
||||||
"org.apache.cassandra.db:type=StorageService:OutstandingSchemaVersionsWithPort" // deprecated (TCM)
|
|
||||||
);
|
|
||||||
private static final Set<String> IGNORE_OPERATIONS = ImmutableSet.of(
|
|
||||||
"org.apache.cassandra.db:type=StorageService:stopDaemon", // halts the instance, which then causes the JVM to exit
|
|
||||||
"org.apache.cassandra.db:type=StorageService:drain", // don't drain, it stops things which can cause other APIs to be unstable as we are in a stopped state
|
|
||||||
"org.apache.cassandra.db:type=StorageService:stopGossiping", // if we stop gossip this can cause other issues, so avoid
|
|
||||||
"org.apache.cassandra.db:type=StorageService:resetLocalSchema", // this will fail when there are no other nodes which can serve schema
|
|
||||||
"org.apache.cassandra.db:type=StorageService:joinRing", // Causes bootstrapping errors
|
|
||||||
"org.apache.cassandra.db:type=Tables,keyspace=system,table=local:loadNewSSTables", // Shouldn't attempt to load SSTables as sometimes the temp directories don't work
|
|
||||||
"org.apache.cassandra.db:type=CIDRGroupsMappingManager:loadCidrGroupsCache", // CIDR cache isn't enabled by default
|
|
||||||
"org.apache.cassandra.db:type=StorageService:clearConnectionHistory", // Throws a NullPointerException
|
|
||||||
"org.apache.cassandra.db:type=StorageService:startGossiping", // causes multiple loops to fail
|
|
||||||
"org.apache.cassandra.db:type=StorageService:startNativeTransport", // causes multiple loops to fail
|
|
||||||
"org.apache.cassandra.db:type=StorageService:resumeMove", // throws since there is no move in progress
|
|
||||||
"org.apache.cassandra.db:type=StorageService:abortMove", // throws since there is no move in progress
|
|
||||||
"org.apache.cassandra.db:type=CIDRGroupsMappingManager:loadCidrGroupsCache", // AllowAllCIDRAuthorizer doesn't support this operation, as feature is disabled by default
|
|
||||||
"org.apache.cassandra.db:type=StorageService:forceRemoveCompletion" // deprecated (TCM)
|
|
||||||
);
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetters() throws Exception
|
public void testGetters() throws Exception
|
||||||
|
|
@ -72,95 +33,8 @@ public class JMXGetterCheckTest extends TestBaseImpl
|
||||||
{
|
{
|
||||||
try (Cluster cluster = Cluster.build(1).withConfig(c -> c.with(Feature.values())).start())
|
try (Cluster cluster = Cluster.build(1).withConfig(c -> c.with(Feature.values())).start())
|
||||||
{
|
{
|
||||||
testAllValidGetters(cluster);
|
JMXTestsUtil.testAllValidGetters(cluster, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests JMX getters and operations.
|
|
||||||
* Useful for more than just testing getters, it also is used in JMXFeatureTest
|
|
||||||
* to make sure we've touched the complete JMX code path.
|
|
||||||
* @param cluster the cluster to test
|
|
||||||
* @throws Exception several kinds of exceptions can be thrown, mostly from JMX infrastructure issues.
|
|
||||||
*/
|
|
||||||
public static void testAllValidGetters(Cluster cluster) throws Exception
|
|
||||||
{
|
|
||||||
for (IInvokableInstance instance: cluster)
|
|
||||||
{
|
|
||||||
if (instance.isShutdown())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
IInstanceConfig config = instance.config();
|
|
||||||
List<Named> errors = new ArrayList<>();
|
|
||||||
try (JMXConnector jmxc = JMXUtil.getJmxConnector(config))
|
|
||||||
{
|
|
||||||
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
|
|
||||||
Set<ObjectName> metricNames = new TreeSet<>(mbsc.queryNames(null, null));
|
|
||||||
for (ObjectName name : metricNames)
|
|
||||||
{
|
|
||||||
if (!name.getDomain().startsWith("org.apache.cassandra"))
|
|
||||||
continue;
|
|
||||||
MBeanInfo info = mbsc.getMBeanInfo(name);
|
|
||||||
for (MBeanAttributeInfo a : info.getAttributes())
|
|
||||||
{
|
|
||||||
String fqn = String.format("%s:%s", name, a.getName());
|
|
||||||
if (!a.isReadable() || IGNORE_ATTRIBUTES.contains(fqn))
|
|
||||||
continue;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mbsc.getAttribute(name, a.getName());
|
|
||||||
}
|
|
||||||
catch (JMRuntimeException e)
|
|
||||||
{
|
|
||||||
errors.add(new Named(String.format("Attribute %s", fqn), e.getCause()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MBeanOperationInfo o : info.getOperations())
|
|
||||||
{
|
|
||||||
String fqn = String.format("%s:%s", name, o.getName());
|
|
||||||
if (o.getSignature().length != 0 || IGNORE_OPERATIONS.contains(fqn))
|
|
||||||
continue;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mbsc.invoke(name, o.getName(), new Object[0], new String[0]);
|
|
||||||
}
|
|
||||||
catch (JMRuntimeException e)
|
|
||||||
{
|
|
||||||
errors.add(new Named(String.format("Operation %s", fqn), e.getCause()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!errors.isEmpty())
|
|
||||||
{
|
|
||||||
AssertionError root = new AssertionError();
|
|
||||||
errors.forEach(root::addSuppressed);
|
|
||||||
throw root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is meant to make new errors easier to read, by adding the JMX endpoint, and cleaning up the unneeded JMX/Reflection logic cluttering the stacktrace
|
|
||||||
*/
|
|
||||||
private static class Named extends RuntimeException
|
|
||||||
{
|
|
||||||
public Named(String msg, Throwable cause)
|
|
||||||
{
|
|
||||||
super(msg + "\nCaused by: " + cause.getClass().getCanonicalName() + ": " + cause.getMessage(), cause.getCause());
|
|
||||||
StackTraceElement[] stack = cause.getStackTrace();
|
|
||||||
List<StackTraceElement> copy = new ArrayList<>();
|
|
||||||
for (StackTraceElement s : stack)
|
|
||||||
{
|
|
||||||
if (!s.getClassName().startsWith("org.apache.cassandra"))
|
|
||||||
break;
|
|
||||||
copy.add(s);
|
|
||||||
}
|
|
||||||
Collections.reverse(copy);
|
|
||||||
setStackTrace(copy.toArray(new StackTraceElement[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.distributed.test.jmx;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.management.remote.rmi.RMIConnectorServer;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.rmi.ssl.SslRMIClientSocketFactory;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.cassandra.distributed.Cluster;
|
||||||
|
import org.apache.cassandra.distributed.api.Feature;
|
||||||
|
import org.apache.cassandra.distributed.impl.JmxTestClientSslContextFactory;
|
||||||
|
import org.apache.cassandra.distributed.impl.JmxTestClientSslSocketFactory;
|
||||||
|
import org.apache.cassandra.distributed.shared.WithProperties;
|
||||||
|
import org.apache.cassandra.distributed.test.AbstractEncryptionOptionsImpl;
|
||||||
|
import org.apache.cassandra.utils.jmx.JMXSslPropertiesUtil;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_CIPHER_SUITES;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Distributed tests for JMX SSL configuration via the system properties OR the encryption options in the cassandra.yaml.
|
||||||
|
*/
|
||||||
|
public class JMXSslConfigDistributedTest extends AbstractEncryptionOptionsImpl
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void testDefaultEncryptionOptions() throws Throwable
|
||||||
|
{
|
||||||
|
// We must set the keystore in the system variable to make sure that the call to SSLContext.getDefault()
|
||||||
|
// uses it when Client SSL Socketfactory is initialized even if we don't need it here.
|
||||||
|
// The same default SSLContext.getDefault() will be used by other methods like testSystemSettings() in this test
|
||||||
|
// for the Server SSL Socketfactory and at that time we will need the keystore to be available
|
||||||
|
// All of the above is the issue because we run everything (JMX Server, Client) in the same JVM, multiple times
|
||||||
|
// and the SSLContext.getDefault() relies on static initialization that is reused
|
||||||
|
try (WithProperties withProperties = JMXSslPropertiesUtil.preserveAllProperties())
|
||||||
|
{
|
||||||
|
setKeystoreProperties(withProperties);
|
||||||
|
ImmutableMap<String, Object> encryptionOptionsMap = ImmutableMap.<String, Object>builder().putAll(validFileBasedKeystores)
|
||||||
|
.put("enabled", true)
|
||||||
|
.put("accepted_protocols", Arrays.asList("TLSv1.2", "TLSv1.3", "TLSv1.1"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
|
c.with(Feature.JMX).set("jmx_encryption_options", encryptionOptionsMap);
|
||||||
|
}).start())
|
||||||
|
{
|
||||||
|
Map<String, Object> jmxEnv = new HashMap<>();
|
||||||
|
configureClientSocketFactory(jmxEnv, encryptionOptionsMap);
|
||||||
|
JMXTestsUtil.testAllValidGetters(cluster, jmxEnv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClientAuth() throws Throwable
|
||||||
|
{
|
||||||
|
try (WithProperties withProperties = JMXSslPropertiesUtil.preserveAllProperties())
|
||||||
|
{
|
||||||
|
setKeystoreProperties(withProperties);
|
||||||
|
ImmutableMap<String, Object> encryptionOptionsMap = ImmutableMap.<String, Object>builder().putAll(validFileBasedKeystores)
|
||||||
|
.put("enabled", true)
|
||||||
|
.put("require_client_auth", true)
|
||||||
|
.put("accepted_protocols", Arrays.asList("TLSv1.2", "TLSv1.3", "TLSv1.1"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
|
c.with(Feature.JMX).set("jmx_encryption_options", encryptionOptionsMap);
|
||||||
|
}).start())
|
||||||
|
{
|
||||||
|
Map<String, Object> jmxEnv = new HashMap<>();
|
||||||
|
configureClientSocketFactory(jmxEnv, encryptionOptionsMap);
|
||||||
|
JMXTestsUtil.testAllValidGetters(cluster, jmxEnv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSystemSettings() throws Throwable
|
||||||
|
{
|
||||||
|
COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_CIPHER_SUITES.reset();
|
||||||
|
try (WithProperties withProperties = JMXSslPropertiesUtil.use(true, false,
|
||||||
|
"TLSv1.2,TLSv1.3,TLSv1.1"))
|
||||||
|
{
|
||||||
|
setKeystoreProperties(withProperties);
|
||||||
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
|
c.with(Feature.JMX);
|
||||||
|
}).start())
|
||||||
|
{
|
||||||
|
Map<String, Object> jmxEnv = new HashMap<>();
|
||||||
|
SslRMIClientSocketFactory clientFactory = new SslRMIClientSocketFactory();
|
||||||
|
jmxEnv.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, clientFactory);
|
||||||
|
jmxEnv.put("com.sun.jndi.rmi.factory.socket", clientFactory);
|
||||||
|
JMXTestsUtil.testAllValidGetters(cluster, jmxEnv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidKeystorePath() throws Throwable
|
||||||
|
{
|
||||||
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
|
c.with(Feature.JMX).set("jmx_encryption_options",
|
||||||
|
ImmutableMap.<String, Object>builder()
|
||||||
|
.put("enabled", true)
|
||||||
|
.put("keystore", "/path/to/bad/keystore/that/should/not/exist")
|
||||||
|
.put("keystore_password", "cassandra")
|
||||||
|
.put("accepted_protocols", Arrays.asList("TLSv1.2", "TLSv1.3", "TLSv1.1"))
|
||||||
|
.build());
|
||||||
|
}).createWithoutStarting())
|
||||||
|
{
|
||||||
|
assertCannotStartDueToConfigurationException(cluster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@code disabled} jmx_encryption_options. Here even if the configured {@code keystore} is invalid, it will
|
||||||
|
* not matter and the JMX server/client should start.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDisabledEncryptionOptions() throws Throwable
|
||||||
|
{
|
||||||
|
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||||
|
c.with(Feature.JMX).set("jmx_encryption_options",
|
||||||
|
ImmutableMap.builder()
|
||||||
|
.put("enabled", false)
|
||||||
|
.put("keystore", "/path/to/bad/keystore/that/should/not/exist")
|
||||||
|
.put("keystore_password", "cassandra")
|
||||||
|
.build());
|
||||||
|
}).start())
|
||||||
|
{
|
||||||
|
JMXTestsUtil.testAllValidGetters(cluster, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setKeystoreProperties(WithProperties properties)
|
||||||
|
{
|
||||||
|
properties.with("javax.net.ssl.trustStore", (String) validFileBasedKeystores.get("truststore"),
|
||||||
|
"javax.net.ssl.trustStorePassword", (String) validFileBasedKeystores.get("truststore_password"),
|
||||||
|
"javax.net.ssl.keyStore", (String) validFileBasedKeystores.get("keystore"),
|
||||||
|
"javax.net.ssl.keyStorePassword", (String) validFileBasedKeystores.get("keystore_password"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void configureClientSocketFactory(Map<String, Object> jmxEnv, Map<String, Object> encryptionOptionsMap) throws SSLException
|
||||||
|
{
|
||||||
|
JmxTestClientSslContextFactory clientSslContextFactory = new JmxTestClientSslContextFactory(encryptionOptionsMap);
|
||||||
|
List<String> cipherSuitesList = (List<String>) encryptionOptionsMap.get("cipher_suites");
|
||||||
|
String[] cipherSuites = cipherSuitesList == null ? null : cipherSuitesList.toArray(new String[0]);
|
||||||
|
List<String> acceptedProtocolList = (List<String>) encryptionOptionsMap.get("accepted_protocols");
|
||||||
|
String[] acceptedProtocols = acceptedProtocolList == null ? null : acceptedProtocolList.toArray(new String[0]);
|
||||||
|
JmxTestClientSslSocketFactory clientFactory = new JmxTestClientSslSocketFactory(clientSslContextFactory.createSSLContext(),
|
||||||
|
cipherSuites, acceptedProtocols);
|
||||||
|
jmxEnv.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, clientFactory);
|
||||||
|
jmxEnv.put("com.sun.jndi.rmi.factory.socket", clientFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.distributed.test.jmx;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import javax.management.JMRuntimeException;
|
||||||
|
import javax.management.MBeanAttributeInfo;
|
||||||
|
import javax.management.MBeanInfo;
|
||||||
|
import javax.management.MBeanOperationInfo;
|
||||||
|
import javax.management.MBeanServerConnection;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
import javax.management.remote.JMXConnector;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.apache.cassandra.distributed.Cluster;
|
||||||
|
import org.apache.cassandra.distributed.api.IInstanceConfig;
|
||||||
|
import org.apache.cassandra.distributed.api.IInvokableInstance;
|
||||||
|
import org.apache.cassandra.distributed.shared.JMXUtil;
|
||||||
|
|
||||||
|
public class JMXTestsUtil
|
||||||
|
{
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(JMXTestsUtil.class);
|
||||||
|
private static final Set<String> IGNORE_ATTRIBUTES = ImmutableSet.of(
|
||||||
|
"org.apache.cassandra.net:type=MessagingService:BackPressurePerHost", // throws unsupported saying the feature was removed... dropped in CASSANDRA-15375
|
||||||
|
"org.apache.cassandra.db:type=DynamicEndpointSnitch:Scores", // when running in multiple-port-one-IP mode, this fails
|
||||||
|
"org.apache.cassandra.db:type=StorageService:OutstandingSchemaVersions", // deprecated (TCM)
|
||||||
|
"org.apache.cassandra.db:type=StorageService:OutstandingSchemaVersionsWithPort" // deprecated (TCM)
|
||||||
|
);
|
||||||
|
private static final Set<String> IGNORE_OPERATIONS = ImmutableSet.of(
|
||||||
|
"org.apache.cassandra.db:type=StorageService:stopDaemon", // halts the instance, which then causes the JVM to exit
|
||||||
|
"org.apache.cassandra.db:type=StorageService:drain", // don't drain, it stops things which can cause other APIs to be unstable as we are in a stopped state
|
||||||
|
"org.apache.cassandra.db:type=StorageService:stopGossiping", // if we stop gossip this can cause other issues, so avoid
|
||||||
|
"org.apache.cassandra.db:type=StorageService:resetLocalSchema", // this will fail when there are no other nodes which can serve schema
|
||||||
|
"org.apache.cassandra.db:type=StorageService:joinRing", // Causes bootstrapping errors
|
||||||
|
"org.apache.cassandra.db:type=Tables,keyspace=system,table=local:loadNewSSTables", // Shouldn't attempt to load SSTables as sometimes the temp directories don't work
|
||||||
|
"org.apache.cassandra.db:type=CIDRGroupsMappingManager:loadCidrGroupsCache", // CIDR cache isn't enabled by default
|
||||||
|
"org.apache.cassandra.db:type=StorageService:clearConnectionHistory", // Throws a NullPointerException
|
||||||
|
"org.apache.cassandra.db:type=StorageService:startGossiping", // causes multiple loops to fail
|
||||||
|
"org.apache.cassandra.db:type=StorageService:startNativeTransport", // causes multiple loops to fail
|
||||||
|
"org.apache.cassandra.db:type=StorageService:resumeMove", // throws since there is no move in progress
|
||||||
|
"org.apache.cassandra.db:type=StorageService:abortMove", // throws since there is no move in progress
|
||||||
|
"org.apache.cassandra.db:type=CIDRGroupsMappingManager:loadCidrGroupsCache", // AllowAllCIDRAuthorizer doesn't support this operation, as feature is disabled by default
|
||||||
|
"org.apache.cassandra.db:type=StorageService:forceRemoveCompletion" // deprecated (TCM)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests JMX getters and operations and allows passing JMX Env used for the client JMX connection.
|
||||||
|
*
|
||||||
|
* @param cluster the cluster to test
|
||||||
|
* @throws Exception several kinds of exceptions can be thrown, mostly from JMX infrastructure issues.
|
||||||
|
*/
|
||||||
|
public static void testAllValidGetters(Cluster cluster, Map<String, ?> jmxEnv) throws Exception
|
||||||
|
{
|
||||||
|
for (IInvokableInstance instance : cluster)
|
||||||
|
{
|
||||||
|
if (instance.isShutdown())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
IInstanceConfig config = instance.config();
|
||||||
|
List<Named> errors = new ArrayList<>();
|
||||||
|
try (JMXConnector jmxc = JMXUtil.getJmxConnector(config, jmxEnv))
|
||||||
|
{
|
||||||
|
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
|
||||||
|
Set<ObjectName> metricNames = new TreeSet<>(mbsc.queryNames(null, null));
|
||||||
|
for (ObjectName name : metricNames)
|
||||||
|
{
|
||||||
|
if (!name.getDomain().startsWith("org.apache.cassandra"))
|
||||||
|
continue;
|
||||||
|
MBeanInfo info = mbsc.getMBeanInfo(name);
|
||||||
|
for (MBeanAttributeInfo a : info.getAttributes())
|
||||||
|
{
|
||||||
|
String fqn = String.format("%s:%s", name, a.getName());
|
||||||
|
if (!a.isReadable() || IGNORE_ATTRIBUTES.contains(fqn))
|
||||||
|
continue;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mbsc.getAttribute(name, a.getName());
|
||||||
|
}
|
||||||
|
catch (JMRuntimeException e)
|
||||||
|
{
|
||||||
|
errors.add(new Named(String.format("Attribute %s", fqn), e.getCause()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MBeanOperationInfo o : info.getOperations())
|
||||||
|
{
|
||||||
|
String fqn = String.format("%s:%s", name, o.getName());
|
||||||
|
if (o.getSignature().length != 0 || IGNORE_OPERATIONS.contains(fqn))
|
||||||
|
continue;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mbsc.invoke(name, o.getName(), new Object[0], new String[0]);
|
||||||
|
}
|
||||||
|
catch (JMRuntimeException e)
|
||||||
|
{
|
||||||
|
errors.add(new Named(String.format("Operation %s", fqn), e.getCause()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!errors.isEmpty())
|
||||||
|
{
|
||||||
|
AssertionError root = new AssertionError();
|
||||||
|
for (Named error : errors)
|
||||||
|
{
|
||||||
|
// The Named object's message has the cause also so this only logs the message
|
||||||
|
logger.error("Error {}", error.getMessage());
|
||||||
|
root.addSuppressed(error);
|
||||||
|
}
|
||||||
|
throw root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is meant to make new errors easier to read, by adding the JMX endpoint, and cleaning up the unneeded JMX/Reflection logic cluttering the stacktrace
|
||||||
|
*/
|
||||||
|
static class Named extends RuntimeException
|
||||||
|
{
|
||||||
|
public Named(String msg, Throwable cause)
|
||||||
|
{
|
||||||
|
super(msg + "\nCaused by: " + cause.getClass().getCanonicalName() + ": " + cause.getMessage(), cause.getCause());
|
||||||
|
StackTraceElement[] stack = cause.getStackTrace();
|
||||||
|
List<StackTraceElement> copy = new ArrayList<>();
|
||||||
|
for (StackTraceElement s : stack)
|
||||||
|
{
|
||||||
|
if (!s.getClassName().startsWith("org.apache.cassandra"))
|
||||||
|
break;
|
||||||
|
copy.add(s);
|
||||||
|
}
|
||||||
|
Collections.reverse(copy);
|
||||||
|
setStackTrace(copy.toArray(new StackTraceElement[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -250,7 +250,7 @@ public class EncryptionOptionsTest
|
||||||
|
|
||||||
for(EncryptionOptions.ConfigKey configKey: EncryptionOptions.ConfigKey.values())
|
for(EncryptionOptions.ConfigKey configKey: EncryptionOptions.ConfigKey.values())
|
||||||
{
|
{
|
||||||
customSslContextFactoryParams.put(configKey.getKeyName(), "my-custom-value");
|
customSslContextFactoryParams.put(configKey.toString(), "my-custom-value");
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptionOptionsTestCase.of(null, absentKeystore, true, customSslContextFactoryParams, ENCRYPTED);
|
EncryptionOptionsTestCase.of(null, absentKeystore, true, customSslContextFactoryParams, ENCRYPTED);
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,10 @@ public class PropertiesTest
|
||||||
Config config = new Config();
|
Config config = new Config();
|
||||||
Set<String> keys = ImmutableSet.of("server_encryption_options.enabled",
|
Set<String> keys = ImmutableSet.of("server_encryption_options.enabled",
|
||||||
"client_encryption_options.enabled",
|
"client_encryption_options.enabled",
|
||||||
|
"jmx_encryption_options.enabled",
|
||||||
"server_encryption_options.optional",
|
"server_encryption_options.optional",
|
||||||
"client_encryption_options.optional");
|
"client_encryption_options.optional",
|
||||||
|
"jmx_encryption_options.optional");
|
||||||
for (Property prop : ps.values())
|
for (Property prop : ps.values())
|
||||||
{
|
{
|
||||||
// skip these properties as they don't allow get/set within the context of this test
|
// skip these properties as they don't allow get/set within the context of this test
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ public class SettingsTableTest extends CQLTester
|
||||||
config = new Config();
|
config = new Config();
|
||||||
config.client_encryption_options.applyConfig();
|
config.client_encryption_options.applyConfig();
|
||||||
config.server_encryption_options.applyConfig();
|
config.server_encryption_options.applyConfig();
|
||||||
|
config.jmx_encryption_options.applyConfig();
|
||||||
config.sstable_preemptive_open_interval = null;
|
config.sstable_preemptive_open_interval = null;
|
||||||
config.index_summary_resize_interval = null;
|
config.index_summary_resize_interval = null;
|
||||||
config.cache_load_timeout = new DurationSpec.IntSecondsBound(0);
|
config.cache_load_timeout = new DurationSpec.IntSecondsBound(0);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.utils.jmx;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.management.remote.rmi.RMIConnectorServer;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.rmi.ssl.SslRMIServerSocketFactory;
|
||||||
|
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
|
import org.apache.cassandra.distributed.shared.WithProperties;
|
||||||
|
import org.apache.cassandra.utils.JMXServerUtils;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.CASSANDRA_CONFIG;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for Local JMX server, the remote JMX SSL configuration via System properties in absence of jmx_encryption_options
|
||||||
|
* in the cassandra.yaml. This is the behavior before CASSANDRA-18508.
|
||||||
|
*/
|
||||||
|
public class JMXSslConfigTest
|
||||||
|
{
|
||||||
|
static WithProperties properties;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setupDatabaseDescriptor()
|
||||||
|
{
|
||||||
|
properties = new WithProperties().set(CASSANDRA_CONFIG, "cassandra.yaml");
|
||||||
|
DatabaseDescriptor.daemonInitialization();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDownDatabaseDescriptor()
|
||||||
|
{
|
||||||
|
properties.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for remote JMX SSL configuration specified via the System properties.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testRemoteJmxSystemConfig() throws SSLException
|
||||||
|
{
|
||||||
|
InetAddress serverAddress = InetAddress.getLoopbackAddress();
|
||||||
|
String enabledProtocols = "TLSv1.2,TLSv1.3,TLSv1.1";
|
||||||
|
String cipherSuites = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256";
|
||||||
|
|
||||||
|
try (WithProperties ignored = JMXSslPropertiesUtil.use(true, true, enabledProtocols,
|
||||||
|
cipherSuites))
|
||||||
|
{
|
||||||
|
Map<String, Object> env = JMXServerUtils.configureJmxSocketFactories(serverAddress, false);
|
||||||
|
Assert.assertNotNull("ServerSocketFactory must not be null", env.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE));
|
||||||
|
Assert.assertTrue("RMI_SERVER_SOCKET_FACTORY must be of SslRMIServerSocketFactory type", env.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE) instanceof SslRMIServerSocketFactory);
|
||||||
|
Assert.assertNotNull("ClientSocketFactory must not be null", env.get(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE));
|
||||||
|
Assert.assertNotNull("com.sun.jndi.rmi.factory.socket must be set in the env", env.get("com.sun.jndi.rmi.factory.socket"));
|
||||||
|
Assert.assertEquals("protocols must match", enabledProtocols, JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS.getString());
|
||||||
|
Assert.assertEquals("cipher-suites must match", cipherSuites, JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES.getString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@code localOnly} JMX Server configuration.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testLocalJmxServer() throws SSLException
|
||||||
|
{
|
||||||
|
InetAddress serverAddress = InetAddress.getLoopbackAddress();
|
||||||
|
try (WithProperties ignored = JMXSslPropertiesUtil.use(false))
|
||||||
|
{
|
||||||
|
Map<String, Object> env = JMXServerUtils.configureJmxSocketFactories(serverAddress, true);
|
||||||
|
|
||||||
|
Assert.assertNull("ClientSocketFactory must be null", env.get(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE));
|
||||||
|
Assert.assertNull("com.sun.jndi.rmi.factory.socket must not be set in the env", env.get("com.sun.jndi.rmi.factory.socket"));
|
||||||
|
Assert.assertNotNull("ServerSocketFactory must not be null", env.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE));
|
||||||
|
Assert.assertNull("protocols must be empty", JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS.getString());
|
||||||
|
Assert.assertNull("cipher-suites must empty", JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES.getString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.utils.jmx;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.management.remote.rmi.RMIConnectorServer;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.rmi.ssl.SslRMIServerSocketFactory;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
|
import org.apache.cassandra.config.EncryptionOptions;
|
||||||
|
import org.apache.cassandra.distributed.shared.WithProperties;
|
||||||
|
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||||
|
import org.apache.cassandra.utils.JMXServerUtils;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.CASSANDRA_CONFIG;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for Default JMX SSL configuration specified in the cassandra.yaml using jmx_encryption_options.
|
||||||
|
* The default configurtion means keystore/truststore configured with file paths and using {@code DefaultSslContextFactory}
|
||||||
|
* to initialize the SSLContext.
|
||||||
|
*/
|
||||||
|
public class JMXSslConfiguredWithYamlFileOptionsTest
|
||||||
|
{
|
||||||
|
static WithProperties properties;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setupDatabaseDescriptor()
|
||||||
|
{
|
||||||
|
properties = new WithProperties().set(CASSANDRA_CONFIG, "cassandra-jmx-sslconfig.yaml");
|
||||||
|
DatabaseDescriptor.daemonInitialization();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDownDatabaseDescriptor()
|
||||||
|
{
|
||||||
|
properties.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testYamlFileJmxEncryptionOptions() throws SSLException
|
||||||
|
{
|
||||||
|
EncryptionOptions jmxEncryptionOptions = DatabaseDescriptor.getJmxEncryptionOptions();
|
||||||
|
String expectedProtocols = StringUtils.join(jmxEncryptionOptions.getAcceptedProtocols(), ",");
|
||||||
|
String expectedCipherSuites = StringUtils.join(jmxEncryptionOptions.cipherSuitesArray(), ",");
|
||||||
|
|
||||||
|
InetAddress serverAddress = InetAddress.getLoopbackAddress();
|
||||||
|
|
||||||
|
try (WithProperties ignored = JMXSslPropertiesUtil.use(false))
|
||||||
|
{
|
||||||
|
Map<String, Object> env = JMXServerUtils.configureJmxSocketFactories(serverAddress, false);
|
||||||
|
Assert.assertTrue("com.sun.management.jmxremote.ssl must be true", COM_SUN_MANAGEMENT_JMXREMOTE_SSL.getBoolean());
|
||||||
|
Assert.assertNotNull("ServerSocketFactory must not be null", env.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE));
|
||||||
|
Assert.assertTrue("RMI_SERVER_SOCKET_FACTORY must be of JMXSslRMIServerSocketFactory type", env.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE) instanceof SslRMIServerSocketFactory);
|
||||||
|
Assert.assertNotNull("ClientSocketFactory must not be null", env.get(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE));
|
||||||
|
Assert.assertNotNull("com.sun.jndi.rmi.factory.socket must be set in the env", env.get("com.sun.jndi.rmi.factory.socket"));
|
||||||
|
Assert.assertEquals("javax.rmi.ssl.client.enabledProtocols must match", expectedProtocols, JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS.getString());
|
||||||
|
Assert.assertEquals("javax.rmi.ssl.client.enabledCipherSuites must match", expectedCipherSuites, JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES.getString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for the error scenario when the JMX SSL configuration is provided as
|
||||||
|
* system configuration as well as encryption_options.
|
||||||
|
*
|
||||||
|
* @throws SSLException
|
||||||
|
*/
|
||||||
|
@Test(expected = ConfigurationException.class)
|
||||||
|
public void testDuplicateConfig() throws SSLException
|
||||||
|
{
|
||||||
|
InetAddress serverAddress = InetAddress.getLoopbackAddress();
|
||||||
|
|
||||||
|
try (WithProperties ignored = JMXSslPropertiesUtil.use(true))
|
||||||
|
{
|
||||||
|
JMXServerUtils.configureJmxSocketFactories(serverAddress, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.utils.jmx;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.management.remote.rmi.RMIConnectorServer;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
|
import org.apache.cassandra.distributed.shared.WithProperties;
|
||||||
|
import org.apache.cassandra.utils.JMXServerUtils;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.CASSANDRA_CONFIG;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for disabling jmx_encryption_options in the cassandra.yaml.
|
||||||
|
*/
|
||||||
|
public class JMXSslDisabledEncryptionOptionsTest
|
||||||
|
{
|
||||||
|
static WithProperties properties;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setupDatabaseDescriptor()
|
||||||
|
{
|
||||||
|
properties = new WithProperties().set(CASSANDRA_CONFIG, "cassandra-jmx-disabled-sslconfig.yaml");
|
||||||
|
DatabaseDescriptor.daemonInitialization();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDownDatabaseDescriptor()
|
||||||
|
{
|
||||||
|
properties.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests disabled JMX SSL configuration. This means, all below are either not configured OR configured with
|
||||||
|
* disabled mode.
|
||||||
|
* 1. local only JMX server
|
||||||
|
* 2. System properties set for remote JMX SSL
|
||||||
|
* 3. jmx_encryption_options in the cassandra.yaml
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDisabledJmxSslConfigs() throws SSLException
|
||||||
|
{
|
||||||
|
InetAddress serverAddress = InetAddress.getLoopbackAddress();
|
||||||
|
|
||||||
|
try (WithProperties ignored = JMXSslPropertiesUtil.use(false))
|
||||||
|
{
|
||||||
|
Map<String, Object> env = JMXServerUtils.configureJmxSocketFactories(serverAddress, false);
|
||||||
|
Assert.assertTrue("no properties must be set", env.isEmpty());
|
||||||
|
Assert.assertFalse("com.sun.management.jmxremote.ssl must be false", COM_SUN_MANAGEMENT_JMXREMOTE_SSL.getBoolean());
|
||||||
|
Assert.assertNull("javax.rmi.ssl.client.enabledProtocols must be null", JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS.getString());
|
||||||
|
Assert.assertNull("javax.rmi.ssl.client.enabledCipherSuites must be null", JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES.getString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests fallback to the `local only` JMX server when jmx_encryption_options are disabled in the cassandra.yaml
|
||||||
|
* and no System settings provided for the remote SSL config.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testFallbackToLocalJmxServer() throws SSLException
|
||||||
|
{
|
||||||
|
InetAddress serverAddress = InetAddress.getLoopbackAddress();
|
||||||
|
|
||||||
|
try (WithProperties ignored = JMXSslPropertiesUtil.use(false))
|
||||||
|
{
|
||||||
|
Map<String, Object> env = JMXServerUtils.configureJmxSocketFactories(serverAddress, true);
|
||||||
|
Assert.assertFalse("com.sun.management.jmxremote.ssl must be false", COM_SUN_MANAGEMENT_JMXREMOTE_SSL.getBoolean());
|
||||||
|
Assert.assertNull("com.sun.jndi.rmi.factory.socket must be null", env.get("com.sun.jndi.rmi.factory.socket"));
|
||||||
|
Assert.assertNotNull("ServerSocketFactory must not be null", env.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE));
|
||||||
|
Assert.assertFalse("com.sun.management.jmxremote.ssl must be false", COM_SUN_MANAGEMENT_JMXREMOTE_SSL.getBoolean());
|
||||||
|
Assert.assertNull("javax.rmi.ssl.client.enabledProtocols must be null", JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS.getString());
|
||||||
|
Assert.assertNull("javax.rmi.ssl.client.enabledCipherSuites must be null", JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES.getString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.utils.jmx;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.management.remote.rmi.RMIConnectorServer;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.rmi.ssl.SslRMIServerSocketFactory;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
|
import org.apache.cassandra.config.EncryptionOptions;
|
||||||
|
import org.apache.cassandra.distributed.shared.WithProperties;
|
||||||
|
import org.apache.cassandra.utils.JMXServerUtils;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.CASSANDRA_CONFIG;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for JMX SSL config using keystore/truststore in the PEM format with jmx_encryption_options in the cassandra.yaml.
|
||||||
|
*/
|
||||||
|
public class JMXSslPEMConfigTest
|
||||||
|
{
|
||||||
|
static WithProperties properties;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setupDatabaseDescriptor()
|
||||||
|
{
|
||||||
|
properties = new WithProperties().set(CASSANDRA_CONFIG, "cassandra-jmx-pem-sslconfig.yaml");
|
||||||
|
DatabaseDescriptor.daemonInitialization();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDownDatabaseDescriptor()
|
||||||
|
{
|
||||||
|
properties.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPEMBasedJmxSslConfig() throws SSLException
|
||||||
|
{
|
||||||
|
EncryptionOptions jmxEncryptionOptions = DatabaseDescriptor.getJmxEncryptionOptions();
|
||||||
|
String expectedProtocols = StringUtils.join(jmxEncryptionOptions.getAcceptedProtocols(), ",");
|
||||||
|
String expectedCipherSuites = StringUtils.join(jmxEncryptionOptions.cipherSuitesArray(), ",");
|
||||||
|
|
||||||
|
InetAddress serverAddress = InetAddress.getLoopbackAddress();
|
||||||
|
|
||||||
|
try (WithProperties ignored = JMXSslPropertiesUtil.use(false))
|
||||||
|
{
|
||||||
|
Map<String, Object> env = JMXServerUtils.configureJmxSocketFactories(serverAddress, false);
|
||||||
|
Assert.assertTrue("com.sun.management.jmxremote.ssl must be true", COM_SUN_MANAGEMENT_JMXREMOTE_SSL.getBoolean());
|
||||||
|
Assert.assertNotNull("ServerSocketFactory must not be null", env.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE));
|
||||||
|
Assert.assertTrue("RMI_SERVER_SOCKET_FACTORY must be of JMXSslRMIServerSocketFactory type", env.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE) instanceof SslRMIServerSocketFactory);
|
||||||
|
Assert.assertNotNull("ClientSocketFactory must not be null", env.get(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE));
|
||||||
|
Assert.assertNotNull("com.sun.jndi.rmi.factory.socket must be set in the env", env.get("com.sun.jndi.rmi.factory.socket"));
|
||||||
|
Assert.assertEquals("javax.rmi.ssl.client.enabledProtocols must match", expectedProtocols, JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS.getString());
|
||||||
|
Assert.assertEquals("javax.rmi.ssl.client.enabledCipherSuites must match", expectedCipherSuites, JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES.getString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.utils.jmx;
|
||||||
|
|
||||||
|
import org.apache.cassandra.distributed.shared.WithProperties;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_CIPHER_SUITES;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_PROTOCOLS;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.COM_SUN_MANAGEMENT_JMXREMOTE_SSL_NEED_CLIENT_AUTH;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES;
|
||||||
|
import static org.apache.cassandra.config.CassandraRelevantProperties.JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS;
|
||||||
|
|
||||||
|
public final class JMXSslPropertiesUtil
|
||||||
|
{
|
||||||
|
public static WithProperties use(boolean enableRemoteSsl, boolean needClientAuth, String enabledProtocols, String cipherSuites)
|
||||||
|
{
|
||||||
|
return preserveAllProperties()
|
||||||
|
.set(COM_SUN_MANAGEMENT_JMXREMOTE_SSL, enableRemoteSsl)
|
||||||
|
.set(COM_SUN_MANAGEMENT_JMXREMOTE_SSL_NEED_CLIENT_AUTH, needClientAuth)
|
||||||
|
.set(COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_PROTOCOLS, enabledProtocols)
|
||||||
|
.set(COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_CIPHER_SUITES, cipherSuites);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WithProperties use(boolean enableRemoteSsl, boolean needClientAuth, String enabledProtocols)
|
||||||
|
{
|
||||||
|
return preserveAllProperties()
|
||||||
|
.set(COM_SUN_MANAGEMENT_JMXREMOTE_SSL, enableRemoteSsl)
|
||||||
|
.set(COM_SUN_MANAGEMENT_JMXREMOTE_SSL_NEED_CLIENT_AUTH, needClientAuth)
|
||||||
|
.set(COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_PROTOCOLS, enabledProtocols);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WithProperties use(boolean enableRemoteSsl)
|
||||||
|
{
|
||||||
|
return preserveAllProperties()
|
||||||
|
.set(COM_SUN_MANAGEMENT_JMXREMOTE_SSL, enableRemoteSsl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WithProperties preserveAllProperties()
|
||||||
|
{
|
||||||
|
return new WithProperties()
|
||||||
|
.preserve(COM_SUN_MANAGEMENT_JMXREMOTE_SSL)
|
||||||
|
.preserve(COM_SUN_MANAGEMENT_JMXREMOTE_SSL_NEED_CLIENT_AUTH)
|
||||||
|
.preserve(COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_PROTOCOLS)
|
||||||
|
.preserve(COM_SUN_MANAGEMENT_JMXREMOTE_SSL_ENABLED_CIPHER_SUITES)
|
||||||
|
.preserve(JAVAX_RMI_SSL_CLIENT_ENABLED_PROTOCOLS)
|
||||||
|
.preserve(JAVAX_RMI_SSL_CLIENT_ENABLED_CIPHER_SUITES);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue