Skip to content

Enable tests in FIPS 140 in JDK 11 #48378

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Nov 15, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ if (project != rootProject) {
exclude '**/*.p12'
// the file that actually defines nocommit
exclude '**/ForbiddenPatternsTask.java'
exclude '**/*.bcfks'
}

testingConventions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import org.apache.commons.io.IOUtils
import org.elasticsearch.gradle.info.GlobalBuildInfoPlugin
import org.elasticsearch.gradle.info.GlobalInfoExtension
import org.elasticsearch.gradle.info.JavaHome
import org.elasticsearch.gradle.precommit.DependencyLicensesTask
import org.elasticsearch.gradle.precommit.PrecommitTasks
import org.elasticsearch.gradle.test.ErrorReportingTestListener
import org.elasticsearch.gradle.testclusters.ElasticsearchCluster
import org.elasticsearch.gradle.testclusters.TestClustersPlugin
import org.elasticsearch.gradle.tool.Boilerplate
import org.gradle.api.Action
import org.gradle.api.GradleException
import org.gradle.api.InvalidUserDataException
Expand Down Expand Up @@ -142,33 +142,43 @@ class BuildPlugin implements Plugin<Project> {
configureTestTasks(project)
configurePrecommit(project)
configureDependenciesInfo(project)


configureFips140(project)
}

public static void configureFips140(Project project) {
// Need to do it here to support external plugins
GlobalInfoExtension globalInfo = project.rootProject.extensions.getByType(GlobalInfoExtension)
// wait until global info is populated because we don't know if we are running in a fips jvm until execution time
globalInfo.ready {
ExtraPropertiesExtension ext = project.extensions.getByType(ExtraPropertiesExtension)
// Common config when running with a FIPS-140 runtime JVM
if (ext.has('inFipsJvm') && ext.get('inFipsJvm')) {
project.tasks.withType(Test).configureEach { Test task ->
task.systemProperty 'javax.net.ssl.trustStorePassword', 'password'
task.systemProperty 'javax.net.ssl.keyStorePassword', 'password'
}
project.pluginManager.withPlugin("elasticsearch.testclusters") {
NamedDomainObjectContainer<ElasticsearchCluster> testClusters = project.extensions.findByName(TestClustersPlugin.EXTENSION_NAME) as NamedDomainObjectContainer<ElasticsearchCluster>
if (testClusters != null) {
testClusters.all { ElasticsearchCluster cluster ->
cluster.systemProperty 'javax.net.ssl.trustStorePassword', 'password'
cluster.systemProperty 'javax.net.ssl.keyStorePassword', 'password'
}
}
static void configureFips140(Project project) {
// Common config when running with a FIPS-140 runtime JVM
if (inFipsJvm()) {
ExportElasticsearchBuildResourcesTask buildResources = project.tasks.getByName('buildResources') as ExportElasticsearchBuildResourcesTask
File securityProperties = buildResources.copy("fips_java.security")
File securityPolicy = buildResources.copy("fips_java.policy")
File bcfksKeystore = buildResources.copy("cacerts.bcfks")
// This configuration can be removed once system modules are available
Boilerplate.maybeCreate(project.configurations, 'extraJars') {
project.dependencies.add('extraJars', "org.bouncycastle:bc-fips:1.0.1")
project.dependencies.add('extraJars', "org.bouncycastle:bctls-fips:1.0.9")
}
project.pluginManager.withPlugin("elasticsearch.testclusters") {
NamedDomainObjectContainer<ElasticsearchCluster> testClusters = project.extensions.findByName(TestClustersPlugin.EXTENSION_NAME) as NamedDomainObjectContainer<ElasticsearchCluster>
if (testClusters != null) {
testClusters.all { ElasticsearchCluster cluster ->
cluster.extraConfigFile("fips_java.security", securityProperties);
cluster.extraConfigFile("fips_java.policy", securityPolicy);
cluster.extraConfigFile("cacerts.bcfks", bcfksKeystore);
}
}
}
project.tasks.withType(Test).configureEach { Test task ->
task.dependsOn(buildResources)
task.systemProperty('javax.net.ssl.trustStorePassword', 'password')
task.systemProperty('javax.net.ssl.keyStorePassword', 'password')
task.systemProperty('javax.net.ssl.trustStoreType', 'BCFKS')
// Using the key==value format to override default JVM security settings and policy
// see also: https://docs.oracle.com/javase/8/docs/technotes/guides/security/PolicyFiles.html
task.systemProperty('java.security.properties', String.format(Locale.ROOT, "=%s", securityProperties.toString()))
task.systemProperty('java.security.policy', String.format(Locale.ROOT, "=%s", securityPolicy.toString()))
task.systemProperty('javax.net.ssl.trustStore', bcfksKeystore.toString())
}

}
}

Expand Down Expand Up @@ -821,14 +831,11 @@ class BuildPlugin implements Plugin<Project> {
project.mkdir(testOutputDir)
project.mkdir(heapdumpDir)
project.mkdir(test.workingDir)

if (project.property('inFipsJvm')) {
nonInputProperties.systemProperty('runtime.java', "${-> (ext.get('runtimeJavaVersion') as JavaVersion).getMajorVersion()}FIPS")
} else {
nonInputProperties.systemProperty('runtime.java', "${-> (ext.get('runtimeJavaVersion') as JavaVersion).getMajorVersion()}")
}
}

if (inFipsJvm()) {
project.dependencies.add('testRuntimeOnly', "org.bouncycastle:bc-fips:1.0.1")
project.dependencies.add('testRuntimeOnly', "org.bouncycastle:bctls-fips:1.0.9")
}
test.jvmArgumentProviders.add(nonInputProperties)
test.extensions.add('nonInputProperties', nonInputProperties)

Expand Down Expand Up @@ -983,4 +990,8 @@ class BuildPlugin implements Plugin<Project> {
})
}
}

private static inFipsJvm(){
return Boolean.parseBoolean(System.getProperty("tests.fips.enabled"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,7 @@ public void generate() {
runtimeJavaVersionDetails = findJavaVersionDetails(runtimeJavaHome);
runtimeJavaVersionEnum = JavaVersion.toVersion(findJavaSpecificationVersion(runtimeJavaHome));

// We don't expect Gradle to be running in a FIPS JVM
String inFipsJvmScript = "print(java.security.Security.getProviders()[0].name.toLowerCase().contains(\"fips\"));";
inFipsJvm = Boolean.parseBoolean(runJavaAsScript(runtimeJavaHome, inFipsJvmScript));
inFipsJvm = Boolean.parseBoolean(System.getProperty("tests.fips.enabled"));
} else {
throw new RuntimeException("Runtime Java home path of '" + compilerJavaHome + "' does not exist");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.gradle.api.Named;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.api.tasks.Classpath;
Expand Down Expand Up @@ -70,6 +71,7 @@
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
Expand Down Expand Up @@ -454,6 +456,10 @@ public synchronized void start() {

copyExtraConfigFiles();

copyExtraJars();

configureNodeForFips();

if (isSettingTrue("xpack.security.enabled")) {
if (credentials.isEmpty()) {
user(Collections.emptyMap());
Expand Down Expand Up @@ -530,6 +536,40 @@ private void copyExtraConfigFiles() {
});
}

/**
* Copies extra jars to the `/lib` directory.
* //TODO: Remove this when system modules are available
*/
private void copyExtraJars() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We prefer not to couple testclusters with random project configuration like an extraJars configuration.
Would be better to have extra jars work exactly as extra config files so these jars could be passed in externally.

A more generic way would be to add the possibility to add hooks right before the task starts.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this outside as suggested, similar to how extraConfigurationFiles work

Configuration extraJarsConfig = project.getConfigurations().findByName("extraJars");
if ( extraJarsConfig != null ) {
Set<File> extraJars = project.getConfigurations().getByName("extraJars").getFiles();
for (File jar : extraJars) {
Path destination = getDistroDir().resolve("lib");
LOGGER.info("Adding extra jar {} to {}", jar.getName(), destination);
project.copy(spec -> {
spec.from(jar);
spec.into(destination);
});
}
}
}

private void configureNodeForFips() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should do this externally, you can use testclusters.all {} to configure all clusters in a project, so combining that with allprojects {} would allow you to configure it across the board

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't figure out how I can reference the file locations from within the BuildPlugin if I do this in testclusters.all {}. Any ideas ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Locations of what files exactly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fips_java.security, fips_java.policy and cacerts.bcfks that I copy as extra config files

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's problematic indeed. getConfigDir is public, but we don't have a way to iterate over the nodes, or configure nodes individually.

Does this needs to be an absolute path ? Would a path relative to cwd work ? e.x. conf/fips_java.policy i think that's how this problem is solved elsewhere.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These paths are passed as values to system properties, not used in ES configuration so we either need an absolute path. Maybe we have a global reference to the ES_CONF_DIR that can be resolved in a system property? I'll look into it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solution required adding ES_PATH_CONF to the substitutions we do when parsing jvm.properties . Can't think of a side-effect but I wanted to raise it for visibility when this is reviewed

boolean inFipsJvm = Boolean.parseBoolean(System.getProperty("tests.fips.enabled", "false"));
if (inFipsJvm) {
systemProperties.put("java.security.properties",
String.format(Locale.ROOT, "=%s/fips_java.security", getConfigDir().toString()));
systemProperties.put("java.security.policy",
String.format(Locale.ROOT, "=%s/fips_java.policy", getConfigDir().toString()));
systemProperties.put("javax.net.ssl.trustStore",
String.format(Locale.ROOT, "%s/cacerts.bcfks", getConfigDir().toString()));
systemProperties.put("javax.net.ssl.trustStorePassword", "password");
systemProperties.put("javax.net.ssl.keyStorePassword", "password");
systemProperties.put("javax.net.ssl.trustStoreType","BCFKS");
}
}

private void installModules() {
if (testDistribution == TestDistribution.INTEG_TEST) {
logToProcessStdout("Installing " + modules.size() + "modules");
Expand Down
Binary file added buildSrc/src/main/resources/cacerts.bcfks
Binary file not shown.
16 changes: 16 additions & 0 deletions buildSrc/src/main/resources/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";
};
50 changes: 50 additions & 0 deletions buildSrc/src/main/resources/fips_java.security
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS
security.provider.3=SUN
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;!*
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
Expand Down Expand Up @@ -51,7 +52,12 @@ public class SslConfiguration {
static final Map<String, String> ORDERED_PROTOCOL_ALGORITHM_MAP;
static {
LinkedHashMap<String, String> protocolAlgorithmMap = new LinkedHashMap<>();
protocolAlgorithmMap.put("TLSv1.3", "TLSv1.3");
try {
SSLContext.getInstance("TLSv1.3");
protocolAlgorithmMap.put("TLSv1.3", "TLSv1.3");
} catch (NoSuchAlgorithmException e) {
// ignore since we support JVMs using BCJSSE in FIPS mode which doesn't support TLSv1.3
}
protocolAlgorithmMap.put("TLSv1.2", "TLSv1.2");
protocolAlgorithmMap.put("TLSv1.1", "TLSv1.1");
protocolAlgorithmMap.put("TLSv1", "TLSv1");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.elasticsearch.common.ssl.KeyStoreUtil.inferKeyStoreType;
import static org.elasticsearch.common.ssl.SslConfiguration.ORDERED_PROTOCOL_ALGORITHM_MAP;
import static org.elasticsearch.common.ssl.SslConfigurationKeys.CERTIFICATE;
import static org.elasticsearch.common.ssl.SslConfigurationKeys.CERTIFICATE_AUTHORITIES;
import static org.elasticsearch.common.ssl.SslConfigurationKeys.CIPHERS;
Expand Down Expand Up @@ -66,7 +69,9 @@
*/
public abstract class SslConfigurationLoader {

static final List<String> DEFAULT_PROTOCOLS = List.of("TLSv1.3", "TLSv1.2", "TLSv1.1");
static final List<String> DEFAULT_PROTOCOLS = Collections.unmodifiableList(
ORDERED_PROTOCOL_ALGORITHM_MAP.containsKey("TLSv1.3") ?
Arrays.asList("TLSv1.3", "TLSv1.2", "TLSv1.1") : Arrays.asList("TLSv1.2", "TLSv1.1"));

private static final List<String> JDK11_CIPHERS = List.of(
"TLS_AES_256_GCM_SHA384", "TLS_AES_128_GCM_SHA256", // TLSv1.3 cipher has PFS, AEAD, hardware support
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Security;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -2091,6 +2090,6 @@ public static Index resolveIndex(String index) {
}

public static boolean inFipsJvm() {
return Security.getProviders()[0].getName().toLowerCase(Locale.ROOT).contains("fips");
return Boolean.parseBoolean(System.getProperty(FIPS_SYSPROP));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.security.Security;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -191,6 +190,8 @@ public static void resetPortCounter() {

public static final String DEFAULT_TEST_WORKER_ID = "--not-gradle--";

public static final String FIPS_SYSPROP = "tests.fips.enabled";

static {
TEST_WORKER_VM_ID = System.getProperty(TEST_WORKER_SYS_PROPERTY, DEFAULT_TEST_WORKER_ID);
setTestSysProps();
Expand Down Expand Up @@ -1334,7 +1335,7 @@ private static boolean isUnusableLocale() {
}

public static boolean inFipsJvm() {
return Security.getProviders()[0].getName().toLowerCase(Locale.ROOT).contains("fips");
return Boolean.parseBoolean(System.getProperty(FIPS_SYSPROP));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,7 @@ private ReproduceErrorMessageBuilder appendESProperties() {
appendOpt("tests.distribution", System.getProperty("tests.distribution"));
appendOpt("compiler.java", System.getProperty("compiler.java"));
appendOpt("runtime.java", System.getProperty("runtime.java"));
appendOpt("javax.net.ssl.keyStorePassword", System.getProperty("javax.net.ssl.keyStorePassword"));
appendOpt("javax.net.ssl.trustStorePassword", System.getProperty("javax.net.ssl.trustStorePassword"));
appendOpt(ESTestCase.FIPS_SYSPROP, System.getProperty(ESTestCase.FIPS_SYSPROP));
return this;
}

Expand Down
Loading