Skip to content

ForwardedHeadersFilter: wrong mapping for IPv6 remote address #2214

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
hhwidera opened this issue Apr 16, 2021 · 4 comments
Closed

ForwardedHeadersFilter: wrong mapping for IPv6 remote address #2214

hhwidera opened this issue Apr 16, 2021 · 4 comments
Labels
Milestone

Comments

@hhwidera
Copy link
Contributor

hhwidera commented Apr 16, 2021

Hi,
I see a problem with IPv6 remote addresses and the org.springframework.cloud.gateway.filter.headers.ForwardedHeadersFilter.
When a remote address is in IPv6 format then in the Forwarded header I miss the '[' and ']' around the IPv6 address (see https://tools.ietf.org/html/rfc7239).

Example:
For '2001:db8:cafe:0:0:0:0:17' and port 4711 the result header should include 'for="[2001:db8:cafe:0:0:0:0:17]:4711"' but I see only 'for="2001:db8:cafe:0:0:0:0:17:4711"' (missing '[' and ']' around the IPv6 address).

Possible test:

public class ForwardedHeadersFilterTests {

	...

	@Test
	public void correctIPv6RemoteAddressMapping() throws UnknownHostException {
		MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/get")
				.remoteAddress(new InetSocketAddress(InetAddress.getByName("2001:db8:cafe:0:0:0:0:17"), 80))
				.header(HttpHeaders.HOST, "myhost").build();

		ForwardedHeadersFilter filter = new ForwardedHeadersFilter();

		HttpHeaders headers = filter.filter(request.getHeaders(), MockServerWebExchange.from(request));

		assertThat(headers.get(FORWARDED_HEADER)).hasSize(1);

		List<Forwarded> forwardeds = ForwardedHeadersFilter.parse(headers.get(FORWARDED_HEADER));

		assertThat(forwardeds).hasSize(1);
		Forwarded forwarded = forwardeds.get(0);

		assertThat(forwarded.getValues()).containsEntry("for", "\"[2001:db8:cafe:0:0:0:0:17]:80\"");
	}

Possible fix:

public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered {

	...

	@Override
	public HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange) {

		... 

		URI uri = request.getURI();
		String host = original.getFirst(HttpHeaders.HOST);
		Forwarded forwarded = new Forwarded().put("host", host).put("proto", uri.getScheme());

		InetSocketAddress remoteAddress = request.getRemoteAddress();
		if (remoteAddress != null) {
			// If remoteAddress is unresolved, calling getHostAddress() would cause a
			// NullPointerException.
			String forValue;
			if (remoteAddress.isUnresolved()) {
				forValue = remoteAddress.getHostName();
			} else {
				InetAddress address = remoteAddress.getAddress();
				forValue = remoteAddress.getAddress().getHostAddress();
				if (address instanceof Inet6Address) {
					forValue = "[" + forValue + "]";
				}
			}
			int port = remoteAddress.getPort();
			if (port >= 0) {
				forValue = forValue + ":" + port;
			}
			forwarded.put("for", forValue);
		}
		// TODO: support by?

		updated.add(FORWARDED_HEADER, forwarded.toHeaderValue());

		return updated;
	}

Thanks!

@hhwidera
Copy link
Contributor Author

see #2217

@lanmingle
Copy link

spring-projects/spring-framework#26748

我在测试中发现,这个能否解决 Invalid IPv4 address

		if (StringUtils.hasText(forwardedHeader)) {
			String forwardedToUse = StringUtils.tokenizeToStringArray(forwardedHeader, ",")[0];
			Matcher matcher = FORWARDED_FOR_PATTERN.matcher(forwardedToUse);
			if (matcher.find()) {
				String value = matcher.group(1).trim();
				String host = value;
				int portSeparatorIdx = value.lastIndexOf(':');
				int squareBracketIdx = value.lastIndexOf(']');
				if (portSeparatorIdx > squareBracketIdx) {
					if (squareBracketIdx == -1 && value.indexOf(':') != portSeparatorIdx) {
						throw new IllegalArgumentException("Invalid IPv4 address: " + value);
					}
					host = value.substring(0, portSeparatorIdx);
					try {
						port = Integer.parseInt(value.substring(portSeparatorIdx + 1));
					}
					catch (NumberFormatException ex) {
						throw new IllegalArgumentException(
								"Failed to parse a port from \"forwarded\"-type header value: " + value);
					}
				}
				return InetSocketAddress.createUnresolved(host, port);
			}
		}

@s-frei
Copy link

s-frei commented May 28, 2021

Experiencing the excat same issue. Glad there is already a fix provided 👍

@spencergibb
Copy link
Member

Closing in favor of #2217

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants