From b6d9205265dd63acdb9047b102370485c5a218c8 Mon Sep 17 00:00:00 2001 From: Andrew Fitzgerald Date: Fri, 6 Apr 2018 14:27:43 -0400 Subject: [PATCH] Fixes #238 document RemoteAddressResolvers (#239) document RemoteAddressResolvers Fixes gh-238 --- .../main/asciidoc/spring-cloud-gateway.adoc | 55 ++++++++++++++++++- .../gateway/route/builder/PredicateSpec.java | 13 ++++- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-gateway.adoc b/docs/src/main/asciidoc/spring-cloud-gateway.adoc index 54a365de..97a93588 100644 --- a/docs/src/main/asciidoc/spring-cloud-gateway.adoc +++ b/docs/src/main/asciidoc/spring-cloud-gateway.adoc @@ -225,7 +225,7 @@ This route would match if the request contained a `foo` query parameter whose va === RemoteAddr Route Predicate Factory -The RemoteAddr Route Predicate Factory takes a list (min size 1) of CIDR-notation (IPv4 or IPv6) strings, e.g. `192.168.0.1/16` (where `192.168.0.1` is an IP address and `16` is a subnet mask. +The RemoteAddr Route Predicate Factory takes a list (min size 1) of CIDR-notation (IPv4 or IPv6) strings, e.g. `192.168.0.1/16` (where `192.168.0.1` is an IP address and `16` is a subnet mask). .application.yml [source,yaml] @@ -242,7 +242,60 @@ spring: This route would match if the remote address of the request was, for example, `192.168.1.10`. +==== Modifying the way remote addresses are resolved +By default the RemoteAddr Route Predicate Factory uses the remote address from the incoming request. +This may not match the actual client IP address if Spring Cloud Gateway sits behind a proxy layer. + +You can customize the way that the remote address is resolved by setting a custom `RemoteAddressResolver`. +Spring Cloud Gateway comes with one non-default remote address resolver which is based off of the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For[X-Forwarded-For header], `XForwardedRemoteAddressResolver`. + +`XForwardedRemoteAddressResolver` has two static constructor methods which take different approaches to security: + +`XForwardedRemoteAddressResolver::trustAllXForwardedRemoteAddressResolver` returns a `RemoteAddressResolver` which always takes the first IP address found in the `X-Forwarded-For` header. +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. + +`XForwardedRemoteAddressResolver::maxTrustedIndexXForwardedRemoteAddressResolver` takes an index which correlates to the number of trusted infrastructure running in front of Spring Cloud Gateway. +If Spring Cloud Gateway is, for example only accessible via 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. + +Given the following header value: + +[source] +X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3 + + +The `maxTrustedIndex` values below will yield the following remote addresses. + +[options="header"] +|=== +|`maxTrustedIndex` | result +|[`Integer.MIN_VALUE`,0] | (invalid, `IllegalArgumentException` during initialization) +|1 | 0.0.0.3 +|2 | 0.0.0.2 +|3 | 0.0.0.1 +|[4, `Integer.MAX_VALUE`] | 0.0.0.1 +|=== [[gateway-route-filters]] + +Using Java config: + +GatewayConfig.java +[source,java] +---- +RemoteAddressResolver resolver = XForwardedRemoteAddressResolver + .maxTrustedIndexXForwardedRemoteAddressResolver(1); + +... + +.route("direct-route", + r -> r.remoteAddr("10.1.1.1", "10.10.1.1/24") + .uri("https://downstream1") +.route("proxied-route", + r -> r.remoteAddr(resolver, "10.10.1.1", "10.10.1.1/24") + .uri("https://downstream2") +) +---- + == GatewayFilter Factories Route filters allow the modification of the incoming HTTP request or outgoing HTTP response in some manner. Route filters are scoped to a particular route. Spring Cloud Gateway includes many built-in GatewayFilter Factories. diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java index 59fc96cd..f532030c 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java @@ -31,6 +31,7 @@ import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFa import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.WeightRoutePredicateFactory; import org.springframework.cloud.gateway.route.Route; +import org.springframework.cloud.gateway.support.ipresolver.RemoteAddressResolver; import org.springframework.http.HttpMethod; import org.springframework.web.server.ServerWebExchange; @@ -114,8 +115,16 @@ public class PredicateSpec extends UriSpec { } public BooleanSpec remoteAddr(String... addrs) { - return predicate(getBean(RemoteAddrRoutePredicateFactory.class) - .apply(c -> c.setSources(addrs))); + return remoteAddr(null, addrs); + } + + public BooleanSpec remoteAddr(RemoteAddressResolver resolver, String... addrs) { + return predicate(getBean(RemoteAddrRoutePredicateFactory.class).apply(c -> { + c.setSources(addrs); + if (resolver != null) { + c.setRemoteAddressResolver(resolver); + } + })); } public BooleanSpec weight(String group, int weight) {