-
Notifications
You must be signed in to change notification settings - Fork 24.9k
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
Allow Integ Tests to run in a FIPS-140 JVM #31989
Conversation
- Mute :x-pack:qa:sql:security:ssl:integTest as it cannot run in FIPS 140 JVM until the SQL CLI supports key/cert. - Set default JVM keystore/truststore password in top level build script for all integTest tasks in a FIPS 140 JVM - Changed top level x-pack build script to use keys and certificates for trust/key material when spinning up clusters for IT
Pinging @elastic/es-security |
Pinging @elastic/es-core-infra for a review of the build related changes. |
x-pack/plugin/build.gradle
Outdated
// User cert and key PEM files instead of a JKS Keystore for the cluster's trust material so that | ||
// it can run in a FIPS 140 JVM | ||
task copyKeyCerts(type: Copy) { | ||
from('./core/src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating coupling between projects like this is not a good idea in Gradle.
We should at least reference this as somethign like project(':x-pack:plugin:core').file('src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/')
to make this more explicit.
The clean solution would be to have a small Gradle project with these resources and then
dependencies {
testRuntime project(':test:fips:certs')
}
Where :test:fips:certs
would just have these files that need to be on the CP.
Gradle will take care of dependencies and adding it to the cp automatically and it will play nicer with --parallel
as well. Would also mean that the copy task doesn't have to be repeated.
In this case, would it be feasible to have the build generate these files ?
Maybe even with random passwords and such ? Rest integration tests don't run in the IDE anyhow.
Might prevent some automated checks for committed private keys from tripping on our repo - even if it's only for testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, would it be feasible to have the build generate these files ?
Unfortunately not. keytool
only generates Private Keys and stores them in a Keystore, it can't output them as a PEM file. So we can only generate a PKCS12 or a JKS keystore containing the private key and associated certificate. keytool
also doesn't support exporting private keys from a keystore. The only solution would be to use an external tool like openssl
to either export the key and certificate form a PKCS12 store or generate the key , certificate in the first place. We can't be sure that openssl
will be available on all platforms though ( and I don't think Windows have a utility with corresponding functionality )
The clean solution would be to have a small Gradle project with these resources and then
Not all projects need all files ( test{node,client}.{jks,pem,crt}
) but it wouldn't hurt to add them. No issue going with this suggestion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about doing in Java what openssl would do ? Maybe using BouncyCastle or some other lib ? Would it be more effort than what it's worth ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, can't do that. We explicitly removed BC dependency recently on purpose ( see #30358 on why ).
I guess I can look into the possibility of exporting the keys in Java (without external deps) but in my mind this is much more complicated and I can't really see the benefits.
Granted that the files are referenced correctly or even replicated in this Gradle project so that we don't need to reference them from x-pack:plugin:core
, or even better I go down the path of the new gradle project with those files as resources, do you have any specific concerns?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, BouncyCastle would be a dependency for the build, which is a bit different, I don't think we'll be able to run Gradle itself on a FIPS JVM, or if we even want to. That being said I suggested it because it seemed the cleanest and not too much work - but admittedly I'm missing a lot of context on this so I could be totally wrong. I don't have a strong preference for this, separate project or referencing trough project is also fine for me ( in this order of preference ).
x-pack/plugin/build.gradle
Outdated
} | ||
if (nodeKeystore.exists()) { | ||
delete nodeKeystore | ||
File nodeKey = new File(keystoreDir, 'testnode.pem') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is usually written file("$keystoreDir/testnode.pem")
in Gradle. It doesn't make a difference in this case, but sometimes does because file()
resolves to projectDir
but new File()
resolves to working dir which can be the directory where the daemon was started. The /
does work on Windows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I was merely following existing conventions here. I'll change this
// Do not attempt to form a cluster in a FIPS JVM, as doing so with a JKS keystore will fail. | ||
// TODO Revisit this when SQL CLI client can handle key/certificate instead of only Keystores. | ||
tasks.matching({ it.name == "integTestCluster#init" }).all { | ||
onlyIf { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth reducing the duplication here
Closure notRunningFips = {
{
Boolean.parseBoolean(BuildPlugin.runJavascript(project, project.runtimeJavaHome,
'print(java.security.Security.getProviders()[0].name.toLowerCase().contains("fips"));')) == false
}
and then
onlyIf notRunningFips
CI run failed due to the intermittent issue that #31973 addresses, hopefully it will be merged soon. |
- Attempt to limit arbitrary project coupling by bringing necessary certificate/key/keystore files for IT in a small separate project and - Introduce Closre checking if we run in a FIPS JVM
@@ -29,132 +30,13 @@ project.sourceSets.test.output.dir(outputDir, builtBy: copyXPackPluginProps) | |||
// needed to be consistent with ssl host checking | |||
Object san = new SanEvaluator() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The certs that are pre-created, what do they have for a SAN value? We used to dynamically generate this on the fly so that we could still use hostname verification
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We used to dynamically generate this on the fly so that we could still use hostname verification
Yes, I know. It's a little more complicated now - we touched upon generation and potential issues with Alpar also yesterday: #31989 (comment)
The certs that are pre-created, what do they have for a SAN value?
X509v3 Subject Alternative Name:
DNS:localhost, DNS:localhost.localdomain, DNS:localhost4, DNS:localhost4.localdomain4, DNS:localhost6, DNS:localhost6.localdomain6, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
I think it will suffice, once CI reaches that we'll get a verification.
I 'd need to remove the SanEvaluator , as it is not used anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that looks close to what we used to use, lets go with it and see if we have issues
'print(java.security.Security.getProviders()[0].name.toLowerCase().contains("fips"));')) == false | ||
} | ||
// Do not attempt to form a cluster in a FIPS JVM, as doing so with a JKS keystore will fail. | ||
// TODO Revisit this when SQL CLI client can handle key/certificate instead of only Keystores. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there an issue open for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, we discussed it with Costin but I didn't yet come around to opening the issue. Will do ASAP
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks and please add it to the code comment
build.gradle
Outdated
allprojects { | ||
tasks.withType(RandomizedTestingTask) { | ||
// So that this gets executed only right before the test runs | ||
doFirst { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not actually guarantee that it will be run right before the test is executed. Other doFirst blocks could (and are) added throughout the build, and which order they run in is dependent on when they are added to the task.
I think this would be better as setting a project property in BuildPlugin.globalBuildInfo
alongside how we set runtimeJavaHome
. Then in BuildPlugin.commonTestConfig
have a condition on whether it is a fips jvm to add these sysprops.
@@ -287,7 +287,7 @@ class BuildPlugin implements Plugin<Project> { | |||
} | |||
|
|||
/** Runs the given javascript using jjs from the jdk, and returns the output */ | |||
private static String runJavascript(Project project, String javaHome, String script) { | |||
static String runJavascript(Project project, String javaHome, String script) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see my comment about this use. This can go back to private.
assertThat(nodeResponse.reloadException(), instanceOf(IOException.class)); | ||
// Running in a JVM with a BouncyCastle FIPS Security Provider, decrypting the Keystore with the wrong | ||
// password can return a SecurityException if the DataInputStream can't be fully consumed | ||
assertThat(nodeResponse.reloadException(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should set an additional system property when running in a fips jvm so tests can conditionalize checks like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something similar is already added in ESTestCase
https://github.com/elastic/elasticsearch/pull/31666/files#diff-ea1d57cff30e53747f2ce541df4dff2eR1368 (ready to be merged). However this is the case of a specific (granted, FIPS ) provider's behavior difference in how streams are consumed and might not be the case with others, so this could not be generalized enough with an if inFipsJvm()
check - See our discussion in #28515
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then we should add whatever extra properties are necessary for the test to distinguish the two cases. What would happen if one jvm started throwing the other exception? The test would be out of date but we would have no idea there was a behavior change.
extraConfigFile nodeKeystore.name, nodeKeystore | ||
extraConfigFile nodeKey.name, nodeKey |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are going to have static files, then I think these should be resource files in buildSrc. You can add a helper method (ie property that is a closure) in BuildPlugin that is attached to all projects called resourceFile
, so this can look like:
extraConfigFile nodeKey.name, project.resourceFile('testnode.pem')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback @rjernst ! I addressed the rest of your points but can you give me a pointer on how such a Closure
property could look like ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In BuildPlugin, look at configureTest
. This adds an additionalTest
closure that acts like a method to add additional test tasks.
After discussions with @atorok and @rjernst ( thanks both for the feedback ! ) the issue of how static files can be resolved from the buildSrc project resources will be tackled in a generic manner and @atorok is working on a PR that addresses that. I will pause working on this until the newly added helper methods are merged |
@rjernst, @jaymode this is ready for another round of review. I adjusted my approach of referencing static files to the alternative that (continues to) use project coupling but with better referencing as Alpar suggested in #31989 (comment). Once #32201 is merged, I will take up the task to rewrite the all the cases in our IT ( the ones touched in this PR is only a subset of those using this way of referencing static files) using the new approach. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks ok, but if we are going to have these cross project deps, there needs to be a followup issue to remove them, dependent on the in progress gradle work. Also, there are a ton of whitespace related changes, can those be omitted? It makes the PR much larger than it would be otherwise.
x-pack/plugin/build.gradle
Outdated
// Add key and certs to test classpath: it expects them there | ||
// User cert and key PEM files instead of a JKS Keystore for the cluster's trust material so that | ||
// it can run in a FIPS 140 JVM | ||
task copyKeyCerts(type: Copy) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add a comment with a TODO and link to the PR that will make this not use cross project references?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
* Complete changes for running IT in a fips JVM - Mute :x-pack:qa:sql:security:ssl:integTest as it cannot run in FIPS 140 JVM until the SQL CLI supports key/cert. - Set default JVM keystore/truststore password in top level build script for all integTest tasks in a FIPS 140 JVM - Changed top level x-pack build script to use keys and certificates for trust/key material when spinning up clusters for IT
* Complete changes for running IT in a fips JVM - Mute :x-pack:qa:sql:security:ssl:integTest as it cannot run in FIPS 140 JVM until the SQL CLI supports key/cert. - Set default JVM keystore/truststore password in top level build script for all integTest tasks in a FIPS 140 JVM - Changed top level x-pack build script to use keys and certificates for trust/key material when spinning up clusters for IT This is a backport of #31989
Now that elastic#31666 and elastic#31989 are merged we can run our tests in fips JVM. This commits enables us to run tests on a Java 8 JVM using BouncyCastleFIPS as a security Provider.
* master: Security: revert to old way of merging automata (#32254) Networking: Fix test leaking buffer (#32296) Undo a debugging change that snuck in during the field aliases merge. Painless: Update More Methods to New Naming Scheme (#32305) [TEST] Fix assumeFalse -> assumeTrue in SSLReloadIntegTests Ingest: Support integer and long hex values in convert (#32213) Introduce fips_mode setting and associated checks (#32326) Add V_6_3_3 version constant [DOCS] Removed extraneous callout number. Rest HL client: Add put license action (#32214) Add ERR to ranking evaluation documentation (#32314) Introduce Application Privileges with support for Kibana RBAC (#32309) Build: Shadow x-pack:protocol into x-pack:plugin:core (#32240) [Kerberos] Add Kerberos authentication support (#32263) [ML] Extract persistent task methods from MlMetadata (#32319) Add Restore Snapshot High Level REST API Register ERR metric with NamedXContentRegistry (#32320) fixes broken build for third-party-tests (#32315) Allow Integ Tests to run in a FIPS-140 JVM (#31989) [DOCS] Rollup Caps API incorrectly mentions GET Jobs API (#32280) awaitsfix testRandomClusterStateUpdates [TEST] add version skip to weighted_avg tests Consistent encoder names (#29492) Add WeightedAvg metric aggregation (#31037) Switch monitoring to new style Requests (#32255) Rename ranking evaluation `quality_level` to `metric_score` (#32168) Fix a test bug around nested aggregations and field aliases. (#32287) Add new permission for JDK11 to load JAAS libraries (#32132) Silence SSL reload test that fails on JDK 11 [test] package pre-install java check (#32259) specify subdirs of lib, bin, modules in package (#32253) Switch x-pack:core to new style Requests (#32252) awaitsfix SSLConfigurationReloaderTests Painless: Clean up add methods in PainlessLookup (#32258) Fail shard if IndexShard#storeStats runs into an IOException (#32241) AwaitsFix RecoveryIT#testHistoryUUIDIsGenerated Remove unnecessary warning supressions (#32250) CCE when re-throwing "shard not available" exception in TransportShardMultiGetAction (#32185) Add new fields to monitoring template for Beats state (#32085)
Necessary changes for running Integration tests in a FIPS-140 JVM.
The main drive for change is that JKS keystores cannot be used for
KeyManagerFactory#init()
so we should use the relevant key andcertificate instead for creating all relevant SSLContexts.
cannot run in FIPS 140 JVM until the SQL CLI supports key/cert.
(It currently supports only Keystores for the key configuration
and it's not trivial to support reading private keys without
depending either on BouncyCastle (Non FIPS version) or X-Pack
(
PEMUtils
)script for all integTest tasks in a FIPS 140 JVM as that JVM will
be using a BCFKS keystore which will be password protected.
for trust/key material when spinning up clusters for IT
Depends on #31666