Skip to content
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

Upgrade to OpenSAML 4.3.0 (shadowed) #98199

Merged

Conversation

albertzaharovits
Copy link
Contributor

@albertzaharovits albertzaharovits commented Aug 4, 2023

This commit upgrades to OpenSAML v4.3.0

Versions of OpenSAML ≥ 4.1 have a hard dependency on the non-FIPS release of BouncyCastle.
This would prevent ES from being able to run in a JVM where BC-FIPS is configured as the security provider.

Closes: #71983

Co-authored-by: Tim Vernum tim@adjective.org

This commit upgrades to OpenSAML v4.3.0

Versions of OpenSAML ≥ 4.1 have a hard dependency on the non-FIPS
release of BouncyCastle.
This would prevent ES from being able to run in a JVM where BC-FIPS is
configured as the security provider.
In order to overcome this, we shadow OpenSAML and patch two classes
to remove their dependency on BouncyCastle
@albertzaharovits
Copy link
Contributor Author

@elasticsearchmachine run elasticsearch-ci/part-2

@albertzaharovits
Copy link
Contributor Author

@tvernum This is your work passing CI 🎊

I'm going to try to reduce the surface of the changes by:

  1. use a non-empty org.elasticsearch.xpack.security.authc.saml.OpenSamlConfig implementation that sets properties.setProperty("opensaml.config.ecdh.defaultKDF","PBKDF2") to avoid loading the KDF code that refers BC
  2. remove the service implementation META-INF/services/org.opensaml.security.crypto.ec.NamedCurve form the opensaml-security-api-4.3.0.jar to avoid changing the AbstractNamedCurve class.

If all goes well there should be only one shadowed jar, which just removes the META-INF/services part from the original jar.
I'll ping you when I sort it out. (if it doesn't work out, I think this here is OK too, so I'll revert back to it).

@albertzaharovits
Copy link
Contributor Author

These changes effectively bypass the BC dependency. Tests do pass, but I'm not 100% that somehow, via some obscure protocal/parameter combination, our SAML SP and IDP implementations won't try to use functionality that has hence been eliminated.
To learn what functionality has been eliminated, apply the following 2 patches and run any SAML test:

diff --git a/x-pack/libs/es-opensaml-security-api/build.gradle b/x-pack/libs/es-opensaml-security-api/build.gradle
index 56fd6b81df6..104bb51f1de 100644
--- a/x-pack/libs/es-opensaml-security-api/build.gradle
+++ b/x-pack/libs/es-opensaml-security-api/build.gradle
@@ -27,5 +27,5 @@ tasks.named("shadowJar").configure {
   manifest {
     attributes 'Automatic-Module-Name': 'org.opensaml.security'
   }
-  exclude 'META-INF/services/org.opensaml.security.crypto.ec.NamedCurve'
+  //exclude 'META-INF/services/org.opensaml.security.crypto.ec.NamedCurve'
 }
diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/OpenSamlXpackSecurityConfigurationPropertiesSource.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/OpenSamlXpackSecurityConfigurationPropertiesSource.java
index 0e885dd0daa..45243df6baf 100644
--- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/OpenSamlXpackSecurityConfigurationPropertiesSource.java
+++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/OpenSamlXpackSecurityConfigurationPropertiesSource.java
@@ -16,7 +16,7 @@ public class OpenSamlXpackSecurityConfigurationPropertiesSource implements Confi
     @Override
     public Properties getProperties() {
         Properties properties = new Properties();
-        properties.setProperty("opensaml.config.ecdh.defaultKDF", "PBKDF2");
+        //properties.setProperty("opensaml.config.ecdh.defaultKDF", "PBKDF2");
         return properties;
     }
 }
 ./gradlew ':x-pack:plugin:security:test' --tests "org.elasticsearch.xpack.security.authc.saml.SamlSpMetadataBuilderTests"

@albertzaharovits
Copy link
Contributor Author

@mark-vieira I think it is possible to put the OpenSamlXpackSecurityConfigurationPropertiesSource service provider inside the shadowed jar, rather than put it in the ES code that uses the code that depends on the service. That's because the same code that uses the shadowed jar also uses the library code that depends on the service implementation.
But I'm not sure if this is better/preferable?
Also I couldn't figure out what the module declaration inside the shadowed jar should look like in order to be able to expose the ConfigurationPropertiesSource implementation (that's going to be used by the code that depends on the shadowed jar).

@albertzaharovits albertzaharovits marked this pull request as ready for review August 10, 2023 17:06
@albertzaharovits albertzaharovits added the :Security/Security Security issues without another label label Aug 10, 2023
@albertzaharovits albertzaharovits self-assigned this Aug 10, 2023
@elasticsearchmachine elasticsearchmachine added the Team:Security Meta label for security team label Aug 10, 2023
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-security (Team:Security)

Copy link
Contributor

@mark-vieira mark-vieira left a comment

Choose a reason for hiding this comment

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

Yeah, I don't think we want to duplicate the property source. Sticking it in a library is probably the best way to go. It's still not clear to me that we need to repackage the dependency though.

manifest {
attributes 'Automatic-Module-Name': 'org.opensaml.security'
}
exclude 'META-INF/services/org.opensaml.security.crypto.ec.NamedCurve'
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this the only modification to this JAR we're making? Typically a "shadow" JAR is repackaging several dependencies, or relocating them under a different package, etc. Is this effectively identical to the original JAR, just excluding this file?

Copy link
Contributor

Choose a reason for hiding this comment

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

Pretty much.

OpenSAML out of the box doesn't work on FIPS. My first attempt to solve it required patching 2 classes. Albert worked out that we can just avoid ever loading those classes by manipulating the set of services - one of them can be handled by stripping this service, the other by implementing our own properties resolver so that a default property can be changed.

It does seem like overkill to repackage just to drop one services file, but I'm not aware of a simpler option.
We could just strip the file as part of the distribution, but I'm not a fan of shipping modified files JAR files that claim to be the originals.

Copy link
Contributor

Choose a reason for hiding this comment

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

Gotcha, makes sense. I would suggest we go with the pattern we used for log4j though (look in libs/log4j/build.gradle rather than use the Shadow plugin. It's not really the intended use case, and it's more complex than necessary for this, plus the log4j hack plays more nicely with IDEs.

Copy link
Contributor

@tvernum tvernum Aug 11, 2023

Choose a reason for hiding this comment

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

++
The original attempt modified a couple of classes, so it was based on the approach we used for Jackson.
Now that we've settled on just stripping classes files, we can simplify it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mark-vieira I think it is possible to put the OpenSamlXpackSecurityConfigurationPropertiesSource service provider inside the shadowed jar, rather than put it in the ES code that uses the code that depends on the service. That's because the same code that uses the shadowed jar also uses the library code that depends on the service implementation.
But I'm not sure if this is better/preferable?

I think it's preferable to co-locate it with the shadowed jar. If for no other reason then it removes the existing duplication.

I've pushed 404298c and c179601 to incoporate the ConfigurationPropertiesSource service implementation in the shadowed jar, in order to avoid having two identical such implementations in the x-pack/plugin/security and x-pack/plugin/identity-provider projects (it works because the same code that must not see the NamedCurve implementations, also needs to see the new ConfigurationPropertiesSource implementation). So now the shadowed jar effectively removes one service implementation and adds another different one (a single new class).
I think this is now a fair usage of the "shadow" plugin?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When we say "pull in" what does that mean? Is it a transitive dependency or is it shadowed in the OpenSAML jar?

It is a runtime transitive dependency of OpenSAML (since version >4.1).

We manually manage transitive dependencies so we can use whatever bouncy castle jar we want, or omit it entirely.

We wish to omit it entirely because it contains crypto algorithms that are not FIPS compliant (there is a different bouncy castle with FIPS compliant algorithms), and that's a problem because we don't want the Elasticsearch FIPS distribution to ship with non-FIPS compliant crypto on the classpath (or really, no Elasticsearch distrib should have any crypto impl on the classpath. Instead we opt to delegate to the JCA API, where the JVM is configured for FIPS or not.).

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, then I don't quite understand why we need this solution. We disable transitive dependencies by default in our build. The transitive BC jar shouldn't be brought in by this dependency. How does this relate to omitting that service file? From what I can tell that's part of the OpenSAML jar, not BC.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we don't put the BC on the classpath and we don't shadow jar the OpenSAML lib, then ES throws a class not found exception when it starts up, see this issue: #71983 .

Copy link
Contributor

Choose a reason for hiding this comment

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

Got it. So there is a runtime dependency on BC and stripping out that service avoids that issue. Understood.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, OpenSAML has had a transitive dependency on BouncyCastle for a while, but we've just avoided those parts.
In 4.1 the dependency become harder to avoid - a couple of classes that are eagerly loaded (with the the default services/properties config) have a direct dependency.

Copy link
Contributor

@tvernum tvernum left a comment

Choose a reason for hiding this comment

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

LGTM

manifest {
attributes 'Automatic-Module-Name': 'org.opensaml.security'
}
exclude 'META-INF/services/org.opensaml.security.crypto.ec.NamedCurve'
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, OpenSAML has had a transitive dependency on BouncyCastle for a while, but we've just avoided those parts.
In 4.1 the dependency become harder to avoid - a couple of classes that are eagerly loaded (with the the default services/properties config) have a direct dependency.

@quux00 quux00 added v8.11.0 and removed v8.10.0 labels Aug 16, 2023
@tvernum
Copy link
Contributor

tvernum commented Aug 21, 2023

@mark-vieira Do you want a chance to review this again before it's merged?

Copy link
Contributor

@mark-vieira mark-vieira left a comment

Choose a reason for hiding this comment

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

Minor comment about re-enabling some of our precommit checks but otherwise LGTM.

compileOnly "org.opensaml:opensaml-core:${versions.opensaml}"
}

['jarHell', 'thirdPartyAudit', 'forbiddenApisMain', 'splitPackagesAudit', 'checkstyleMain', 'licenseHeaders', 'spotlessJavaCheck'].each {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can (and should) re-enable things like forbiddenApis, checkstyle and spotless here since this module actually has some code (albeit minimal) of its own.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mark-vieira I see that forbiddenApisMain fails with:

> Task :x-pack:libs:es-opensaml-security-api:forbiddenApisMain FAILED

> Task :libs:elasticsearch-core:compileJava
Note: Some input files use or override a deprecated API that is marked for removal.
Note: Recompile with -Xlint:removal for details.

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':x-pack:libs:es-opensaml-security-api:forbiddenApisMain'.
> Parsing signatures failed: Class 'org.apache.lucene.util.IOUtils' not found on classpath while parsing signature: org.apache.lucene.util.IOUtils

Is it a real problem? I suspect it's just the forbidden task itself missing a dependency classs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've only reenable checkstyle and spotless for now

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, this looks to be because some of our forbidden api rules reference classes that aren't on the classpath here. We can disable this task here for now.

@albertzaharovits
Copy link
Contributor Author

@tvernum @mark-vieira any objections to merging this post FF in 8.10?

@albertzaharovits
Copy link
Contributor Author

@elasticsearchmachine run elasticsearch-ci/example-plugins

@mark-vieira
Copy link
Contributor

You can ignore the example-plugin build failures for now.

@tvernum
Copy link
Contributor

tvernum commented Aug 23, 2023

any objections to merging this post FF in 8.10?

None from me. It's the type of upgrade we would consider backporting to a 8.10.x patch release any way, in which case it would be acceptable to merge post FF (since FF only applies to non-patch features)

@albertzaharovits albertzaharovits merged commit 1b468a0 into elastic:main Aug 23, 2023
@albertzaharovits albertzaharovits deleted the tim-upgrade-opensaml-4.3 branch August 23, 2023 09:59
albertzaharovits added a commit to albertzaharovits/elasticsearch that referenced this pull request Aug 23, 2023
This commit upgrades to OpenSAML v4.3.0

Versions of OpenSAML ≥ 4.1 have a hard dependency on the non-FIPS release of BouncyCastle.
This would prevent ES from being able to run in a JVM where BC-FIPS is configured as the security provider.

Closes: elastic#71983

Co-authored-by: Tim Vernum tim@adjective.org
albertzaharovits added a commit that referenced this pull request Aug 23, 2023
This commit upgrades to OpenSAML v4.3.0

Versions of OpenSAML ≥ 4.1 have a hard dependency on the non-FIPS release of BouncyCastle.
This would prevent ES from being able to run in a JVM where BC-FIPS is configured as the security provider.

Backport of: #98199

Closes: #71983

Co-authored-by: Tim Vernum tim@adjective.org
dnhatn added a commit to dnhatn/elasticsearch that referenced this pull request Sep 5, 2023
albertzaharovits added a commit that referenced this pull request Aug 23, 2024
apache.santuario.xmlsec version 2.1.4 is documented vulnerable.
We should update to mitigate the vulnerabilities.
But apache.santuario.xmlsec is a dependency of opensaml version 3.*.

However, in a patch release of elasticsearch (i.e. 7.17.*) it's best we avoid updating dependencies across major versions (i.e. opensaml from version 3.* to version 4.*), particularly for such a complex dependency as opensaml (we did update the opensaml dep in this way, but in a minor elasticsearch 8.* release, i.e. #98199). The latest opensaml 3.* release (i.e. 3.4.6) still requires a vulnerable apache.santuario.xmlsec dep: https://mvnrepository.com/artifact/org.opensaml/opensaml-xmlsec-impl/3.4.6).

In this case, our best hope is to find a non-vulnerable version of apache.santuario.xmlsec that is still on the same major version as the version listed in the deps of opensaml (i.e. 2.*). That's version 2.2.6: https://mvnrepository.com/artifact/org.apache.santuario/xmlsec/2.2.6 , which is not vulnerable

This PR updates apache.santuario.xmlsec from the existing 2.1.4 version to the 2.2.6 version. The release notes of the 2.2.0 version from https://santuario.apache.org/javareleasenotes.html look simple, and the dependencies differences (from https://mvnrepository.com/artifact/org.apache.santuario/xmlsec/2.1.4) are minimal as well (hopefully optional dependencies, which we don't pull in, stay optional in the same way in the new version).
So, it looks to me that the dep update is relatively safe (and it also passes CI)!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>non-issue :Security/Security Security issues without another label Team:Security Meta label for security team v8.10.0 v8.11.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update opensaml dependency to 4.1.0
6 participants