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
|
||||
* 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)
|
||||
* Optionally prevent tombstone purging during repair (CASSANDRA-20071)
|
||||
* 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
|
||||
when 'GENERATED PASSWORD' clause is used. Character sets supported are: English, Cyrillic, modern Cyrillic,
|
||||
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
|
||||
|
|
|
|||
|
|
@ -236,6 +236,11 @@ else
|
|||
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=true"
|
||||
|
||||
# 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.need.client.auth=true"
|
||||
#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.
|
||||
# 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
|
||||
# compressed.
|
||||
# Can be:
|
||||
|
|
|
|||
|
|
@ -1676,6 +1676,24 @@ client_encryption_options:
|
|||
# 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
|
||||
# compressed.
|
||||
# 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
|
||||
YAML!)
|
||||
|
||||
....
|
||||
[source,yaml]
|
||||
----
|
||||
client/server_encryption_options:
|
||||
ssl_context_factory:
|
||||
class_name: org.apache.cassandra.security.PEMBasedSslContextFactory
|
||||
|
|
@ -97,17 +98,19 @@ YAML!)
|
|||
-----BEGIN CERTIFICATE-----
|
||||
<your base64 encoded certificate>
|
||||
-----END CERTIFICATE-----
|
||||
....
|
||||
----
|
||||
|
||||
* Configuration: PEM keys/certs defined in files
|
||||
....
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
client/server_encryption_options:
|
||||
ssl_context_factory:
|
||||
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_password: "<your password if the private key is encrypted with a password>"
|
||||
truststore: <file path to the truststore file in the PEM format>
|
||||
....
|
||||
----
|
||||
|
||||
== SSL Certificate Hot Reloading
|
||||
|
||||
|
|
@ -564,8 +567,61 @@ See also: xref:cassandra:developing/cql/security.adoc#permissions[`Permissions`]
|
|||
|
||||
=== JMX With SSL
|
||||
|
||||
JMX SSL configuration is controlled by a number of system properties,
|
||||
some of which are optional. To turn on SSL, edit the relevant lines in
|
||||
JMX SSL configuration should be specified in `cassandra.yaml` under
|
||||
`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
|
||||
set the values of these properties as required:
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ COMPLEX_OPTIONS = (
|
|||
'hints_compression',
|
||||
'server_encryption_options',
|
||||
'client_encryption_options',
|
||||
'jmx_encryption_options',
|
||||
'transparent_data_encryption_options',
|
||||
'hinted_handoff_disabled_datacenters',
|
||||
'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
|
||||
* 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"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -434,6 +434,7 @@ public class Config
|
|||
|
||||
public EncryptionOptions.ServerEncryptionOptions server_encryption_options = new EncryptionOptions.ServerEncryptionOptions();
|
||||
public EncryptionOptions client_encryption_options = new EncryptionOptions();
|
||||
public EncryptionOptions jmx_encryption_options = new EncryptionOptions();
|
||||
|
||||
public InternodeCompression internode_compression = InternodeCompression.none;
|
||||
|
||||
|
|
@ -1320,6 +1321,7 @@ public class Config
|
|||
private static final Set<String> SENSITIVE_KEYS = new HashSet<String>() {{
|
||||
add("client_encryption_options");
|
||||
add("server_encryption_options");
|
||||
add("jmx_encryption_options");
|
||||
}};
|
||||
|
||||
public static void log(Config config)
|
||||
|
|
|
|||
|
|
@ -960,6 +960,9 @@ public class DatabaseDescriptor
|
|||
if (conf.client_encryption_options != null)
|
||||
conf.client_encryption_options.applyConfig();
|
||||
|
||||
if (conf.jmx_encryption_options != null)
|
||||
conf.jmx_encryption_options.applyConfig();
|
||||
|
||||
if (conf.snapshot_links_per_second < 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("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);
|
||||
/*
|
||||
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)
|
||||
{
|
||||
|
|
@ -3653,6 +3663,11 @@ public class DatabaseDescriptor
|
|||
return conf.client_encryption_options;
|
||||
}
|
||||
|
||||
public static EncryptionOptions getJmxEncryptionOptions()
|
||||
{
|
||||
return conf.jmx_encryption_options;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void updateNativeProtocolEncryptionOptions(Function<EncryptionOptions, EncryptionOptions> update)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -172,10 +172,10 @@ public class EncryptionOptions
|
|||
|
||||
ConfigKey(String keyName)
|
||||
{
|
||||
this.keyName=keyName;
|
||||
this.keyName = keyName;
|
||||
}
|
||||
|
||||
String getKeyName()
|
||||
public String toString()
|
||||
{
|
||||
return keyName;
|
||||
}
|
||||
|
|
@ -184,8 +184,9 @@ public class EncryptionOptions
|
|||
{
|
||||
Set<String> valueSet = new HashSet<>();
|
||||
ConfigKey[] values = values();
|
||||
for(ConfigKey key: values) {
|
||||
valueSet.add(toLowerCaseLocalized(key.getKeyName()));
|
||||
for (ConfigKey key : values)
|
||||
{
|
||||
valueSet.add(toLowerCaseLocalized(key.toString()));
|
||||
}
|
||||
return valueSet;
|
||||
}
|
||||
|
|
@ -357,7 +358,7 @@ public class EncryptionOptions
|
|||
protected static void putSslContextFactoryParameter(Map<String, Object> existingParameters, ConfigKey configKey, Object value)
|
||||
{
|
||||
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.RMIServerSocketFactory;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.management.remote.*;
|
||||
import javax.management.remote.JMXAuthenticator;
|
||||
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.RMIJRMPServerImpl;
|
||||
import javax.rmi.ssl.SslRMIClientSocketFactory;
|
||||
import javax.rmi.ssl.SslRMIServerSocketFactory;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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_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_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_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;
|
||||
|
||||
public class JMXServerUtils
|
||||
|
|
@ -92,6 +87,7 @@ public class JMXServerUtils
|
|||
}
|
||||
|
||||
// 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));
|
||||
|
||||
// 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<>();
|
||||
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;
|
||||
return new DefaultJmxSocketFactory().configure(serverAddress, localOnly, DatabaseDescriptor.getJmxEncryptionOptions());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
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 hostName;
|
||||
|
|
@ -280,21 +263,7 @@ public class JMXServerUtils
|
|||
? '[' + serverAddress.getHostAddress() + ']'
|
||||
: serverAddress.getHostAddress();
|
||||
}
|
||||
String url = 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());
|
||||
return String.format(urlTemplate, hostName, port);
|
||||
}
|
||||
|
||||
private static class JMXPluggableAuthenticatorWrapper implements JMXAuthenticator
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import java.io.IOException;
|
|||
import java.io.Serializable;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.rmi.server.RMIClientSocketFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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
|
||||
* 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<>();
|
||||
private final InetAddress localAddress;
|
||||
|
||||
|
|
@ -50,6 +50,7 @@ public class RMIClientSocketFactoryImpl implements RMIClientSocketFactory, Seria
|
|||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
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.ServerSocket;
|
||||
import java.net.SocketException;
|
||||
import java.rmi.server.RMIServerSocketFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
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
|
||||
* 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 CollectingRMIServerSocketFactoryImpl implements RMIServerSocketFactory
|
||||
class CollectingRMIServerSocketFactoryImpl implements RMICloseableServerSocketFactory
|
||||
{
|
||||
private final InetAddress bindAddress;
|
||||
List<ServerSocket> sockets = new ArrayList<>();
|
||||
|
|
@ -62,7 +63,7 @@ class CollectingRMIServerSocketFactoryImpl implements RMIServerSocketFactory
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
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",
|
||||
config.networkTopology(), config.broadcastAddress());
|
||||
DistributedTestSnitch.assign(config.networkTopology());
|
||||
DatabaseDescriptor.daemonInitialization();
|
||||
if (config.has(JMX))
|
||||
startJmx();
|
||||
DatabaseDescriptor.daemonInitialization();
|
||||
LoggingSupportFactory.getLoggingSupport().onStartup();
|
||||
logSystemInfo(inInstancelogger);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ import java.io.IOException;
|
|||
import java.net.InetAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
|
@ -34,12 +34,14 @@ import javax.management.remote.rmi.RMIJRMPServerImpl;
|
|||
import com.google.common.util.concurrent.Uninterruptibles;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.cassandra.config.EncryptionOptions;
|
||||
import org.apache.cassandra.distributed.api.IInstance;
|
||||
import org.apache.cassandra.distributed.api.IInstanceConfig;
|
||||
import org.apache.cassandra.distributed.shared.JMXUtil;
|
||||
import org.apache.cassandra.utils.JMXServerUtils;
|
||||
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 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 RMIJRMPServerImpl jmxRmiServer;
|
||||
private MBeanWrapper.InstanceMBeanWrapper wrapper;
|
||||
private RMIClientSocketFactoryImpl clientSocketFactory;
|
||||
private CollectingRMIServerSocketFactoryImpl serverSocketFactory;
|
||||
private RMICloseableClientSocketFactory clientSocketFactory;
|
||||
private RMICloseableServerSocketFactory serverSocketFactory;
|
||||
private Logger inInstancelogger;
|
||||
private IInstanceConfig config;
|
||||
|
||||
public IsolatedJmx(IInstance instance, Logger inInstanceLogger) {
|
||||
public IsolatedJmx(IInstance instance, Logger inInstanceLogger)
|
||||
{
|
||||
this.config = instance.config();
|
||||
this.inInstancelogger = inInstanceLogger;
|
||||
}
|
||||
|
||||
public void startJmx() {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void startJmx()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 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();
|
||||
wrapper = new MBeanWrapper.InstanceMBeanWrapper(hostname + ":" + jmxPort);
|
||||
((MBeanWrapper.DelegatingMbeanWrapper) MBeanWrapper.instance).setDelegate(wrapper);
|
||||
Map<String, Object> env = new HashMap<>();
|
||||
|
||||
serverSocketFactory = new CollectingRMIServerSocketFactoryImpl(addr);
|
||||
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
|
||||
serverSocketFactory);
|
||||
clientSocketFactory = new RMIClientSocketFactoryImpl(addr);
|
||||
env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
|
||||
clientSocketFactory);
|
||||
// CASSANDRA-18508: Sensitive JMX SSL configuration options can be easily exposed
|
||||
Map<String, Object> encryptionOptionsMap = (Map<String, Object>) config.getParams().get("jmx_encryption_options");
|
||||
EncryptionOptions jmxEncryptionOptions = getJmxEncryptionOptions(encryptionOptionsMap);
|
||||
// Here the `localOnly` is always passed as true as it is for the local isolated JMX testing
|
||||
// However if the `jmxEncryptionOptions` are provided or JMX SSL configuration is set it will configure
|
||||
// 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
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
// 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,
|
||||
"truststore", validTrustStorePath,
|
||||
"truststore_password", validTrustStorePassword);
|
||||
|
||||
// Configuration with a valid keystore, but an unknown protocol
|
||||
final static Map<String,Object> nonExistantProtocol = ImmutableMap.<String,Object>builder()
|
||||
.putAll(validKeystore)
|
||||
.putAll(validFileBasedKeystores)
|
||||
.put("accepted_protocols", Collections.singletonList("NoProtocolIKnow"))
|
||||
.build();
|
||||
// Configuration with a valid keystore, but an unknown cipher suite
|
||||
final static Map<String,Object> nonExistantCipher = ImmutableMap.<String,Object>builder()
|
||||
.putAll(validKeystore)
|
||||
.putAll(validFileBasedKeystores)
|
||||
.put("cipher_suites", Collections.singletonList("NoCipherIKnow"))
|
||||
.build();
|
||||
|
||||
|
|
@ -340,7 +340,7 @@ public class AbstractEncryptionOptionsImpl extends TestBaseImpl
|
|||
}
|
||||
|
||||
/* Provde the cluster cannot start with the configured options */
|
||||
void assertCannotStartDueToConfigurationException(Cluster cluster)
|
||||
protected void assertCannotStartDueToConfigurationException(Cluster cluster)
|
||||
{
|
||||
Throwable tr = null;
|
||||
try
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
|||
c.with(Feature.NETWORK);
|
||||
c.set("ssl_storage_port", 7013);
|
||||
c.set("server_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("internode_encryption", "none")
|
||||
.put("optional", false)
|
||||
.put("legacy_ssl_storage_port_enabled", "true")
|
||||
|
|
@ -87,7 +87,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
|||
{
|
||||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||
c.with(Feature.NETWORK);
|
||||
c.set("server_encryption_options", validKeystore);
|
||||
c.set("server_encryption_options", validFileBasedKeystores);
|
||||
}).createWithoutStarting())
|
||||
{
|
||||
InetAddress address = cluster.get(1).config().broadcastAddress().getAddress();
|
||||
|
|
@ -111,7 +111,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
|||
c.set("storage_port", 7012);
|
||||
c.set("ssl_storage_port", 7013);
|
||||
c.set("server_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("internode_encryption", "none")
|
||||
.put("optional", 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("ssl_storage_port", 7012);
|
||||
c.set("server_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("internode_encryption", "none")
|
||||
.put("optional", 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 -> {
|
||||
c.with(Feature.NETWORK);
|
||||
c.set("server_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("internode_encryption", "none")
|
||||
.put("optional", false)
|
||||
.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.NATIVE_PROTOCOL); // For virtual tables
|
||||
c.set("server_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("internode_encryption", "all")
|
||||
.build());
|
||||
}).start())
|
||||
|
|
@ -234,7 +234,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
|||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||
c.with(Feature.NETWORK);
|
||||
c.set("server_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("internode_encryption", "all")
|
||||
.put("accepted_protocols", ImmutableList.of("TLSv1.1", "TLSv1.2", "TLSv1.3"))
|
||||
.build());
|
||||
|
|
@ -272,7 +272,7 @@ public class InternodeEncryptionOptionsTest extends AbstractEncryptionOptionsImp
|
|||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||
c.with(Feature.NETWORK);
|
||||
c.set("server_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("internode_encryption", "all")
|
||||
.put("accepted_protocols", Collections.singletonList("TLSv1.2"))
|
||||
.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 -> {
|
||||
c.with(Feature.NATIVE_PROTOCOL);
|
||||
c.set("client_encryption_options", validKeystore);
|
||||
c.set("client_encryption_options", validFileBasedKeystores);
|
||||
}).createWithoutStarting())
|
||||
{
|
||||
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 -> {
|
||||
c.with(Feature.NATIVE_PROTOCOL);
|
||||
c.set("client_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("enabled", true)
|
||||
.put("accepted_protocols", ImmutableList.of("TLSv1.1", "TLSv1.2", "TLSv1.3"))
|
||||
.build());
|
||||
|
|
@ -156,7 +156,7 @@ public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOpti
|
|||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||
c.with(Feature.NATIVE_PROTOCOL);
|
||||
c.set("client_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("enabled", true)
|
||||
.put("accepted_protocols", Collections.singletonList("TLSv1.2"))
|
||||
.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 -> {
|
||||
c.with(Feature.NATIVE_PROTOCOL);
|
||||
c.set("client_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("enabled", true)
|
||||
.put("require_client_auth", "optional")
|
||||
.put("optional", true)
|
||||
|
|
@ -282,7 +282,7 @@ public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOpti
|
|||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||
c.with(Feature.NATIVE_PROTOCOL);
|
||||
c.set("client_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("enabled", true)
|
||||
.put("require_client_auth", "optional")
|
||||
.put("optional", false)
|
||||
|
|
@ -329,7 +329,7 @@ public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOpti
|
|||
// Server configuration for optional mTLS mode
|
||||
c.with(Feature.NATIVE_PROTOCOL);
|
||||
c.set("client_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("enabled", true)
|
||||
.put("require_client_auth", "false")
|
||||
.put("optional", true)
|
||||
|
|
@ -384,7 +384,7 @@ public class NativeTransportEncryptionOptionsTest extends AbstractEncryptionOpti
|
|||
try (Cluster cluster = builder().withNodes(1).withConfig(c -> {
|
||||
c.with(Feature.NATIVE_PROTOCOL);
|
||||
c.set("client_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("enabled", true)
|
||||
.put("require_client_auth", true)
|
||||
.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.NATIVE_PROTOCOL;
|
||||
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.hamcrest.Matchers.lessThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
|
@ -167,7 +167,7 @@ public class ResourceLeakTest extends TestBaseImpl
|
|||
Assert.assertThat(defaultDomain, startsWith(JMXUtil.getJmxHost(config) + ":" + config.jmxPort()));
|
||||
}
|
||||
}
|
||||
testAllValidGetters(cluster);
|
||||
testAllValidGetters(cluster, null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -61,12 +61,12 @@ public class SSTableLoaderEncryptionOptionsTest extends AbstractEncryptionOption
|
|||
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.set("server_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("internode_encryption", "all")
|
||||
.put("optional", false)
|
||||
.build());
|
||||
c.set("client_encryption_options",
|
||||
ImmutableMap.builder().putAll(validKeystore)
|
||||
ImmutableMap.builder().putAll(validFileBasedKeystores)
|
||||
.put("enabled", true)
|
||||
.put("optional", false)
|
||||
.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.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.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
|
@ -85,7 +85,7 @@ public class JMXFeatureTest extends TestBaseImpl
|
|||
allInstances.addAll(instancesContacted);
|
||||
// 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.
|
||||
testAllValidGetters(cluster);
|
||||
testAllValidGetters(cluster, null);
|
||||
}
|
||||
}
|
||||
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.getStdout(), containsString("UN 127.0.0.1"));
|
||||
testInstance(instances, cluster.get(1));
|
||||
testAllValidGetters(cluster);
|
||||
testAllValidGetters(cluster, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,53 +17,14 @@
|
|||
*/
|
||||
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.apache.cassandra.distributed.Cluster;
|
||||
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;
|
||||
|
||||
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
|
||||
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())
|
||||
{
|
||||
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())
|
||||
{
|
||||
customSslContextFactoryParams.put(configKey.getKeyName(), "my-custom-value");
|
||||
customSslContextFactoryParams.put(configKey.toString(), "my-custom-value");
|
||||
}
|
||||
|
||||
EncryptionOptionsTestCase.of(null, absentKeystore, true, customSslContextFactoryParams, ENCRYPTED);
|
||||
|
|
|
|||
|
|
@ -40,8 +40,10 @@ public class PropertiesTest
|
|||
Config config = new Config();
|
||||
Set<String> keys = ImmutableSet.of("server_encryption_options.enabled",
|
||||
"client_encryption_options.enabled",
|
||||
"jmx_encryption_options.enabled",
|
||||
"server_encryption_options.optional",
|
||||
"client_encryption_options.optional");
|
||||
"client_encryption_options.optional",
|
||||
"jmx_encryption_options.optional");
|
||||
for (Property prop : ps.values())
|
||||
{
|
||||
// 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.client_encryption_options.applyConfig();
|
||||
config.server_encryption_options.applyConfig();
|
||||
config.jmx_encryption_options.applyConfig();
|
||||
config.sstable_preemptive_open_interval = null;
|
||||
config.index_summary_resize_interval = null;
|
||||
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