fix: adds additional info / warnings to hostname v2 (#33261)
* fix: adds additional info / warnings to hostname v2 closes: #24815 Signed-off-by: Steve Hawkins <shawkins@redhat.com> * refining the proxy-headers language from #33209 Signed-off-by: Steve Hawkins <shawkins@redhat.com> * adding hostname-strict-https Signed-off-by: Steve Hawkins <shawkins@redhat.com> * moving removed property check to the quarkus side Signed-off-by: Steve Hawkins <shawkins@redhat.com> * Update quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HostnameV2PropertyMappers.java Co-authored-by: Martin Bartoš <mabartos@redhat.com> Signed-off-by: Steven Hawkins <shawkins@redhat.com> * Update docs/guides/server/hostname.adoc Signed-off-by: Steven Hawkins <shawkins@redhat.com> --------- Signed-off-by: Steve Hawkins <shawkins@redhat.com> Signed-off-by: Steven Hawkins <shawkins@redhat.com> Co-authored-by: Martin Bartoš <mabartos@redhat.com>
This commit is contained in:
parent
cf2ecf87f6
commit
f1a7a4804e
6 changed files with 33 additions and 10 deletions
|
@ -48,7 +48,7 @@ The result of this configuration is that you can continue to access {project_nam
|
||||||
|
|
||||||
== Using a reverse proxy
|
== Using a reverse proxy
|
||||||
|
|
||||||
When a proxy is in use, the `proxy-headers` should be set. Depending on the hostname settings, some or all of the URL, may be dynamically determined.
|
When a proxy is forwarding http or reencrypted TLS requests, the `proxy-headers` option should be set. Depending on the hostname settings, some or all of the URL, may be dynamically determined.
|
||||||
|
|
||||||
WARNING: If either `forwarded` or `xforwarded` is selected, make sure your reverse proxy properly sets and overwrites the `Forwarded` or `X-Forwarded-*` headers respectively. To set these headers, consult the documentation for your reverse proxy. Misconfiguration will leave {project_name} exposed to security vulnerabilities.
|
WARNING: If either `forwarded` or `xforwarded` is selected, make sure your reverse proxy properly sets and overwrites the `Forwarded` or `X-Forwarded-*` headers respectively. To set these headers, consult the documentation for your reverse proxy. Misconfiguration will leave {project_name} exposed to security vulnerabilities.
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ Distributed environments frequently require the use of a reverse proxy. {project
|
||||||
* `forwarded` enables parsing of the `Forwarded` header as per https://www.rfc-editor.org/rfc/rfc7239.html[RFC7239].
|
* `forwarded` enables parsing of the `Forwarded` header as per https://www.rfc-editor.org/rfc/rfc7239.html[RFC7239].
|
||||||
* `xforwarded` enables parsing of non-standard `X-Forwarded-*` headers, such as `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host`, and `X-Forwarded-Port`.
|
* `xforwarded` enables parsing of non-standard `X-Forwarded-*` headers, such as `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host`, and `X-Forwarded-Port`.
|
||||||
|
|
||||||
NOTE: If you are using a reverse proxy and do not set the `proxy-headers` option, then by default you will see 403 Forbidden responses to requests via the proxy that perform origin checking.
|
NOTE: If you are using a reverse proxy for anything other than https passthrough and do not set the `proxy-headers` option, then by default you will see 403 Forbidden responses to requests via the proxy that perform origin checking.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,9 @@ import org.keycloak.quarkus.runtime.KeycloakMain;
|
||||||
import org.keycloak.quarkus.runtime.Messages;
|
import org.keycloak.quarkus.runtime.Messages;
|
||||||
import org.keycloak.quarkus.runtime.cli.ExecutionExceptionHandler;
|
import org.keycloak.quarkus.runtime.cli.ExecutionExceptionHandler;
|
||||||
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource;
|
||||||
|
import org.keycloak.quarkus.runtime.configuration.mappers.HostnameV2PropertyMappers;
|
||||||
import org.keycloak.quarkus.runtime.configuration.mappers.HttpPropertyMappers;
|
import org.keycloak.quarkus.runtime.configuration.mappers.HttpPropertyMappers;
|
||||||
|
import org.keycloak.url.HostnameV2ProviderFactory;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -44,6 +46,7 @@ public abstract class AbstractStartCommand extends AbstractCommand implements Ru
|
||||||
doBeforeRun();
|
doBeforeRun();
|
||||||
CommandLine cmd = spec.commandLine();
|
CommandLine cmd = spec.commandLine();
|
||||||
HttpPropertyMappers.validateConfig();
|
HttpPropertyMappers.validateConfig();
|
||||||
|
HostnameV2PropertyMappers.validateConfig();
|
||||||
validateConfig();
|
validateConfig();
|
||||||
|
|
||||||
if (ConfigArgsConfigSource.getAllCliArgs().contains(OPTIMIZED_BUILD_OPTION_LONG) && !wasBuildEverRun()) {
|
if (ConfigArgsConfigSource.getAllCliArgs().contains(OPTIMIZED_BUILD_OPTION_LONG) && !wasBuildEverRun()) {
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
package org.keycloak.quarkus.runtime.configuration.mappers;
|
package org.keycloak.quarkus.runtime.configuration.mappers;
|
||||||
|
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.config.HostnameV2Options;
|
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
|
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
|
||||||
|
|
||||||
final class HostnameV2PropertyMappers {
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.config.HostnameV2Options;
|
||||||
|
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||||
|
|
||||||
|
public final class HostnameV2PropertyMappers {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(PropertyMappers.class);
|
||||||
|
private static final List<String> REMOVED_OPTIONS = Arrays.asList("hostname-admin-url", "hostname-path", "hostname-port", "hostname-strict-backchannel", "hostname-url", "proxy", "hostname-strict-https");
|
||||||
|
|
||||||
private HostnameV2PropertyMappers(){}
|
private HostnameV2PropertyMappers(){}
|
||||||
|
|
||||||
|
@ -28,5 +35,13 @@ final class HostnameV2PropertyMappers {
|
||||||
.map(b -> b.isEnabled(() -> Profile.isFeatureEnabled(Profile.Feature.HOSTNAME_V2), "hostname:v2 feature is enabled").build())
|
.map(b -> b.isEnabled(() -> Profile.isFeatureEnabled(Profile.Feature.HOSTNAME_V2), "hostname:v2 feature is enabled").build())
|
||||||
.toArray(s -> new PropertyMapper<?>[s]);
|
.toArray(s -> new PropertyMapper<?>[s]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void validateConfig() {
|
||||||
|
List<String> inUse = REMOVED_OPTIONS.stream().filter(s -> Configuration.getOptionalKcValue(s).isPresent()).toList();
|
||||||
|
|
||||||
|
if (!inUse.isEmpty()) {
|
||||||
|
LOGGER.errorf("Hostname v1 options %s are still in use, please review your configuration", inUse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ public class LoginStatusIframeEndpoint {
|
||||||
if (validWebOrigins.contains("*") || validWebOrigins.contains(origin)) {
|
if (validWebOrigins.contains("*") || validWebOrigins.contains(origin)) {
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
}
|
}
|
||||||
logger.debugf("client %s does not allow origin=%s for requestOrigin=%s (as determined by hostname settings), init will return a 403", clientId, origin, requestOrigin);
|
logger.debugf("client %s does not allow origin=%s for requestOrigin=%s (as determined by the proxy-header setting), init will return a 403", clientId, origin, requestOrigin);
|
||||||
} else {
|
} else {
|
||||||
logger.debugf("client %s does not exist or not enabled, init will return a 403", clientId);
|
logger.debugf("client %s does not exist or not enabled, init will return a 403", clientId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.net.URI;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -32,6 +33,9 @@ import org.keycloak.urls.HostnameProviderFactory;
|
||||||
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||||
*/
|
*/
|
||||||
public class HostnameV2ProviderFactory implements HostnameProviderFactory, EnvironmentDependentProviderFactory {
|
public class HostnameV2ProviderFactory implements HostnameProviderFactory, EnvironmentDependentProviderFactory {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(HostnameV2ProviderFactory.class);
|
||||||
|
|
||||||
private static final String INVALID_HOSTNAME = "Provided hostname is neither a plain hostname nor a valid URL";
|
private static final String INVALID_HOSTNAME = "Provided hostname is neither a plain hostname nor a valid URL";
|
||||||
private String hostname;
|
private String hostname;
|
||||||
private URI hostnameUrl;
|
private URI hostnameUrl;
|
||||||
|
@ -41,7 +45,7 @@ public class HostnameV2ProviderFactory implements HostnameProviderFactory, Envir
|
||||||
@Override
|
@Override
|
||||||
public void init(Config.Scope config) {
|
public void init(Config.Scope config) {
|
||||||
// Strict mode is used just for enforcing that hostname is set
|
// Strict mode is used just for enforcing that hostname is set
|
||||||
Boolean strictMode = config.getBoolean("hostname-strict", false);
|
boolean strictMode = config.getBoolean("hostname-strict", false);
|
||||||
|
|
||||||
String hostnameRaw = config.get("hostname");
|
String hostnameRaw = config.get("hostname");
|
||||||
if (strictMode && hostnameRaw == null) {
|
if (strictMode && hostnameRaw == null) {
|
||||||
|
@ -49,6 +53,7 @@ public class HostnameV2ProviderFactory implements HostnameProviderFactory, Envir
|
||||||
} else if (hostnameRaw != null && !strictMode) {
|
} else if (hostnameRaw != null && !strictMode) {
|
||||||
// We might not need this validation as it doesn't matter in this case if strict is true or false. It's just for consistency – hostname XOR !strict.
|
// We might not need this validation as it doesn't matter in this case if strict is true or false. It's just for consistency – hostname XOR !strict.
|
||||||
// throw new IllegalArgumentException("hostname is configured, hostname-strict must be set to true");
|
// throw new IllegalArgumentException("hostname is configured, hostname-strict must be set to true");
|
||||||
|
LOGGER.info("If hostanme is specified, hostname-strict is effectively ignored");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set hostname, can be either a full URL, or just hostname
|
// Set hostname, can be either a full URL, or just hostname
|
||||||
|
|
Loading…
Reference in a new issue