If you run a spring-boot application inside a docker container on context root, the actuator endpoint is exposed at: localhost:8080/actuator. (assuming the docker container runs on 8080).
Spring generated the base url for the actuator links by request.getRequestURL(). And as inside a docker container, this is also always the context root:
{
"_links": {
"self": {
"href": "http://localhost:8080/actuator",
"templated": false
},
"auditevents": {
"href": "http://localhost:8080/actuator/auditevents",
"templated": false
},
"health": {
"href": "http://localhost:8080/actuator/health",
"templated": false
},
....
Problem: when you're behind a loadbalancer or a simple apache2 reverse proxy, you may face situations where the actuator endpoint must be "rewritten". If you run multiple applications on a host, you often have a subpath mapping as follows (in this case apache2, but it does not matter):
ProxyPass https://yourhost/api1 http://localhost:8080
ProxyPassReverse https://yourhost/api1 http://localhost:8080
ProxyPass https://yourhost/api2 http://localhost:8081
ProxyPassReverse https://yourhost/api2 http://localhost:8081
This way, clients can connect via SSL without opening extra ports, and forward them to the desired docker app.
Problem: request.getRequestURL() only sees the forwarded request, not the original request.
As a result, health endpoints always result in https://yourhost/actuator or https://yourhost/actuator/health, which thus never reach the desired actuator endpoint (as the subpath mapping for apache2 is missing).
It should thus be possible to set the base-path for the actuator endpoint href links explicit via configuration, but without changing the actual path on which the actuator endpoint is accessed (it should still be exposed under the /actuator root context).
org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping.class:
class WebMvcLinksHandler implements LinksHandler {
@Override
@ResponseBody
public Map<String, Map<String, Link>> links(HttpServletRequest request, HttpServletResponse response) {
return Collections.singletonMap("_links",
WebMvcEndpointHandlerMapping.this.linksResolver.resolveLinks(request.getRequestURL().toString()));
}
}
I know there is management.endpoints.web.base-path, but this actually changes also the path where the actuator is exposed.
I'm looking for a way that only changes the base path that is used to generate the links, but keeps the actuator at default path.
Comment From: philwebb
Usually when running behind a proxy-server the X-Forwarded-Host header is used to replace the local host name with the actual one. @membersound have you looked at this section of the reference docs?
Comment From: membersound
Yes I know, but the X-Forwarded-Host is not the problem here.
The problem is that the subpath like /api1 or /api2 gets lost on proxy forwarding, and is thus missing inside the generated actuator links, which are therefore not accessible.
Like: actuator generates https://yourhost/actuator inside the docker container, whereas I need generated links like https://yourhost/api1/actuator to accessible from the outside. When those are accessed, again they reach the proxy first, then "api1" path is removed by forwarding to correct 8080 docker app.
Comment From: bclozel
Is your proxy adding the relevant Forwarded or X-Forwarded-Prefix headers for this prefix? If not, this is an issue on the proxy side as there is no way for Spring to reliably know about this prefix.
Comment From: membersound
That would work, but is a problem for those types of apache configurations. Because you can only set the static value for X-Forwarded-Prefix once.
So it does not help in cases where multiple docker apps are running, each having its own actuator endpoint, like:
<VirtualHost *:443>
//SSL stuff
ProxyPass https://yourhost/api1 http://localhost:8080
ProxyPassReverse https://yourhost/api1 http://localhost:8080
ProxyPass https://yourhost/api2 http://localhost:8081
ProxyPassReverse https://yourhost/api2 http://localhost:8081
RequestHeader set X-Forwarded-Prefix "/api"
</VirtualHost>
Is there any way in Spring to set X-Forwarded-Prefix to a static value inside each application, eg during startup or interceptors?
So that before /actuator request.getRequestURL() is evaluated, the -Prefix is already applied and thus sets the context path?
Comment From: bclozel
This is really a proxy issue. You could address this limitation with a Servlet filter, but I strongly suggest to try and solve this problem at the proxy level as it should.
Comment From: membersound
I discovered the Location tag can be used in VirtualHost as follows:
<VirtualHost *:443>
<Location "/api1">
ProxyPass http://localhost:8080
ProxyPassReverse http://localhost:8080
RequestHeader set X-Forwarded-Prefix "/api1"
</Location>
</VirtualHost>
Now I can see x-forwarded-prefix:"/api1", but the actuator endpoint still does not show the /api1 path. What would I have to change in Spring in order to have the prefix path respected?
Comment From: bclozel
Phil pointed already to the relevant section in the docs.
Comment From: membersound
So I have to set server.forward-headers-strategy=framework (instead of native) to make use of x-forwarded-prefix.
Drawback is here that with "framework" the headers are actually removed from the request, and when logging it, all forwarded headers are gone. With "native", they are still present in the HttpRequest object.
Question: is there any way to tell spring to not remove the forwarded headers?
Comment From: philwebb
There's not way to use ForwardedHeaderFilter and not remove the headers. The source code for the filter is here.