Skip to content

Improve documentation and/or support for securing requests made on a different management port #50355

@mauromol

Description

@mauromol

TLDR:
IMHO Spring Boot should provide a supported and documented way to set up the actuator endpoints on a separate management port with completely isolated Spring Security filter chains, or at least with a dedicated chain that does not cause requests made on that port to leak into other chains (unless I want it to).

Long explanation:

I have a Spring Boot application, secured with Spring Security and a custom security filter chain. I was requested to add support for management on a different port, with endpoints exposed through HTTP open to anyone. I followed the docs but it was quite hard for me to deeply understand what happens under the hood and how I should adapt my security configuration. Especially because matchers used to secure the application usually work on paths, and not on specific ports. And apparently there's no out-of-the-box support to match on ports in the actuator API as well.

So, this is what I ended up with, after quite some investigation and debugging.

Starting from https://docs.spring.io/spring-boot/reference/actuator/endpoints.html#actuator.endpoints.security I understood I had to create a separate security filter chain. It's key that this chain is applied first (through proper @Order, which is not evident from the example), so that:

  • if the securityMatcher matches (and this happens when the request is made on the management port), this chain is applied
  • otherwise the next available filter chain will be applied, instead, since it means the request was made on the standard port
  • (please note: this gets complicated when you have a WebSecurityCustomizer which adds ignored resources with ignoring().requestMatchers(...), because this will cause an additional chain to be created and put in front of any other chain - but this is a different story)

In my mind, I wanted that any request made on the management port would go into the first chain, being authorized or not based on what that chain establishes, while any request made on the standard port would go into the other chain. So two completely separate paths for processing the request, from reception to response, passing through filtering. However, with the solution proposed in that example, things are not fully working in this way. If I make a request to the management port with a path which does not correspond to a management endpoint, the security matcher returned by EndpointRequest.toAnyEndpoint() will return false, and hence my other security chain is queried, even if the request was made on the management port. In the end, most probably my main application resources would still be protected and not reachable, due to how Spring Boot works under the hood: it creates a separate application context, child of the main one, where another DispatcherServlet is configured, which can serve only management endpoints and nothing else (again, something that the documentation does not fully explain and which has important consequences that, instead, should be made perfectly clear to users, as the currently open issues demonstrate and my direct experience also proved, as I suddenly started to receive two ContextRefreshEvents on a listener that didn't expect it). So, even if security filter chains are not isolated by each other, the final request handling is. Still, the fact that requests made on the management port still leak into my main application security chain does not sound correct to me, at least not on an "out-of-the-box" experience, as I feel the two different ports should be doors to completely different resources and security processing. In my case, it was also causing troubles because my main security chain had quite some configuration that was expecting things to work in a way which simply does not apply to requests made on the management port, like assumptions on pre-authenticated HTTP headers which are instead missing, or redirections on AccessDeniedExceptions to resources that are not available on the context bound to requests made on the management port.
In the end, what I did was to apply on the above chain a security matcher extending ApplicationContextRequestMatcher that works similarly to the matcher returned by EndpointRequest.toAnyEndpoint() (i.e.: it filters out requests where the request context is not bound to the management application context, in its ignoreApplicationContext(WebApplicationContext)), but has matches(HttpServletRequest, Supplier) always returning true.

Another option could be to provide a way to define a completely independent filter chain, instead of one that goes into the same FilterChainProxy as the main one...
Maybe other people have different requirements, which require full or hybrid application processing of requests made on both ports (as some other open issues here seem to suggest), but this sounds more like the exception to me, rather than the first simple option to start with.

What do you think?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions