Introduce pluggable crypto providers and default to Amazon Corretto Crypto Provider

patch by Ayushi Singh; reviewed by Stefan Miklosovic, Michael Semb Wever and Maxim Muzafarov for CASSANDRA-18624

Co-authored-by: Stefan Miklosovic <smiklosovic@apache.org>
This commit is contained in:
ayushis 2023-07-10 15:21:07 -07:00 committed by Stefan Miklosovic
parent a9d1712aec
commit 6ab45971fc
No known key found for this signature in database
GPG Key ID: 32F35CB2F546D93E
21 changed files with 1156 additions and 5 deletions

View File

@ -64,7 +64,7 @@
<macrodef name="resolve">
<!--
maven-resolver-ant-tasks's resolve logic doesn't have retry logic and does not respect settings.xml,
maven-resolver-ant-tasks's resolve logic doesn't have retry logic and does not respect settings.xml,
this causes issues when overriding maven central is required (such as when behind a corporate firewall);
it is critical to always provide the 'all' remoterepos to override resolve's default hard coded logic.
@ -74,7 +74,7 @@
<element name="elements" implicit="yes"/>
<sequential>
<retry retrycount="3">
<resolver:resolve failonmissingattachments="@{failonmissingattachments}">
<resolver:resolve failonmissingattachments="@{failonmissingattachments}">
<resolver:remoterepos refid="all"/>
<elements/>
</resolver:resolve>
@ -88,8 +88,8 @@
<sequential>
<retry retrycount="3">
<resolver:pom file="@{file}" id="@{id}">
<remoterepos refid="all"/>
<elements/>
<remoterepos refid="all"/>
<elements/>
</resolver:pom>
</retry>
</sequential>
@ -206,6 +206,26 @@
</retry>
<mkdir dir="${local.repository}/org/apache/cassandra/deps/sigar-bin"/>
<mkdir dir="${build.lib}/sigar-bin"/>
<mkdir dir="${build.lib}/x86_64"/>
<mkdir dir="${build.lib}/aarch64"/> <!-- uname -m on arm prints aarch64 instead of aarch_64 -->
<!-- artifacts needs AmazonCorrettoCryptoProvider for multiple archs -->
<retry retrycount="3" retrydelay="10" >
<resolve>
<dependencies>
<dependency groupId="software.amazon.cryptools" artifactId="AmazonCorrettoCryptoProvider" version="2.2.0" classifier="linux-x86_64" />
</dependencies>
<files dir="${build.lib}/x86_64" layout="{artifactId}-{version}-{classifier}.{extension}" />
</resolve>
</retry>
<retry retrycount="3" retrydelay="10" >
<resolve>
<dependencies>
<dependency groupId="software.amazon.cryptools" artifactId="AmazonCorrettoCryptoProvider" version="2.2.0" classifier="linux-aarch_64" />
</dependencies>
<files dir="${build.lib}/aarch64" layout="{artifactId}-{version}-{classifier}.{extension}" />
</resolve>
</retry>
<retry retrycount="3" retrydelay="10" >
<antcall target="_resolver-dist-lib_get_files"/>
@ -240,6 +260,10 @@
<file file="${local.repository}/org/apache/cassandra/deps/sigar-bin/libsigar-x86-linux.so"/>
<file file="${local.repository}/org/apache/cassandra/deps/sigar-bin/libsigar-x86-solaris.so"/>
</copy>
<!-- as resolver will copy all dependencies into lib dir, and we are copying jars to lib/{x86_64|aarch64} as well, we would have duplicities -->
<delete file="${build.lib}/AmazonCorrettoCryptoProvider-2.2.0-linux-x86_64.jar" failonerror="false"/>
<delete file="${build.lib}/AmazonCorrettoCryptoProvider-2.2.0-linux-aarch_64.jar" failonerror="false"/>
</target>
<target name="_resolver-dist-lib_get_files">

View File

@ -233,12 +233,53 @@
<id>zznate</id>
<name>Nate McCall</name>
</developer>
<developer>
<id>smiklosovic</id>
<name>Stefan Miklosovic</name>
</developer>
</developers>
<scm>
<connection>scm:https://gitbox.apache.org/repos/asf/cassandra.git</connection>
<developerConnection>scm:https://gitbox.apache.org/repos/asf/cassandra.git</developerConnection>
<url>https://gitbox.apache.org/repos/asf?p=cassandra.git;a=tree</url>
</scm>
<profiles>
<profile>
<id>x86_64</id>
<activation>
<os>
<!-- we need something as a default even if it doesn't successfully load the .so files. -->
<arch>!aarch64</arch>
</os>
</activation>
<dependencies>
<dependency>
<groupId>software.amazon.cryptools</groupId>
<artifactId>AmazonCorrettoCryptoProvider</artifactId>
<classifier>linux-x86_64</classifier>
<version>2.2.0</version>
</dependency>
</dependencies>
</profile>
<profile>
<id>aarch_64</id>
<activation>
<os>
<arch>aarch64</arch>
</os>
</activation>
<dependencies>
<dependency>
<groupId>software.amazon.cryptools</groupId>
<artifactId>AmazonCorrettoCryptoProvider</artifactId>
<classifier>linux-aarch_64</classifier>
<version>2.2.0</version>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencyManagement>
<!--
Dependency metadata is specified here (version, scope, exclusions, etc.), then referenced in child POMs by groupId and

View File

@ -1,4 +1,5 @@
5.0
* Introduce pluggable crypto providers and default to AmazonCorrettoCryptoProvider (CASSANDRA-18624)
* Improved DeletionTime serialization (CASSANDRA-18648)
* CEP-7: Storage Attached Indexes (CASSANDRA-16052)
* Add equals/hashCode override for ServerEncryptionOptions (CASSANDRA-18428)

View File

@ -71,6 +71,9 @@ using the provided 'sstableupgrade' tool.
New features
------------
- Pluggable crypto providers were made possible via `crypto_provider` section in cassandra.yaml. The default provider is
Amazon Corretto Crypto Provider and it is installed automatically upon node's start. Only x86_64 and aarch64 architectures are supported now.
Please consult upgrade section to know more details when upgrading from older Cassandra versions.
- Added a new secondary index implementation, Storage-Attached Indexes (SAI). Overview documentation and a basic
tutorial can be found at src/java/org/apache/cassandra/index/sai/README.md.
- *Experimental* support for Java 17 has been added. JVM options that differ between or are
@ -210,6 +213,10 @@ Upgrading
Consult cassandra-rackdc.properties for more details. (CASSANDRA-16555)
- JMX MBean `org.apache.cassandra.metrics:type=BufferPool` without scope has been removed.
Use instead `org.apache.cassandra.metrics:type=BufferPool,scope=chunk-cache`. (CASSANDRA-17668)
- Upon upgrade, when cassandra.yaml does not contain `crypto_provider` configuration section, crypto providers from JRE installation will be used
and no installation of DefaultCryptoProvider installing Amazon Corretto Crypto Provider will be conducted.
You need to explicitly add this section to the old yaml if it does not contain it yet to enable Amazon Corretto Crypto Provider for such node.
New deployments have `crypto_provider` uncommented with DefaultCryptoProvider hence Corretto provider will be installed automatically for corresponding architecture.
Deprecation

View File

@ -84,6 +84,12 @@ JAVA_AGENT="$JAVA_AGENT -javaagent:$CASSANDRA_HOME/lib/jamm-0.4.0.jar"
# Added sigar-bin to the java.library.path CASSANDRA-7838
JAVA_OPTS="$JAVA_OPTS:-Djava.library.path=$CASSANDRA_HOME/lib/sigar-bin"
platform=$(uname -m)
if [ -d "$CASSANDRA_HOME"/lib/"$platform" ]; then
for jar in "$CASSANDRA_HOME"/lib/"$platform"/*.jar ; do
CLASSPATH="$CLASSPATH:${jar}"
done
fi
#
# Java executable and per-Java version JVM settings

View File

@ -1375,6 +1375,21 @@ dynamic_snitch_reset_interval: 600000ms
# until the pinned host was 20% worse than the fastest.
dynamic_snitch_badness_threshold: 1.0
# Configures Java crypto provider. By default, it will use DefaultCryptoProvider
# which will install Amazon Correto Crypto Provider.
#
# Amazon Correto Crypto Provider works currently for x86_64 and aarch_64 platforms.
# If this provider fails it will fall back to the default crypto provider in the JRE.
#
# To force failure when the provider was not installed properly, set the property "fail_on_missing_provider" to "true".
#
# To bypass the installation of a crypto provider use class 'org.apache.cassandra.security.JREProvider'
#
crypto_provider:
- class_name: org.apache.cassandra.security.DefaultCryptoProvider
parameters:
- fail_on_missing_provider: "false"
# Configure server-to-server internode encryption
#
# JVM and netty defaults for supported SSL socket protocols and cipher suites can

View File

@ -29,3 +29,5 @@ tools/bin/sstablepartitions usr/bin
lib/*.jar usr/share/cassandra/lib
lib/*.zip usr/share/cassandra/lib
lib/sigar-bin/* usr/share/cassandra/lib/sigar-bin
lib/x86_64/* usr/share/cassandra/lib/x86_64
lib/aarch64/* usr/share/cassandra/lib/aarch64

View File

@ -42,6 +42,12 @@ CLASSPATH="$CLASSPATH:$EXTRA_CLASSPATH"
# set JVM javaagent opts to avoid warnings/errors
JAVA_AGENT="$JAVA_AGENT -javaagent:$CASSANDRA_HOME/lib/jamm-0.4.0.jar"
platform=$(uname -m)
if [ -d "$CASSANDRA_HOME"/lib/"$platform" ]; then
for jar in "$CASSANDRA_HOME"/lib/"$platform"/*.jar ; do
CLASSPATH="$CLASSPATH:${jar}"
done
fi
#
# Java executable and per-Java version JVM settings

View File

@ -170,6 +170,7 @@ public enum CassandraRelevantProperties
CONSISTENT_DIRECTORY_LISTINGS("cassandra.consistent_directory_listings"),
CONSISTENT_RANGE_MOVEMENT("cassandra.consistent.rangemovement", "true"),
CONSISTENT_SIMULTANEOUS_MOVES_ALLOW("cassandra.consistent.simultaneousmoves.allow"),
CRYPTO_PROVIDER_CLASS_NAME("cassandra.crypto_provider_class_name"),
CUSTOM_GUARDRAILS_CONFIG_PROVIDER_CLASS("cassandra.custom_guardrails_config_provider_class"),
CUSTOM_QUERY_HANDLER_CLASS("cassandra.custom_query_handler_class"),
CUSTOM_TRACING_CLASS("cassandra.custom_tracing_class"),
@ -212,6 +213,7 @@ public enum CassandraRelevantProperties
EXPIRATION_DATE_OVERFLOW_POLICY("cassandra.expiration_date_overflow_policy"),
EXPIRATION_OVERFLOW_WARNING_INTERVAL_MINUTES("cassandra.expiration_overflow_warning_interval_minutes", "5"),
FAILURE_LOGGING_INTERVAL_SECONDS("cassandra.request_failure_log_interval_seconds", "60"),
FAIL_ON_MISSING_CRYPTO_PROVIDER("cassandra.fail_on_missing_crypto_provider", "false"),
FD_INITIAL_VALUE_MS("cassandra.fd_initial_value_ms"),
FD_MAX_INTERVAL_MS("cassandra.fd_max_interval_ms"),
FILE_CACHE_ENABLED("cassandra.file_cache_enabled"),
@ -528,6 +530,7 @@ public enum CassandraRelevantProperties
TEST_SIMULATOR_PRINT_ASM_CLASSES("cassandra.test.simulator.print_asm_classes", ""),
TEST_SIMULATOR_PRINT_ASM_OPTS("cassandra.test.simulator.print_asm_opts", ""),
TEST_SIMULATOR_PRINT_ASM_TYPES("cassandra.test.simulator.print_asm_types", ""),
TEST_SKIP_CRYPTO_PROVIDER_INSTALLATION("cassandra.test.security.skip.provider.installation", "false"),
TEST_SSTABLE_FORMAT_DEVELOPMENT("cassandra.test.sstableformatdevelopment"),
TEST_STRICT_LCS_CHECKS("cassandra.test.strict_lcs_checks"),
/** Turns some warnings into exceptions for testing. */

View File

@ -80,6 +80,7 @@ public class Config
public String authenticator;
public String authorizer;
public String role_manager;
public ParameterizedClass crypto_provider;
public String network_authorizer;
@Replaces(oldName = "permissions_validity_in_ms", converter = Converters.MILLIS_DURATION_INT, deprecated = true)
public volatile DurationSpec.IntMillisecondsBound permissions_validity = new DurationSpec.IntMillisecondsBound("2s");

View File

@ -98,7 +98,9 @@ import org.apache.cassandra.locator.IEndpointSnitch;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.locator.Replica;
import org.apache.cassandra.locator.SeedProvider;
import org.apache.cassandra.security.AbstractCryptoProvider;
import org.apache.cassandra.security.EncryptionContext;
import org.apache.cassandra.security.JREProvider;
import org.apache.cassandra.security.SSLFactory;
import org.apache.cassandra.service.CacheService.CacheType;
import org.apache.cassandra.service.paxos.Paxos;
@ -123,10 +125,11 @@ import static org.apache.cassandra.config.CassandraRelevantProperties.SEARCH_CON
import static org.apache.cassandra.config.CassandraRelevantProperties.SSL_STORAGE_PORT;
import static org.apache.cassandra.config.CassandraRelevantProperties.STORAGE_DIR;
import static org.apache.cassandra.config.CassandraRelevantProperties.STORAGE_PORT;
import static org.apache.cassandra.config.CassandraRelevantProperties.TEST_STRICT_RUNTIME_CHECKS;
import static org.apache.cassandra.config.CassandraRelevantProperties.SUN_ARCH_DATA_MODEL;
import static org.apache.cassandra.config.CassandraRelevantProperties.TEST_FAIL_MV_LOCKS_COUNT;
import static org.apache.cassandra.config.CassandraRelevantProperties.TEST_JVM_DTEST_DISABLE_SSL;
import static org.apache.cassandra.config.CassandraRelevantProperties.TEST_SKIP_CRYPTO_PROVIDER_INSTALLATION;
import static org.apache.cassandra.config.CassandraRelevantProperties.TEST_STRICT_RUNTIME_CHECKS;
import static org.apache.cassandra.config.CassandraRelevantProperties.UNSAFE_SYSTEM;
import static org.apache.cassandra.config.DataRateSpec.DataRateUnit.BYTES_PER_SECOND;
import static org.apache.cassandra.config.DataRateSpec.DataRateUnit.MEBIBYTES_PER_SECOND;
@ -174,6 +177,7 @@ public class DatabaseDescriptor
private static Config.DiskAccessMode indexAccessMode;
private static AbstractCryptoProvider cryptoProvider;
private static IAuthenticator authenticator;
private static IAuthorizer authorizer;
private static INetworkAuthorizer networkAuthorizer;
@ -426,6 +430,8 @@ public class DatabaseDescriptor
//InetAddressAndPort cares that applySimpleConfig runs first
applySSTableFormats();
applyCryptoProvider();
applySimpleConfig();
applyPartitioner();
@ -1219,6 +1225,42 @@ public class DatabaseDescriptor
}
}
public static void applyCryptoProvider()
{
if (TEST_SKIP_CRYPTO_PROVIDER_INSTALLATION.getBoolean())
return;
if (conf.crypto_provider == null)
conf.crypto_provider = new ParameterizedClass(JREProvider.class.getName(), null);
// properties beat configuration
String classNameFromSystemProperties = CassandraRelevantProperties.CRYPTO_PROVIDER_CLASS_NAME.getString();
if (classNameFromSystemProperties != null)
conf.crypto_provider.class_name = classNameFromSystemProperties;
if (conf.crypto_provider.class_name == null)
throw new ConfigurationException("Failed to initialize crypto provider, class_name cannot be null");
if (conf.crypto_provider.parameters == null)
conf.crypto_provider.parameters = new HashMap<>();
Map<String, String> cryptoProviderParameters = new HashMap<>(conf.crypto_provider.parameters);
cryptoProviderParameters.putIfAbsent(AbstractCryptoProvider.FAIL_ON_MISSING_PROVIDER_KEY, "false");
try
{
cryptoProvider = FBUtilities.newCryptoProvider(conf.crypto_provider.class_name, cryptoProviderParameters);
cryptoProvider.install();
}
catch (Exception e)
{
if (e instanceof ConfigurationException)
throw (ConfigurationException) e;
else
throw new ConfigurationException(String.format("Failed to initialize crypto provider %s", conf.crypto_provider.class_name), e);
}
}
public static void applySeedProvider()
{
// load the seeds for node contact points
@ -1502,6 +1544,15 @@ public class DatabaseDescriptor
return detector;
}
public static AbstractCryptoProvider getCryptoProvider()
{
return cryptoProvider;
}
public static void setCryptoProvider(AbstractCryptoProvider cryptoProvider)
{
DatabaseDescriptor.cryptoProvider = cryptoProvider;
}
public static IAuthenticator getAuthenticator()
{
return authenticator;

View File

@ -0,0 +1,205 @@
/*
* 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.security;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.utils.FBUtilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.Provider;
import java.security.Security;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static java.lang.String.format;
import static org.apache.cassandra.config.CassandraRelevantProperties.FAIL_ON_MISSING_CRYPTO_PROVIDER;
public abstract class AbstractCryptoProvider
{
protected static final Logger logger = LoggerFactory.getLogger(AbstractCryptoProvider.class);
public static final String FAIL_ON_MISSING_PROVIDER_KEY = "fail_on_missing_provider";
protected final boolean failOnMissingProvider;
private final Map<String, String> properties;
public AbstractCryptoProvider(Map<String, String> args)
{
this.properties = args == null ? new HashMap<>() : args;
boolean failOnMissingProviderFromProperties = Boolean.parseBoolean(this.properties.getOrDefault(FAIL_ON_MISSING_PROVIDER_KEY, "false"));
failOnMissingProvider = FAIL_ON_MISSING_CRYPTO_PROVIDER.getBoolean(failOnMissingProviderFromProperties);
}
/**
* Returns unmodifiable properties of this crypto provider
*
* @return crypto provider properties
*/
public Map<String, String> getProperties()
{
return Collections.unmodifiableMap(properties);
}
/**
* Returns name of the provider, as returned from {@link Provider#getName()}
*
* @return name of the provider
*/
public abstract String getProviderName();
/**
* Returns the name of the class which installs specific provider of name {@link #getProviderName()}.
*
* @return name of class of provider
*/
public abstract String getProviderClassAsString();
/**
* Returns a runnable which installs this crypto provider.
*
* @return runnable which installs this provider
*/
protected abstract Runnable installator();
/**
* Returns boolean telling if this provider was installed properly.
*
* @return {@code true} if provider was installed properly, {@code false} otherwise.
*/
protected abstract boolean isHealthyInstallation() throws Exception;
/**
* The default installation runs {@link AbstractCryptoProvider#installator()} and after that
* {@link AbstractCryptoProvider#isHealthyInstallation()}.
* <p>
* If any step fails, it will not throw an exception unless the parameter
* {@link AbstractCryptoProvider#FAIL_ON_MISSING_PROVIDER_KEY} is {@code true}.
*/
public void install() throws Exception
{
String failureMessage = null;
Throwable t = null;
try
{
if (JREProvider.class.getName().equals(getProviderClassAsString()))
{
logger.info(format("Installation of a crypto provider was skipped as %s was used.", JREProvider.class.getName()));
return;
}
FBUtilities.classForName(getProviderClassAsString(), "crypto provider");
String providerName = getProviderName();
int providerPosition = getProviderPosition(providerName);
if (providerPosition > 0)
{
if (providerPosition == 1)
{
logger.info("{} was already installed on position {}.", providerName, providerPosition);
}
else if (failOnMissingProvider)
{
throw new IllegalStateException(String.format("%s was already installed on position %s.", providerName, providerPosition));
}
else
{
logger.warn("{} was already installed on position {}. Check the configuration of " +
"JRE and either remove the provider from java.security or do not install this provider " +
"by Cassandra.", providerName, providerPosition);
return;
}
}
else
{
Runnable r = installator();
if (r == null)
throw new IllegalStateException("Installator runnable can not be null!");
else
r.run();
}
if (isHealthyInstallation())
logger.info("{} health check OK.", getProviderName());
else
failureMessage = format("%s has not passed the health check. " +
"Check node's architecture (`uname -m`) is supported, see lib/<arch> subdirectories. " +
"The correct architecture-specific library for %s needs to be on the classpath. ",
getProviderName(),
getProviderClassAsString());
}
catch (ConfigurationException ex)
{
failureMessage = getProviderClassAsString() + " is not on the class path! " +
"Check node's architecture (`uname -m`) is supported, see lib/<arch> subdirectories. " +
"The correct architecture-specific library for needs to be on the classpath.";
}
catch (Throwable ex)
{
failureMessage = format("The installation of %s was not successful, reason: %s",
getProviderClassAsString(), ex.getMessage());
t = ex;
}
if (failureMessage != null)
{
// To be sure there is not any leftover, proactively remove this provider in case of any failure.
// This method returns silently if the provider is not installed or if name is null.
try
{
uninstall();
}
catch (Throwable throwable)
{
logger.warn("Uninstallation of {} failed", getProviderName(), throwable);
}
if (failOnMissingProvider)
throw new ConfigurationException(failureMessage, t);
else
logger.warn(failureMessage);
}
}
/**
* Uninstalls this crypto provider of name {@link #getProviderName()}
*
* @see Security#removeProvider(String)
*/
public void uninstall()
{
Security.removeProvider(getProviderName());
}
private int getProviderPosition(String providerName)
{
Provider[] providers = Security.getProviders();
for (int i = 0; i < providers.length; i++)
{
if (providers[i].getName().equals(providerName))
{
return i + 1;
}
}
return -1;
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.security;
import java.util.Map;
import javax.crypto.Cipher;
import com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider;
/**
* Default crypto provider tries to install AmazonCorrettoCryptoProvider.
* <p>
* The implementation falls back to in-built crypto provider in JRE if the installation
* is not successful.
*/
public class DefaultCryptoProvider extends AbstractCryptoProvider
{
public DefaultCryptoProvider(Map<String, String> args)
{
super(args);
}
@Override
public String getProviderName()
{
return "AmazonCorrettoCryptoProvider";
}
@Override
public String getProviderClassAsString()
{
return "com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider";
}
@Override
public Runnable installator()
{
return AmazonCorrettoCryptoProvider::install;
}
@Override
public boolean isHealthyInstallation() throws Exception
{
if (!getProviderName().equals(Cipher.getInstance("AES/GCM/NoPadding").getProvider().getName()))
return false;
AmazonCorrettoCryptoProvider.INSTANCE.assertHealthy();
return true;
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.security;
import java.util.Map;
/**
* Crypto provider which does nothing. Handy for situations when a user
* wants to completely bypass crypto provider installation.
*/
public class JREProvider extends AbstractCryptoProvider
{
public JREProvider(Map<String, String> properties)
{
super(properties);
}
@Override
public String getProviderName()
{
return JREProvider.class.getSimpleName();
}
@Override
public String getProviderClassAsString()
{
return JREProvider.class.getName();
}
@Override
protected Runnable installator()
{
return () -> {};
}
@Override
protected boolean isHealthyInstallation() throws Exception
{
return true;
}
}

View File

@ -87,6 +87,7 @@ import org.apache.cassandra.io.util.DataOutputBufferFixed;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.security.AbstractCryptoProvider;
import org.apache.cassandra.security.ISslContextFactory;
import org.apache.cassandra.utils.concurrent.FutureCombiner;
import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException;
@ -702,6 +703,26 @@ public class FBUtilities
}
}
public static AbstractCryptoProvider newCryptoProvider(String className, Map<String, String> parameters) throws ConfigurationException
{
try
{
if (!className.contains("."))
className = "org.apache.cassandra.security." + className;
Class<?> cryptoProviderClass = FBUtilities.classForName(className, "crypto provider class");
return (AbstractCryptoProvider) cryptoProviderClass.getConstructor(Map.class).newInstance(Collections.unmodifiableMap(parameters));
}
catch (Exception e)
{
// no need to wrap it in another ConfgurationException if FBUtilities.classForName might throw it
if (e instanceof ConfigurationException)
throw (ConfigurationException) e;
else
throw new ConfigurationException(String.format("Unable to create an instance of crypto provider for %s", className), e);
}
}
/**
* @return The Class for the given name.
* @param classname Fully qualified classname.

View File

@ -923,6 +923,8 @@ public class Instance extends IsolatedExecutor implements IInvokableInstance
error = parallelRun(error, executor, this::stopJmx);
error = parallelRun(error, executor, () -> DatabaseDescriptor.getCryptoProvider().uninstall());
// Make sure any shutdown hooks registered for DeleteOnExit are released to prevent
// references to the instance class loaders from being held
if (graceful)

View File

@ -0,0 +1,203 @@
/*
* 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 java.security.Provider;
import java.security.Security;
import java.util.HashMap;
import org.junit.Before;
import org.junit.Test;
import com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import org.apache.cassandra.distributed.Cluster;
import org.apache.cassandra.distributed.api.IIsolatedExecutor.SerializableCallable;
import org.apache.cassandra.security.DefaultCryptoProvider;
import org.apache.cassandra.security.JREProvider;
import static java.lang.String.format;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class CryptoProviderTest extends TestBaseImpl
{
@Before
public void beforeTest()
{
assertNotInstalledDefaultProvider();
}
@Test
public void testDefaultCryptoProvider() throws Throwable
{
try (Cluster cluster = init(Cluster.build(1)
.withConfig(config -> config.set("crypto_provider",
new HashMap<>()
{{
put("class_name", DefaultCryptoProvider.class.getName());
}}))
.start()))
{
assertTrue("Amazon Corretto Crypto Provider should be installed!",
cluster.get(1).callOnInstance((SerializableCallable<Boolean>) () -> {
Provider provider = Security.getProviders()[0];
return provider != null && AmazonCorrettoCryptoProvider.PROVIDER_NAME.equals(provider.getName());
}));
}
finally
{
Security.removeProvider(AmazonCorrettoCryptoProvider.PROVIDER_NAME);
}
}
@Test
public void testUsingJREProvider() throws Throwable
{
try (Cluster cluster = init(Cluster.build(1)
.withConfig(config -> config.set("crypto_provider",
new HashMap<>()
{{
put("class_name", JREProvider.class.getName());
}}))
.start()))
{
assertTrue("Amazon Corretto Crypto Provider should not be installed!",
cluster.get(1).callOnInstance((SerializableCallable<Boolean>) () -> {
Provider provider = Security.getProviders()[0];
return provider != null && !AmazonCorrettoCryptoProvider.PROVIDER_NAME.equals(provider.getName());
}));
}
finally
{
Security.removeProvider(AmazonCorrettoCryptoProvider.PROVIDER_NAME);
}
}
@Test
public void testUsingNoProviderUsesJREProvider() throws Throwable
{
// crypto_provider = null will be in Descriptor if it is not set in yaml (e.g. nodes being upgraded
// with the old cassandra.yaml, or if it is commented out
try (Cluster cluster = init(Cluster.build(1).start()))
{
assertTrue("Amazon Corretto Crypto Provider should not be installed!",
cluster.get(1).callOnInstance((SerializableCallable<Boolean>) () -> {
Provider provider = Security.getProviders()[0];
return provider != null && !AmazonCorrettoCryptoProvider.PROVIDER_NAME.equals(provider.getName());
}));
}
finally
{
Security.removeProvider(AmazonCorrettoCryptoProvider.PROVIDER_NAME);
}
}
@Test
public void testFailedDefaultProviderInstallationShouldResumeStartup() throws Throwable
{
try (Cluster cluster = builder().withNodes(1)
.withInstanceInitializer(BB::install)
.withConfig(config -> config.set("crypto_provider",
new HashMap<>()
{{
put("class_name", DefaultCryptoProvider.class.getName());
}}))
.createWithoutStarting())
{
cluster.get(1).startup();
assertTrue("Amazon Corretto Crypto Provider should not be installed!",
cluster.get(1).callOnInstance((SerializableCallable<Boolean>) () -> {
Provider provider = Security.getProviders()[0];
return provider != null && !AmazonCorrettoCryptoProvider.PROVIDER_NAME.equals(provider.getName());
}));
}
catch (Exception ex)
{
fail("Startup should not fail! It should fallback to in-built providers and continue to boot");
}
finally
{
Security.removeProvider(AmazonCorrettoCryptoProvider.PROVIDER_NAME);
}
}
@Test
public void testFailedDefaultProviderInstallationShouldFailStartupOnFailOnMissingProperty() throws Throwable
{
try (Cluster cluster = builder().withNodes(1)
.withInstanceInitializer(BB::install)
.withConfig(config -> config.set("crypto_provider",
new HashMap<>()
{{
put("class_name", DefaultCryptoProvider.class.getName());
put("parameters", new HashMap<>()
{{
put("fail_on_missing_provider", "true");
}});
}}))
.createWithoutStarting())
{
cluster.get(1).startup();
fail("Startup should fail! It should not fallback to in-built providers and continue to boot " +
"as fail_on_missing_provider is set to true");
}
catch (Exception ex)
{
assertEquals(format("The installation of %s was not successful, reason: exception from test", AmazonCorrettoCryptoProvider.class.getName()),
ex.getMessage());
assertNotInstalledDefaultProvider();
}
finally
{
Security.removeProvider(AmazonCorrettoCryptoProvider.PROVIDER_NAME);
}
}
public static void assertNotInstalledDefaultProvider()
{
for (Provider provider : Security.getProviders())
assertNotEquals(AmazonCorrettoCryptoProvider.PROVIDER_NAME, provider.getName());
}
public static class BB
{
public static void install(ClassLoader cl, Integer num)
{
new ByteBuddy().redefine(DefaultCryptoProvider.class)
.method(named("installator"))
.intercept(MethodDelegation.to(BB.class))
.make()
.load(cl, ClassLoadingStrategy.Default.INJECTION);
}
public static Runnable installator()
{
throw new RuntimeException("exception from test");
}
}
}

View File

@ -263,6 +263,7 @@ public class DatabaseDescriptorRefTest
"org.apache.cassandra.security.ISslContextFactory",
"org.apache.cassandra.security.SSLFactory",
"org.apache.cassandra.service.CacheService$CacheType",
"org.apache.cassandra.security.AbstractCryptoProvider",
"org.apache.cassandra.transport.ProtocolException",
"org.apache.cassandra.utils.Closeable",
"org.apache.cassandra.utils.CloseableIterator",

View File

@ -0,0 +1,343 @@
/*
* 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.security;
import java.security.Provider;
import java.security.Security;
import java.util.HashMap;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.ParameterizedClass;
import org.apache.cassandra.distributed.shared.WithProperties;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.utils.FBUtilities;
import org.mockito.MockedStatic;
import static com.google.common.collect.ImmutableMap.of;
import static java.lang.String.format;
import static org.apache.cassandra.config.CassandraRelevantProperties.CRYPTO_PROVIDER_CLASS_NAME;
import static org.apache.cassandra.config.CassandraRelevantProperties.FAIL_ON_MISSING_CRYPTO_PROVIDER;
import static org.apache.cassandra.config.CassandraRelevantProperties.TEST_SKIP_CRYPTO_PROVIDER_INSTALLATION;
import static org.apache.cassandra.security.AbstractCryptoProvider.FAIL_ON_MISSING_PROVIDER_KEY;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.spy;
public class CryptoProviderTest
{
@BeforeClass
public static void beforeClass()
{
TEST_SKIP_CRYPTO_PROVIDER_INSTALLATION.setBoolean(true);
DatabaseDescriptor.daemonInitialization();
TEST_SKIP_CRYPTO_PROVIDER_INSTALLATION.setBoolean(false);
}
@Before
public void beforeTest()
{
// be sure it is uninstalled / reset
DatabaseDescriptor.getRawConfig().crypto_provider = null;
DatabaseDescriptor.setCryptoProvider(null);
Security.removeProvider(AmazonCorrettoCryptoProvider.PROVIDER_NAME);
assertNotInstalledDefaultProvider();
}
public static void assertNotInstalledDefaultProvider()
{
for (Provider provider : Security.getProviders())
assertNotEquals(AmazonCorrettoCryptoProvider.PROVIDER_NAME, provider.getName());
}
@Test
public void testCryptoProviderClassSystemProperty()
{
try (WithProperties properties = new WithProperties().set(CRYPTO_PROVIDER_CLASS_NAME, TestJREProvider.class.getName()))
{
DatabaseDescriptor.applyCryptoProvider();
assertEquals(TestJREProvider.class.getSimpleName(), DatabaseDescriptor.getCryptoProvider().getProviderName());
}
}
@Test
public void testNoCryptoProviderInstallationUseJREProvider()
{
DatabaseDescriptor.applyCryptoProvider();
assertEquals("JREProvider", DatabaseDescriptor.getCryptoProvider().getProviderName());
}
@Test
public void testCryptoProviderInstallationWithNullParameters()
{
DatabaseDescriptor.getRawConfig().crypto_provider = new ParameterizedClass(TestJREProvider.class.getName(), null);
DatabaseDescriptor.applyCryptoProvider();
AbstractCryptoProvider cryptoProvider = DatabaseDescriptor.getCryptoProvider();
assertThat(cryptoProvider.getProviderName()).isEqualTo(TestJREProvider.class.getSimpleName());
assertThat(cryptoProvider.getProperties()).isNotNull()
.isNotEmpty()
.hasSize(1)
.containsKeys("fail_on_missing_provider")
.containsValues("false");
}
@Test
public void testCryptoProviderInstallationWithEmptyParameters()
{
DatabaseDescriptor.getRawConfig().crypto_provider = new ParameterizedClass(TestJREProvider.class.getName(), of());
DatabaseDescriptor.applyCryptoProvider();
AbstractCryptoProvider cryptoProvider = DatabaseDescriptor.getCryptoProvider();
assertThat(cryptoProvider.getProviderName()).isEqualTo(TestJREProvider.class.getSimpleName());
assertThat(cryptoProvider.getProperties()).isNotNull()
.isNotEmpty()
.hasSize(1)
.containsKeys("fail_on_missing_provider")
.containsValues("false");
}
@Test
public void testCryptoProviderInstallationWithNotEmptyParameters()
{
DatabaseDescriptor.getRawConfig().crypto_provider = new ParameterizedClass(TestJREProvider.class.getName(),
of("k1", "v1", "k2", "v2"));
DatabaseDescriptor.applyCryptoProvider();
AbstractCryptoProvider cryptoProvider = DatabaseDescriptor.getCryptoProvider();
assertThat(cryptoProvider.getProviderName()).isEqualTo(TestJREProvider.class.getSimpleName());
assertThat(cryptoProvider.getProperties()).isNotNull()
.isNotEmpty()
.hasSize(3)
.containsKeys("k1", "k2", "fail_on_missing_provider")
.containsValues("v1", "v2", "false");
}
@Test
public void testCryptoProviderInstallationWithSimpleClassName()
{
DatabaseDescriptor.getRawConfig().crypto_provider = new ParameterizedClass(TestJREProvider.class.getSimpleName(), null);
DatabaseDescriptor.applyCryptoProvider();
AbstractCryptoProvider cryptoProvider = DatabaseDescriptor.getCryptoProvider();
assertThat(cryptoProvider.getProviderName()).isEqualTo(TestJREProvider.class.getSimpleName());
assertThat(cryptoProvider.getProperties()).isNotNull()
.isNotEmpty()
.hasSize(1)
.containsKeys("fail_on_missing_provider")
.containsValues("false");
}
@Test
public void testUnableToCreateDefaultCryptoProvider()
{
try (MockedStatic<FBUtilities> fbUtilitiesMock = mockStatic(FBUtilities.class))
{
DatabaseDescriptor.getRawConfig().crypto_provider = new ParameterizedClass(DefaultCryptoProvider.class.getName(),
of("k1", "v1", "k2", "v2"));
fbUtilitiesMock.when(() -> FBUtilities.classForName(DefaultCryptoProvider.class.getName(), "crypto provider class"))
.thenThrow(new RuntimeException("exception from test"));
fbUtilitiesMock.when(() -> FBUtilities.newCryptoProvider(anyString(), anyMap())).thenCallRealMethod();
assertThatThrownBy(DatabaseDescriptor::applyCryptoProvider)
.isInstanceOf(ConfigurationException.class)
.hasCauseInstanceOf(RuntimeException.class)
.hasMessage("Unable to create an instance of crypto provider for " + DefaultCryptoProvider.class.getName())
.hasRootCauseMessage("exception from test");
assertNotInstalledDefaultProvider();
}
}
@Test
public void testFailOnMissingProviderSystemProperty()
{
try (WithProperties properties = new WithProperties().set(FAIL_ON_MISSING_CRYPTO_PROVIDER, "true")
.set(CRYPTO_PROVIDER_CLASS_NAME, InvalidCryptoProvider.class.getName()))
{
assertThatExceptionOfType(ConfigurationException.class)
.isThrownBy(DatabaseDescriptor::applyCryptoProvider)
.withMessage("some.package.non.existing.ClassName is not on the class path! Check node's architecture " +
"(`uname -m`) is supported, see lib/<arch> subdirectories. The correct architecture-specific " +
"library for needs to be on the classpath.");
}
}
@Test
public void testCryptoProviderInstallation() throws Exception
{
AbstractCryptoProvider provider = new DefaultCryptoProvider(new HashMap<>());
assertFalse(provider.failOnMissingProvider);
Provider originalProvider = Security.getProviders()[0];
provider.install();
assertTrue(provider.isHealthyInstallation());
Provider installedProvider = Security.getProviders()[0];
assertEquals(installedProvider.getName(), provider.getProviderName());
provider.uninstall();
Provider currentProvider = Security.getProviders()[0];
assertNotEquals(currentProvider.getName(), installedProvider.getName());
assertEquals(originalProvider.getName(), currentProvider.getName());
}
@Test
public void testInvalidProviderInstallator()
{
AbstractCryptoProvider spiedProvider = spy(new DefaultCryptoProvider(of(FAIL_ON_MISSING_PROVIDER_KEY, "true")));
Runnable installator = () ->
{
throw new RuntimeException("invalid installator");
};
doReturn(installator).when(spiedProvider).installator();
assertThatExceptionOfType(ConfigurationException.class)
.isThrownBy(spiedProvider::install)
.withRootCauseInstanceOf(RuntimeException.class)
.withMessage("The installation of %s was not successful, reason: invalid installator", spiedProvider.getProviderClassAsString());
}
@Test
public void testNullInstallatorThrowsException()
{
AbstractCryptoProvider spiedProvider = spy(new DefaultCryptoProvider(of(FAIL_ON_MISSING_PROVIDER_KEY, "true")));
doReturn(null).when(spiedProvider).installator();
assertThatExceptionOfType(ConfigurationException.class)
.isThrownBy(spiedProvider::install)
.withRootCauseInstanceOf(RuntimeException.class)
.withMessage("The installation of %s was not successful, reason: Installator runnable can not be null!", spiedProvider.getProviderClassAsString());
}
@Test
public void testProviderHealthcheckerReturningFalse() throws Exception
{
AbstractCryptoProvider spiedProvider = spy(new DefaultCryptoProvider(of(FAIL_ON_MISSING_PROVIDER_KEY, "true")));
doReturn(false).when(spiedProvider).isHealthyInstallation();
assertThatExceptionOfType(ConfigurationException.class)
.isThrownBy(spiedProvider::install)
.withCause(null)
.withMessage(format("%s has not passed the health check. " +
"Check node's architecture (`uname -m`) is supported, see lib/<arch> subdirectories. " +
"The correct architecture-specific library for %s needs to be on the classpath. ",
spiedProvider.getProviderName(),
spiedProvider.getProviderClassAsString()));
}
@Test
public void testHealthcheckerThrowingException() throws Exception
{
AbstractCryptoProvider spiedProvider = spy(new DefaultCryptoProvider(of(FAIL_ON_MISSING_PROVIDER_KEY, "true")));
Throwable t = new RuntimeException("error in health checker");
doThrow(t).when(spiedProvider).isHealthyInstallation();
assertThatExceptionOfType(ConfigurationException.class)
.isThrownBy(spiedProvider::install)
.withCauseInstanceOf(RuntimeException.class)
.withMessage(format("The installation of %s was not successful, reason: %s",
spiedProvider.getProviderClassAsString(), t.getMessage()));
}
@Test
public void testProviderNotOnClassPathWithPropertyInYaml() throws Exception
{
InvalidCryptoProvider cryptoProvider = new InvalidCryptoProvider(of(FAIL_ON_MISSING_PROVIDER_KEY, "true"));
assertThatExceptionOfType(ConfigurationException.class)
.isThrownBy(cryptoProvider::install)
.withMessage("some.package.non.existing.ClassName is not on the class path! Check node's architecture " +
"(`uname -m`) is supported, see lib/<arch> subdirectories. " +
"The correct architecture-specific library for needs to be on the classpath.");
}
@Test
public void testProviderNotOnClassPathWithSystemProperty()
{
try (WithProperties properties = new WithProperties().set(FAIL_ON_MISSING_CRYPTO_PROVIDER, "true"))
{
InvalidCryptoProvider cryptoProvider = new InvalidCryptoProvider(of());
assertThatExceptionOfType(ConfigurationException.class)
.isThrownBy(cryptoProvider::install)
.withMessage("some.package.non.existing.ClassName is not on the class path! Check node's architecture " +
"(`uname -m`) is supported, see lib/<arch> subdirectories. The correct architecture-specific " +
"library for needs to be on the classpath.");
}
}
@Test
public void testProviderInstallsJustOnce() throws Exception
{
Provider[] originalProviders = Security.getProviders();
int originalProvidersCount = originalProviders.length;
Provider originalProvider = Security.getProviders()[0];
AbstractCryptoProvider provider = new DefaultCryptoProvider(new HashMap<>());
provider.install();
assertEquals(provider.getProviderName(), Security.getProviders()[0].getName());
assertEquals(originalProvidersCount + 1, Security.getProviders().length);
// install one more time -> it will do nothing
provider.install();
assertEquals(provider.getProviderName(), Security.getProviders()[0].getName());
assertEquals(originalProvidersCount + 1, Security.getProviders().length);
provider.uninstall();
assertEquals(originalProvider.getName(), Security.getProviders()[0].getName());
assertEquals(originalProvidersCount, Security.getProviders().length);
}
@Test
public void testInstallationOfIJREProvider() throws Exception
{
String originalProvider = Security.getProviders()[0].getName();
JREProvider jreProvider = new JREProvider(of());
jreProvider.install();
assertEquals(originalProvider, Security.getProviders()[0].getName());
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.security;
import java.util.Map;
public class InvalidCryptoProvider extends AbstractCryptoProvider
{
public InvalidCryptoProvider(Map<String, String> properties)
{
super(properties);
}
@Override
public String getProviderName()
{
return null;
}
@Override
public String getProviderClassAsString()
{
return "some.package.non.existing.ClassName";
}
@Override
protected Runnable installator()
{
return () -> {};
}
@Override
protected boolean isHealthyInstallation() throws Exception
{
return false;
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.security;
import java.util.Map;
// to be public because it is going to be instantiated by reflection in FBUtilties
public class TestJREProvider extends JREProvider
{
public TestJREProvider(Map<String, String> properties)
{
super(properties);
}
@Override
public String getProviderName()
{
return TestJREProvider.class.getSimpleName();
}
@Override
public String getProviderClassAsString()
{
return TestJREProvider.class.getName();
}
}