Skip to content

Commit

Permalink
Add FIPS support to new junit-rule test clusters framework (#93021) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
mark-vieira committed Jan 24, 2023
1 parent 71c7534 commit e9af78c
Show file tree
Hide file tree
Showing 11 changed files with 281 additions and 15 deletions.
10 changes: 10 additions & 0 deletions build-tools-internal/src/main/groovy/elasticsearch.fips.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

import org.elasticsearch.gradle.internal.ExportElasticsearchBuildResourcesTask
import org.elasticsearch.gradle.internal.info.BuildParams
import org.elasticsearch.gradle.internal.test.rest.RestTestBasePlugin
import org.elasticsearch.gradle.testclusters.StandaloneRestIntegTestTask
import org.elasticsearch.gradle.testclusters.TestClustersAware
import org.elasticsearch.gradle.testclusters.TestDistribution

Expand Down Expand Up @@ -77,6 +79,14 @@ if (BuildParams.inFipsJvm) {
keystorePassword 'keystore-password'
}
}

plugins.withType(RestTestBasePlugin) {
tasks.withType(StandaloneRestIntegTestTask).configureEach {
inputs.files(extraFipsJarsConfiguration).withNormalizer(ClasspathNormalizer)
nonInputProperties.systemProperty "tests.cluster.fips.jars.path", "${-> extraFipsJarsConfiguration.asPath}"
}
}

project.tasks.withType(Test).configureEach { Test task ->
dependsOn 'fipsResources'
task.systemProperty('javax.net.ssl.trustStorePassword', 'password')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ public abstract class AbstractLocalSpecBuilder<T extends LocalSpecBuilder<?>> im
private final Set<FeatureFlag> features = new HashSet<>();
private final Map<String, String> keystoreSettings = new HashMap<>();
private final Map<String, Resource> extraConfigFiles = new HashMap<>();
private final Map<String, String> systemProperties = new HashMap<>();
private DistributionType distributionType;
private String keystorePassword;

protected AbstractLocalSpecBuilder(AbstractLocalSpecBuilder<?> parent) {
this.parent = parent;
Expand Down Expand Up @@ -146,6 +148,26 @@ public Map<String, Resource> getExtraConfigFiles() {
return inherit(() -> parent.getExtraConfigFiles(), extraConfigFiles);
}

@Override
public T systemProperty(String property, String value) {
this.systemProperties.put(property, value);
return cast(this);
}

public Map<String, String> getSystemProperties() {
return inherit(() -> parent.getSystemProperties(), systemProperties);
}

@Override
public T keystorePassword(String password) {
this.keystorePassword = password;
return cast(this);
}

public String getKeystorePassword() {
return inherit(() -> parent.getKeystorePassword(), keystorePassword);
}

private <T> List<T> inherit(Supplier<List<T>> parent, List<T> child) {
List<T> combinedList = new ArrayList<>();
if (this.parent != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class DefaultLocalClusterSpecBuilder extends AbstractLocalSpecBuilder<Loc

public DefaultLocalClusterSpecBuilder() {
super(null);
this.apply(new FipsEnabledClusterConfigProvider());
this.settings(new DefaultSettingsProvider());
this.environment(new DefaultEnvironmentProvider());
this.rolesFile(Resource.fromClasspath("default_test_roles.yml"));
Expand Down Expand Up @@ -146,7 +147,9 @@ private LocalNodeSpec build(LocalClusterSpec cluster) {
Optional.ofNullable(getDistributionType()).orElse(DistributionType.INTEG_TEST),
getFeatures(),
getKeystoreSettings(),
getExtraConfigFiles()
getKeystorePassword(),
getExtraConfigFiles(),
getSystemProperties()
);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.test.cluster.local;

import org.elasticsearch.test.cluster.util.resource.Resource;

public class FipsEnabledClusterConfigProvider implements LocalClusterConfigProvider {

@Override
public void apply(LocalClusterSpecBuilder builder) {
if (isFipsEnabled()) {
builder.configFile(
"fips_java.security",
Resource.fromClasspath(isOracleJvm() ? "fips/fips_java_oracle.security" : "fips/fips_java.security")
)
.configFile("fips_java.policy", Resource.fromClasspath("fips/fips_java.policy"))
.configFile("cacerts.bcfks", Resource.fromClasspath("fips/cacerts.bcfks"))
.systemProperty("java.security.properties", "=${ES_PATH_CONF}/fips_java.security")
.systemProperty("java.security.policy", "=${ES_PATH_CONF}/fips_java.policy")
.systemProperty("javax.net.ssl.trustStore", "${ES_PATH_CONF}/cacerts.bcfks")
.systemProperty("javax.net.ssl.trustStorePassword", "password")
.systemProperty("javax.net.ssl.keyStorePassword", "password")
.systemProperty("javax.net.ssl.keyStoreType", "BCFKS")
.systemProperty("org.bouncycastle.fips.approved_only", "true")
.setting("network.host", "_local:ipv4_")
.setting("xpack.security.enabled", "false")
.setting("xpack.security.fips_mode.enabled", "true")
.setting("xpack.license.self_generated.type", "trial")
.setting("xpack.security.authc.password_hashing.algorithm", "pbkdf2_stretch")
.keystorePassword("keystore-password");
}
}

private static boolean isFipsEnabled() {
return Boolean.getBoolean("tests.fips.enabled");
}

private static boolean isOracleJvm() {
return System.getProperty("java.vendor").toLowerCase().contains("oracle");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.util.Arrays;
Expand All @@ -57,6 +58,7 @@ public class LocalClusterFactory implements ClusterFactory<LocalClusterSpec, Loc
private static final Map<Pair<Version, DistributionType>, DistributionDescriptor> TEST_DISTRIBUTIONS = new ConcurrentHashMap<>();
private static final String TESTS_CLUSTER_MODULES_PATH_SYSPROP = "tests.cluster.modules.path";
private static final String TESTS_CLUSTER_PLUGINS_PATH_SYSPROP = "tests.cluster.plugins.path";
private static final String TESTS_CLUSTER_FIPS_JAR_PATH_SYSPROP = "tests.cluster.fips.jars.path";
public static final Pattern BUNDLE_ARTIFACT_PATTERN = Pattern.compile("(.+)(?:-\\d\\.\\d\\.\\d-SNAPSHOT\\.zip)?");

private final Path baseWorkingDir;
Expand Down Expand Up @@ -105,6 +107,7 @@ public synchronized void start() {
distributionDescriptor = resolveDistribution();
LOGGER.info("Distribution for node '{}': {}", spec.getName(), distributionDescriptor);
initializeWorkingDirectory();
copyExtraJarFiles();
installPlugins();
if (spec.getDistributionType() == DistributionType.INTEG_TEST) {
installModules();
Expand Down Expand Up @@ -214,6 +217,22 @@ private void initializeWorkingDirectory() {
}
}

private void copyExtraJarFiles() {
String fipsJarsPath = System.getProperty(TESTS_CLUSTER_FIPS_JAR_PATH_SYSPROP);
if (fipsJarsPath != null) {
LOGGER.info("FIPS is enabled. Copying FIPS jars for node: {}", this);
Path libsDir = distributionDir.resolve("lib");
Arrays.stream(fipsJarsPath.split(File.pathSeparator)).map(Path::of).forEach(jar -> {
LOGGER.info("Copying jar {} to {}", jar, libsDir);
try {
Files.copy(jar, libsDir.resolve(jar.getFileName()), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new UncheckedIOException("An error occurred copy extra jar files for: " + this, e);
}
});
}
}

private DistributionDescriptor resolveDistribution() {
return TEST_DISTRIBUTIONS.computeIfAbsent(
Pair.of(spec.getVersion(), spec.getDistributionType()),
Expand Down Expand Up @@ -274,27 +293,38 @@ private void copyExtraConfigFiles() {

private void createKeystore() {
try {
ProcessUtils.exec(
workingDir,
OS.conditional(
c -> c.onWindows(() -> distributionDir.resolve("bin").resolve("elasticsearch-keystore.bat"))
.onUnix(() -> distributionDir.resolve("bin").resolve("elasticsearch-keystore"))
),
getEnvironmentVariables(),
false,
"-v",
"create"
).waitFor();
Path executable = OS.conditional(
c -> c.onWindows(() -> distributionDir.resolve("bin").resolve("elasticsearch-keystore.bat"))
.onUnix(() -> distributionDir.resolve("bin").resolve("elasticsearch-keystore"))
);

if (spec.getKeystorePassword() == null || spec.getKeystorePassword().isEmpty()) {
ProcessUtils.exec(workingDir, executable, getEnvironmentVariables(), false, "-v", "create").waitFor();
} else {
ProcessUtils.exec(
spec.getKeystorePassword() + "\n" + spec.getKeystorePassword(),
workingDir,
executable,
getEnvironmentVariables(),
false,
"create",
"-p"
).waitFor();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}

private void addKeystoreSettings() {
spec.getKeystoreSettings().forEach((key, value) -> {
String input = spec.getKeystorePassword() == null || spec.getKeystorePassword().isEmpty()
? value
: spec.getKeystorePassword() + "\n" + value;

try {
ProcessUtils.exec(
value,
input,
workingDir,
OS.conditional(
c -> c.onWindows(() -> distributionDir.resolve("bin").resolve("elasticsearch-keystore.bat"))
Expand Down Expand Up @@ -480,6 +510,7 @@ private void installModule(String moduleName, List<Path> modulePaths) {

private void startElasticsearch() {
process = ProcessUtils.exec(
spec.getKeystorePassword(),
workingDir,
OS.conditional(
c -> c.onWindows(() -> distributionDir.resolve("bin").resolve("elasticsearch.bat"))
Expand Down Expand Up @@ -508,12 +539,22 @@ private Map<String, String> getEnvironmentVariables() {
.collect(Collectors.joining(" "));
}

String systemProperties = "";
if (spec.getSystemProperties().isEmpty() == false) {
systemProperties = spec.getSystemProperties()
.entrySet()
.stream()
.map(entry -> "-D" + entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining(" "));
}

String heapSize = System.getProperty("tests.heap.size", "512m");
environment.put("ES_JAVA_OPTS", "-Xms" + heapSize + " -Xmx" + heapSize + " -ea -esa "
// Support passing in additional JVM arguments
+ System.getProperty("tests.jvm.argline", "")
+ " "
+ featureFlagProperties);
+ featureFlagProperties
+ systemProperties);

return environment;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ public static class LocalNodeSpec {
private final DistributionType distributionType;
private final Set<FeatureFlag> features;
private final Map<String, String> keystoreSettings;
private final String keystorePassword;
private final Map<String, Resource> extraConfigFiles;
private final Map<String, String> systemProperties;

public LocalNodeSpec(
LocalClusterSpec cluster,
Expand All @@ -94,7 +96,9 @@ public LocalNodeSpec(
DistributionType distributionType,
Set<FeatureFlag> features,
Map<String, String> keystoreSettings,
Map<String, Resource> extraConfigFiles
String keystorePassword,
Map<String, Resource> extraConfigFiles,
Map<String, String> systemProperties
) {
this.cluster = cluster;
this.name = name;
Expand All @@ -108,7 +112,9 @@ public LocalNodeSpec(
this.distributionType = distributionType;
this.features = features;
this.keystoreSettings = keystoreSettings;
this.keystorePassword = keystorePassword;
this.extraConfigFiles = extraConfigFiles;
this.systemProperties = systemProperties;
}

public LocalClusterSpec getCluster() {
Expand Down Expand Up @@ -151,10 +157,18 @@ public Map<String, String> getKeystoreSettings() {
return keystoreSettings;
}

public String getKeystorePassword() {
return keystorePassword;
}

public Map<String, Resource> getExtraConfigFiles() {
return extraConfigFiles;
}

public Map<String, String> getSystemProperties() {
return systemProperties;
}

public boolean isSecurityEnabled() {
return Boolean.parseBoolean(
resolveSettings().getOrDefault("xpack.security.enabled", getVersion().onOrAfter("8.0.0") ? "true" : "false")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,18 @@ interface LocalSpecBuilder<T extends LocalSpecBuilder<?>> {
*/
T keystore(String key, String value);

/**
* Sets the security setting keystore password.
*/
T keystorePassword(String password);

/**
* Adds a file to the node config directory
*/
T configFile(String fileName, Resource configFile);

/**
* Adds a system property to node JVM arguments.
*/
T systemProperty(String property, String value);
}
Binary file not shown.
16 changes: 16 additions & 0 deletions test/test-clusters/src/main/resources/fips/fips_java.policy
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
grant {
permission java.security.SecurityPermission "putProviderProperty.BCFIPS";
permission java.security.SecurityPermission "putProviderProperty.BCJSSE";
permission java.lang.RuntimePermission "getProtectionDomain";
permission java.util.PropertyPermission "java.runtime.name", "read";
permission org.bouncycastle.crypto.CryptoServicesPermission "tlsAlgorithmsEnabled";
//io.netty.handler.codec.DecoderException
permission java.lang.RuntimePermission "accessClassInPackage.sun.security.internal.spec";
//java.security.InvalidAlgorithmParameterException: Cannot process GCMParameterSpec
permission java.lang.RuntimePermission "accessDeclaredMembers";
permission java.util.PropertyPermission "intellij.debug.agent", "read";
permission java.util.PropertyPermission "intellij.debug.agent", "write";
permission org.bouncycastle.crypto.CryptoServicesPermission "exportSecretKey";
permission org.bouncycastle.crypto.CryptoServicesPermission "exportPrivateKey";
permission java.io.FilePermission "${javax.net.ssl.trustStore}", "read";
};
51 changes: 51 additions & 0 deletions test/test-clusters/src/main/resources/fips/fips_java.security
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider C:HYBRID;ENABLE{All};
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS
security.provider.3=SUN
security.provider.4=SunJGSS
securerandom.source=file:/dev/urandom
securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN
securerandom.drbg.config=
login.configuration.provider=sun.security.provider.ConfigFile
policy.provider=sun.security.provider.PolicyFile
policy.expandProperties=true
policy.allowSystemProperty=true
policy.ignoreIdentityScope=false
keystore.type=BCFKS
keystore.type.compat=true
package.access=sun.misc.,\
sun.reflect.
package.definition=sun.misc.,\
sun.reflect.
security.overridePropertiesFile=true
ssl.KeyManagerFactory.algorithm=PKIX
ssl.TrustManagerFactory.algorithm=PKIX
networkaddress.cache.negative.ttl=10
krb5.kdc.bad.policy = tryLast
jdk.certpath.disabledAlgorithms=MD2, MD5, SHA1 jdkCA & usage TLSServer, \
RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224
jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \
DSA keySize < 1024
jdk.tls.disabledAlgorithms=SSLv3, RC4, MD5withRSA, DH keySize < 1024, \
EC keySize < 224, DES40_CBC, RC4_40, 3DES_EDE_CBC
jdk.tls.legacyAlgorithms= \
K_NULL, C_NULL, M_NULL, \
DH_anon, ECDH_anon, \
RC4_128, RC4_40, DES_CBC, DES40_CBC, \
3DES_EDE_CBC
jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37
crypto.policy=unlimited
jdk.xml.dsig.secureValidationPolicy=\
disallowAlg http://www.w3.org/TR/1999/REC-xslt-19991116,\
disallowAlg http://www.w3.org/2001/04/xmldsig-more#rsa-md5,\
disallowAlg http://www.w3.org/2001/04/xmldsig-more#hmac-md5,\
disallowAlg http://www.w3.org/2001/04/xmldsig-more#md5,\
maxTransforms 5,\
maxReferences 30,\
disallowReferenceUriSchemes file http https,\
minKeySize RSA 1024,\
minKeySize DSA 1024,\
minKeySize EC 224,\
noDuplicateIds,\
noRetrievalMethodLoops
jceks.key.serialFilter = java.base/java.lang.Enum;java.base/java.security.KeyRep;\
java.base/java.security.KeyRep$Type;java.base/javax.crypto.spec.SecretKeySpec;!*

0 comments on commit e9af78c

Please sign in to comment.