Support for standard Forwarded header

Closes #11580
This commit is contained in:
Pedro Igor 2023-02-15 19:00:57 -03:00 committed by Václav Muzikář
parent 7d136c5cca
commit 2b98fcdecb
4 changed files with 31 additions and 3 deletions

View file

@ -33,13 +33,15 @@ To select the proxy mode, enter this command:
== Configure the reverse proxy
Some Keycloak features rely on the assumption that the remote address of the HTTP request connecting to Keycloak is the real IP address of the clients machine.
When you have a reverse proxy or a load balancer in front of Keycloak, this might not be the case, so please make sure your reverse proxy is configured correctly by performing these actions:
When you have a reverse proxy or a load balancer in front of Keycloak, please make sure your reverse proxy is overriding the following headers:
* Set the X-Forwarded-For, X-Forwarded-Proto, and X-Forwarded-Host HTTP headers.
* `Forwarded` as per https://www.rfc-editor.org/rfc/rfc7239.html[RFC7239]
* Non-standard `X-Forwarded`
* Non-standard `X-Forwarded-*`, such as `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host`, and `X-Forwarded-Port`
To set these headers, consult the documentation for your reverse proxy.
Take extra precautions to ensure that the X-Forwarded-For header is set by your reverse proxy.
Take extra precautions to ensure that the client address is properly set by your reverse proxy via the `Forwarded` or `X-Forwarded-For` headers.
If this header is incorrectly configured, rogue clients can set this header and trick Keycloak into thinking the client is connected from a different IP address than the actual address.
This precaution can be more critical if you do any deny or allow listing of IP addresses.

View file

@ -19,4 +19,14 @@ public class ProxyOptions {
.category(OptionCategory.PROXY)
.defaultValue(Boolean.FALSE)
.build();
public static final Option<Boolean> PROXY_FORWARDED_HEADER_ENABLED = new OptionBuilder<>("proxy-allow-forwarded-header", Boolean.class)
.category(OptionCategory.PROXY)
.defaultValue(Boolean.FALSE)
.build();
public static final Option<Boolean> PROXY_X_FORWARDED_HEADER_ENABLED = new OptionBuilder<>("proxy-allow-x-forwarded-header", Boolean.class)
.category(OptionCategory.PROXY)
.defaultValue(Boolean.FALSE)
.build();
}

View file

@ -26,6 +26,16 @@ final class ProxyPropertyMappers {
.to("quarkus.http.proxy.enable-forwarded-host")
.mapFrom("proxy")
.transformer(ProxyPropertyMappers::getResolveEnableForwardedHost)
.build(),
fromOption(ProxyOptions.PROXY_FORWARDED_HEADER_ENABLED)
.to("quarkus.http.proxy.allow-forwarded")
.mapFrom("proxy")
.transformer(ProxyPropertyMappers::getResolveEnableForwardedHost)
.build(),
fromOption(ProxyOptions.PROXY_X_FORWARDED_HEADER_ENABLED)
.to("quarkus.http.proxy.allow-x-forwarded")
.mapFrom("proxy")
.transformer(ProxyPropertyMappers::getResolveEnableForwardedHost)
.build()
};
}

View file

@ -55,6 +55,12 @@ public class ProxyDistTest {
assertXForwardedHeaders();
}
@Test
@Launch({ "start-dev", "--hostname=mykeycloak.org", "--proxy=edge" })
public void testForwardedHeadersWithEdge() {
given().header("Forwarded", "for=12.34.56.78;host=test:1234;proto=https, for=23.45.67.89").when().get("http://mykeycloak.org:8080").then().body(containsString("https://test:1234/admin"));
}
@Test
@Launch({ "start-dev", "--hostname=mykeycloak.org", "--proxy=reencrypt" })
public void testXForwardedHeadersWithReencrypt() {