Issue
We're using Spring Boot 2.7.0 with spring-boot-starter-actuator
that we're exposing on port 8081 under the /management
context path. The proxy sets several X-Forwarded-*
headers, including the X-Forwarded-Prefix
header that is set to /service
. But when navigating to https://www.company.com/management
this is what is returned:
{
"_links": {
"self": {
"href": "https://www.company.com/management",
"templated": false
},
"beans": {
"href": "https://www.company.com/management/beans",
"templated": false
},
"caches-cache": {
"href": "https://www.company.com/management/caches/{cache}",
"templated": true
},
"caches": {
"href": "https://www.company.com/management/caches",
"templated": false
},
"health": {
"href": "https://www.company.com/management/health",
"templated": false
},
"health-path": {
"href": "https://www.company.com/management/health/{*path}",
"templated": true
},
"info": {
"href": "https://www.company.com/management/info",
"templated": false
},
"conditions": {
"href": "https://www.company.com/management/conditions",
"templated": false
},
"configprops": {
"href": "https://www.company.com/management/configprops",
"templated": false
},
"configprops-prefix": {
"href": "https://www.company.com/management/configprops/{prefix}",
"templated": true
},
"env": {
"href": "https://www.company.com/management/env",
"templated": false
},
"env-toMatch": {
"href": "https://www.company.com/management/env/{toMatch}",
"templated": true
},
"integrationgraph": {
"href": "https://www.company.com/management/integrationgraph",
"templated": false
},
"loggers": {
"href": "https://www.company.com/management/loggers",
"templated": false
},
"loggers-name": {
"href": "https://www.company.com/management/loggers/{name}",
"templated": true
},
"heapdump": {
"href": "https://www.company.com/management/heapdump",
"templated": false
},
"threaddump": {
"href": "https://www.company.com/management/threaddump",
"templated": false
},
"metrics-requiredMetricName": {
"href": "https://www.company.com/management/metrics/{requiredMetricName}",
"templated": true
},
"metrics": {
"href": "https://www.company.com/management/metrics",
"templated": false
},
"scheduledtasks": {
"href": "https://www.company.com/management/scheduledtasks",
"templated": false
},
"sessions-sessionId": {
"href": "https://www.company.com/management/sessions/{sessionId}",
"templated": true
},
"sessions": {
"href": "https://www.company.com/management/sessions",
"templated": false
},
"mappings": {
"href": "https://www.company.com/management/mappings",
"templated": false
},
"refresh": {
"href": "https://www.company.com/management/refresh",
"templated": false
},
"features": {
"href": "https://www.company.com/management/features",
"templated": false
},
"traces": {
"href": "https://www.company.com/management/traces",
"templated": false
}
}
}
I'm expecting the href's in the response to start with https://www.company.com/service
due to the supplied X-Forwarded-Prefix
header, but this is not the case. I've tried to add the ForwardedHeaderFilter
like this:
@Bean
public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilterFilterRegistrationBean() {
ForwardedHeaderFilter forwardedHeaderFilter = new ForwardedHeaderFilter();
FilterRegistrationBean<ForwardedHeaderFilter> bean = new FilterRegistrationBean<>(forwardedHeaderFilter);
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
but it makes no difference.
How can I make actuator take the X-Forwarded-Prefix
header into account when generating the links to the endpoints behind a proxy?
Solution
I got the same problem. I found that ManagementContextAutoConfiguration
uses separate WebApplicationContext
if management port is different. That is why declaring of ForwardedHeaderFilter
has no effect. Maybe there is an issue in spring actuator. I made this hack and it seems to be working. But I hope somebody will find better solution.
@Component
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
public class ManagementContextFactoryBeanPostProcessor
implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof ManagementContextFactory managementContextFactory) {
return (ManagementContextFactory) (parent, configurationClasses) -> {
var context = managementContextFactory.createManagementContext(parent, configurationClasses);
if (context instanceof GenericWebApplicationContext genericWebApplicationContext) {
genericWebApplicationContext.registerBean(ForwardedHeaderFilterRegistrationBean.class);
}
return context;
};
}
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
public static class ForwardedHeaderFilterRegistrationBean
extends FilterRegistrationBean<ForwardedHeaderFilter> {
public ForwardedHeaderFilterRegistrationBean() {
setFilter(new ForwardedHeaderFilter());
setOrder(Ordered.HIGHEST_PRECEDENCE);
}
}
}
Answered By - Reeson
Answer Checked By - Gilberto Lyons (JavaFixing Admin)