The X-Forwarded-For header is intended to be used by proxies in your networking chain to track the IP address of the client from where the call originated. When a web request is made, the server receiving the request is aware of the IP address that is calling it. When using the X-Forwarded-For
header, the receiving server should append the calling client’s IP address to the list of IP addresses in that header value. The resulting header might look something like this: 'X-Forwarded-For: <client>, <proxy1>, <proxy2>'.
However, retrieving the client IP address from X-Forwarded-For header in Spring Boot 3 is considered unsafe. This approach is vulnerable to spoofing, as a malicious client could set an initial value for the X-Forwarded-For which would be accepted by the resolver.
Spring Cloud Gateway’s XForwardedRemoteAddressResolver has two static constructor methods to extract a trusted IP.
XForwardedRemoteAddressResolver::trustAll returns a RemoteAddressResolver that always takes the first IP address found in the X-Forwarded-For header. We shall avoid this option and pick up the second.
XForwardedRemoteAddressResolver::maxTrustedIndex takes an index that correlates to the number of trusted infrastructure running in front of Spring Cloud Gateway. If Spring Cloud Gateway is, for example only accessible through HAProxy, then a value of 1 should be used. If two hops of trusted infrastructure are required before Spring Cloud Gateway is accessible, then a value of 2 should be used.
In a spring cloud gateway app, we can use either GlobalFilter (Affects All Routes) or GatewayFilter(Per-Route Customization) to retrieve the client IP address for the purpose of modifying or validating the request before forwarding it.
Here is an example of GlobalFilter (Affects All Routes).
@Configuration
public class GatewayConfig {
@Bean
public GlobalFilter customClientIpFilter() {
return (exchange, chain) -> {
String clientIp = getClientIp(exchange);
System.out.println("Client IP: " + clientIp);
return chain.filter(exchange);
};
}
private String getClientIp(ServerWebExchange exchange) {
XForwardedRemoteAddressResolver resolver = XForwardedRemoteAddressResolver.maxTrustedIndex(1);
InetSocketAddress remoteAddress = resolver.resolve(exchange);
return (remoteAddress != null) ? remoteAddress.getAddress().getHostAddress() : "Unknown";
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> r.host("127.0.0.1")
.and()
.path("/example")
.uri("http://www.example.com"))
.build();
}
}
Modifying the way remote addresses are resolved