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

Add libraries actuator endpoint #25134

Closed

Conversation

philsttr
Copy link

@philsttr philsttr commented Feb 9, 2021

Adds a new libraries actuator endpoint that displays details about the libraries used by the application.

Provides out-of-the-box support for displaying details about jar libraries that are bundled within the application jar/war archive.

The spring-boot-maven-plugin and spring-boot-gradle-plugin now generate a META-INF/bundled-libraries.yaml file when they package the application archive. The file is included in the packaged application archive, and contains details about the libraries contained within the application archive.

At runtime, libraries are contributed to the libraries endpoint via LibrariesContributor beans, similar to how InfoContributor beans contribute to the info endpoint.

spring-boot-actuator-autoconfigure auto-configures a LibrariesContributor that adds the libraries from META-INF/bundled-libraries.yaml to the libraries endpoint. This auto-configuration can be disabled using environment properties similar to how other actuator endpoints behave.

An application can provide other LibrariesContributor beans to contribute more libraries to the libraries endpoint. For example, an application might want to contribute libraries dynamically added at runtime. Or an application might want to contribute details about front-end libraries.

Each Library is described as a simple set of key/value pairs. For example, Java libraries with coordinates in a Maven repository typically contain entries for groupId, artifactId, and version. Other types of libraries contributed by an application could contain other details.

Fixes #22924

Example output of /actuator/libraries endpoint

{
  "bundled": [
    {
      "groupId": "ch.qos.logback",
      "artifactId": "logback-classic",
      "version": "1.2.3"
    },
    {
      "groupId": "ch.qos.logback",
      "artifactId": "logback-core",
      "version": "1.2.3"
    },
// ... snip ...
    {
      "groupId": "org.springframework",
      "artifactId": "spring-core",
      "version": "5.3.4-SNAPSHOT"
    },
// ... snip ...
  ]
}

Alternatives considered, and decisions made

I originally wanted to implement the libraries endpoint by simply inspecting the runtime classpath, rather than relying on metadata generated at build time. I ran into some pretty big challenges with that:

  1. Inspecting the classpath of a classloader is not straightforward.. You have to downcast to URLClassLoader, and there's no guarantee that the class loader is actually a URLClassLoader
  2. Jars on the classpath don't necessarily contain the information I wanted to be available on the libraries endpoint. Specifically, there's no guaranteed way to be able to report the maven repository coordinates of a library

Because of the above reasons, I decided to use metadata generated when assembling the package.

I started out by creating a separate task/goal for generating this metadata, similar to the buildInfo task/goal. I ran into another challenge... The maven and gradle plugins have some pretty complex logic to determine which libraries are included in the final assembled jar/war archive. See here, here, and here. Some of this logic is dictated by configuration options (e.g. here)
I was either going to have to duplicate this logic, or get creative about how to reuse the existing logic while maintaining compatibility.

Because of the above reasons, I decided to generate the metadata automatically as part of the packaging logic. This allowed reuse of the existing logic to determine what to include in the final archive, while maintaining full compatibility.

I also struggled a bit with naming, and I welcome feedback.

  1. I named the endpoint libraries, since that seemed to indicate it's primary purpose. In the original issue I filed (Spring Boot Actuator "manifest" endpoint #22924), I suggested manifest, but that seemed too generic.
  2. I described the libraries contained within the application archive as the bundled libraries. I also considered packaged, but thought bundled ultimately was more clear, since packaged could be mis-interpreted since all libraries are generally packaged themselves.

Current state

New functionality is working, documented, and unit/integration tested.

Please provide feedback and suggestions. I'm happy to incorporate them.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 9, 2021
@philsttr philsttr force-pushed the actuator_libraries_endpoint branch 3 times, most recently from d82a414 to 88aaf52 Compare February 16, 2021 01:06
@philsttr philsttr marked this pull request as ready for review February 16, 2021 01:39
@rfelgent
Copy link

rfelgent commented Feb 28, 2021

Hi @philsttr ,

I like your idea. Could you give me a better understanding how your approach with actuator/libraries is different than the pom.xml enclosed in the fat jar ?

Do you have any concerns regarding vulnerability/security by providing such sensitive data via actuator?

@philsttr
Copy link
Author

Hi @rfelgent

Could you give me a better understanding how your approach with actuator/libraries is different than the pom.xml enclosed in the fat jar

  • The pom.xml enclosed in the fat jar does not include transitive dependencies. (Although, an alternative approach could be to use the flatten-maven-plugin to flatten all the transitive dependencies into the pom.xml that is included in the fat jar.)
  • The pom.xml is not included in the fat jar when using gradle (Although, there might be a way to do so, and there might be an equivalent to the flatten-maven-plugin for gradle as well. I'm not as familiar with gradle)

Do you have any concerns regarding vulnerability/security by providing such sensitive data via actuator?

I don't, since:

  • The endpoint is disabled by default (secure by default)
  • When enabling the endpoint, you can secure it using spring security, like any other actuator endpoint. For example, require authentication, and require specific scopes/authorities.

Adds a libraries actuator endpoint that displays details about the libraries used by the application.
Provides out-of-the-box support for displaying details about Java libraries that are bundled within the application jar/war archive.

The spring-boot-maven-plugin and spring-boot-gradle-plugin now generate a META-INF/bundled-libraries.yaml file when they package the application archive.
The file is included in the packaged application archive.

At runtime, libraries are contributed to the libraries endpoint via LibrariesContributor beans, similar to how InfoContributers contribute to the info endpoint.

spring-boot-actuator-autoconfigure auto-configures a LibrariesContributor that adds the libraries from META-INF/bundled-libraries.yaml to the libraries endpoint.
This auto-configuration can be disabled using environment properties similar to how other actuator endpoints behave.

An application can provide other LibrariesContributor instances to contribute more libraries to the libraries endpoint.
For example, an application might want to contribute libraries dynamically added at runtime.
Or an application might want to contribute details about front-end libraries.

Each Library is described as a simple set of key/value pairs.
For example, Java libraries with coordinates in a Maven repository typically contain entries for `groupId`, `artifactId`, and `version`.

Fixes spring-projects#22924
@philsttr philsttr force-pushed the actuator_libraries_endpoint branch from 88aaf52 to 9426c23 Compare February 28, 2021 19:19
@vpavic
Copy link
Contributor

vpavic commented Mar 2, 2021

My initial thoughts around this proposal is that a report of this kind is much safer to be generated at build time as an additional artifact, rather than being bundled within the application artifact and exposed over an Actuator endpoint.

The area of supply chain attacks is something that's been in the focus recently, and IMO proposals such as this one should take that into account very seriously. With an endpoint like this one, it's quite likely someone will leak sensitive data at some point - either due to negligence, misconfiguration or simply a security breach. That has the potential to be quite a problem if your application artifact uses dependencies that are published privately within your organization, under a namespace that hasn't been claimed on public repositories.

@wilkinsona
Copy link
Member

That has the potential to be quite a problem if your application artifact uses dependencies that are published privately within your organization, under a namespace that hasn't been claimed on a public repositories.

https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610 is an interesting account of how this can be exploited in other ecosystems. Maven Central provides some protection against this but that's not sufficient to make the JVM ecosystem immune to this sort of attack.

@philsttr
Copy link
Author

philsttr commented Mar 2, 2021

Even with the supply chain attacks, we'd like to have this functionality in our applications. I'd like to propose some alternatives in order of my preference.

A. Keep the PR mostly as-is, but add more strongly worded warnings to the documentation about the security risks of the libraries endpoint.

B. Keep the libraries actuator endpoint, but don't generate the metadata at build time by default. (It's currently generated at build time by default in this PR.) Make the user explicitly configure the maven/gradle plugins to generate the metadata, and include it in the artifact. This makes a user jump through one more hoop if they actually want to expose the data, since the libraries endpoint won't show anything if the metadata doesn't exist. This will help in the case the user has already configured management.endpoints.web.exposure.include=*

C. Keep the libraries actuator endpoint, and generate the metadata at build time by default, but don't include it in the artifact by default. Make the user explicitly configure the maven/gradle plugins to include the metadata in the artifact. Again, this makes a user jump through one more hoop if they actually want to expose the data, since the libraries endpoint won't show anything if the metadata doesn't exist.

D. Remove the libraries actuator endpoint from spring boot, but keep the ability to generate the metadata (and being able to include it in the artifact) in the spring boot maven/gradle plugins. In which case, we would move the libraries endpoint code into a private library. Keep the metadata in the current format (yaml file), or at least in some kind of machine-readable format. This will allow the libraries endpoint from our corporate library to read and expose the metadata.

@wilkinsona
Copy link
Member

Thanks for the pull request, @philsttr. As indicated by the pending-design-work label on #22924, I don't think we're ready to merge something in this area just yet. That feeling has been reinforced by recently becoming more familiar with the industry's various software bill-of-materials efforts that I've described in #22924 (comment). I think we need to wait until those efforts settle down a bit and then we can consider what, if anything, we should do to integrate one (or perhaps more) of them. If you're a Maven user, it looks like you can already go a pretty long way with CycloneDX and its existing plugin. Rather than attaching it alongside the other deploy artifacts, the generated sbom could be bundled in your application's jar file and then served as static content or via a small custom endpoint.

@wilkinsona wilkinsona closed this Mar 9, 2021
@wilkinsona wilkinsona added status: declined A suggestion or change that we don't feel we should currently apply and removed status: waiting-for-triage An issue we've not yet triaged labels Mar 9, 2021
@vpavic vpavic mentioned this pull request Oct 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: declined A suggestion or change that we don't feel we should currently apply
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Spring Boot Actuator "manifest" endpoint
5 participants