parent
2394855f48
commit
ffa6df5547
18 changed files with 5165 additions and 50 deletions
|
@ -27,6 +27,11 @@ To set the hostname part of the frontend base URL, enter this command:
|
||||||
|
|
||||||
<@kc.start parameters="--hostname=<value>"/>
|
<@kc.start parameters="--hostname=<value>"/>
|
||||||
|
|
||||||
|
You can also set a different port if your proxy is exposing the frontend URL using a port other than the default HTTP (80) and HTTPS(443) ports. For that,
|
||||||
|
set the `hostname-port` option.
|
||||||
|
|
||||||
|
<@kc.start parameters="--hostname=<value> --hostname-port=<port>"/>
|
||||||
|
|
||||||
=== Backend Endpoints
|
=== Backend Endpoints
|
||||||
Backend endpoints are used for direct communication between Keycloak and applications.
|
Backend endpoints are used for direct communication between Keycloak and applications.
|
||||||
Examples of backend endpoints are the Token endpoint and the User info endpoint.
|
Examples of backend endpoints are the Token endpoint and the User info endpoint.
|
||||||
|
@ -39,11 +44,6 @@ When all applications connected to Keycloak communicate through the public URL,
|
||||||
Otherwise, leave this parameter as false to allow internal applications to communicate with Keycloak through an internal URL.
|
Otherwise, leave this parameter as false to allow internal applications to communicate with Keycloak through an internal URL.
|
||||||
|
|
||||||
=== Administrative Endpoints
|
=== Administrative Endpoints
|
||||||
When the Admin Console is exposed on a different hostname, use `--hostname-admin` to link to it as shown in this example:
|
|
||||||
|
|
||||||
<@kc.start parameters="--hostname=<hostname> --hostname-admin=<adminHostname>"/>
|
|
||||||
|
|
||||||
When `hostname-admin` is configured, all links and static resources used to render the Admin Console are served from the value you enter for `<adminHostname>` instead of the value for `<hostname>`.
|
|
||||||
|
|
||||||
To reduce attack surface, the administration endpoints for Keycloak and the Admin Console should not be publicly accessible.
|
To reduce attack surface, the administration endpoints for Keycloak and the Admin Console should not be publicly accessible.
|
||||||
Therefore, you can secure them by using a reverse proxy.
|
Therefore, you can secure them by using a reverse proxy.
|
||||||
|
|
|
@ -13,9 +13,6 @@ For Keycloak, your choice of proxy modes depends on the TLS termination in your
|
||||||
== Proxy modes
|
== Proxy modes
|
||||||
The following proxy modes are available:
|
The following proxy modes are available:
|
||||||
|
|
||||||
none:: Disables proxy support.
|
|
||||||
It is the default mode.
|
|
||||||
|
|
||||||
edge:: Enables communication through HTTP between the proxy and Keycloak.
|
edge:: Enables communication through HTTP between the proxy and Keycloak.
|
||||||
This mode is suitable for deployments with a highly secure internal network where the reverse proxy keeps a secure connection (HTTP over TLS) with clients while communicating with Keycloak using HTTP.
|
This mode is suitable for deployments with a highly secure internal network where the reverse proxy keeps a secure connection (HTTP over TLS) with clients while communicating with Keycloak using HTTP.
|
||||||
|
|
||||||
|
|
2234
kubernetes/keycloakrealmimports.keycloak.org-v1.yml
Normal file
2234
kubernetes/keycloakrealmimports.keycloak.org-v1.yml
Normal file
File diff suppressed because it is too large
Load diff
2708
kubernetes/keycloaks.keycloak.org-v1.yml
Normal file
2708
kubernetes/keycloaks.keycloak.org-v1.yml
Normal file
File diff suppressed because it is too large
Load diff
|
@ -212,13 +212,13 @@ public class KeycloakDeploymentE2EIT extends ClusterOperatorTest {
|
||||||
Awaitility.await()
|
Awaitility.await()
|
||||||
.ignoreExceptions()
|
.ignoreExceptions()
|
||||||
.untilAsserted(() -> {
|
.untilAsserted(() -> {
|
||||||
String url = "https://" + service.getName() + "." + namespace + ":" + Constants.KEYCLOAK_HTTPS_PORT;
|
String url = "https://" + service.getName() + "." + namespace + ":" + Constants.KEYCLOAK_HTTPS_PORT + "/admin/master/console/";
|
||||||
Log.info("Checking url: " + url);
|
Log.info("Checking url: " + url);
|
||||||
|
|
||||||
var curlOutput = K8sUtils.inClusterCurl(k8sclient, namespace, "--insecure", "-H", "Host: foo.bar", url);
|
var curlOutput = K8sUtils.inClusterCurl(k8sclient, namespace, "-s", "--insecure", "-H", "Host: foo.bar", url);
|
||||||
Log.info("Curl Output: " + curlOutput);
|
Log.info("Curl Output: " + curlOutput);
|
||||||
|
|
||||||
assertTrue(curlOutput.contains("<a href=\"https://example.com:8443/admin/\">"));
|
assertTrue(curlOutput.contains("var authServerUrl = 'https://example.com:8443';"));
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
savePodLogs();
|
savePodLogs();
|
||||||
|
@ -237,13 +237,13 @@ public class KeycloakDeploymentE2EIT extends ClusterOperatorTest {
|
||||||
Awaitility.await()
|
Awaitility.await()
|
||||||
.ignoreExceptions()
|
.ignoreExceptions()
|
||||||
.untilAsserted(() -> {
|
.untilAsserted(() -> {
|
||||||
String url = "https://" + service.getName() + "." + namespace + ":" + Constants.KEYCLOAK_HTTPS_PORT;
|
String url = "https://" + service.getName() + "." + namespace + ":" + Constants.KEYCLOAK_HTTPS_PORT + "/admin/master/console/";
|
||||||
Log.info("Checking url: " + url);
|
Log.info("Checking url: " + url);
|
||||||
|
|
||||||
var curlOutput = K8sUtils.inClusterCurl(k8sclient, namespace, "--insecure", "-H", "Host: foo.bar", url);
|
var curlOutput = K8sUtils.inClusterCurl(k8sclient, namespace, "-s", "--insecure", "-H", "Host: foo.bar", url);
|
||||||
Log.info("Curl Output: " + curlOutput);
|
Log.info("Curl Output: " + curlOutput);
|
||||||
|
|
||||||
assertTrue(curlOutput.contains("<a href=\"https://foo.bar:8443/admin/\">"));
|
assertTrue(curlOutput.contains("var authServerUrl = 'https://foo.bar:8443';"));
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
savePodLogs();
|
savePodLogs();
|
||||||
|
|
|
@ -12,11 +12,6 @@ final class HostnamePropertyMappers {
|
||||||
.description("Hostname for the Keycloak server.")
|
.description("Hostname for the Keycloak server.")
|
||||||
.paramLabel("hostname")
|
.paramLabel("hostname")
|
||||||
.build(),
|
.build(),
|
||||||
builder().from("hostname-admin")
|
|
||||||
.to("kc.spi-hostname-default-admin")
|
|
||||||
.description("Overrides the hostname for the admin console and APIs.")
|
|
||||||
.paramLabel("url")
|
|
||||||
.build(),
|
|
||||||
builder().from("hostname-strict")
|
builder().from("hostname-strict")
|
||||||
.to("kc.spi-hostname-default-strict")
|
.to("kc.spi-hostname-default-strict")
|
||||||
.description("Disables dynamically resolving the hostname from request headers. Should always be set to true in production, unless proxy verifies the Host header.")
|
.description("Disables dynamically resolving the hostname from request headers. Should always be set to true in production, unless proxy verifies the Host header.")
|
||||||
|
@ -39,6 +34,12 @@ final class HostnamePropertyMappers {
|
||||||
.to("kc.spi-hostname-default-path")
|
.to("kc.spi-hostname-default-path")
|
||||||
.description("This should be set if proxy uses a different context-path for Keycloak.")
|
.description("This should be set if proxy uses a different context-path for Keycloak.")
|
||||||
.paramLabel("path")
|
.paramLabel("path")
|
||||||
|
.build(),
|
||||||
|
builder().from("hostname-port")
|
||||||
|
.to("kc.spi-hostname-default-hostname-port")
|
||||||
|
.defaultValue("-1")
|
||||||
|
.description("The port used by the proxy when exposing the hostname. Set this option if the proxy uses a port other than the default HTTP and HTTPS ports.")
|
||||||
|
.paramLabel("port")
|
||||||
.build()
|
.build()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import org.keycloak.quarkus.runtime.Messages;
|
||||||
|
|
||||||
final class ProxyPropertyMappers {
|
final class ProxyPropertyMappers {
|
||||||
|
|
||||||
private static final String[] possibleProxyValues = {"none", "edge", "reencrypt", "passthrough"};
|
private static final String[] possibleProxyValues = {"edge", "reencrypt", "passthrough"};
|
||||||
|
|
||||||
private ProxyPropertyMappers(){}
|
private ProxyPropertyMappers(){}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,8 @@ public final class DefaultHostnameProvider implements HostnameProvider, Hostname
|
||||||
private String adminHostName;
|
private String adminHostName;
|
||||||
private Boolean strictBackChannel;
|
private Boolean strictBackChannel;
|
||||||
private boolean hostnameEnabled;
|
private boolean hostnameEnabled;
|
||||||
|
private boolean strictHttps;
|
||||||
|
private int hostnamePort;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getScheme(UriInfo originalUriInfo, UrlType urlType) {
|
public String getScheme(UriInfo originalUriInfo, UrlType urlType) {
|
||||||
|
@ -60,6 +62,10 @@ public final class DefaultHostnameProvider implements HostnameProvider, Hostname
|
||||||
return scheme;
|
return scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ADMIN.equals(urlType)) {
|
||||||
|
return getScheme(originalUriInfo);
|
||||||
|
}
|
||||||
|
|
||||||
return fromFrontChannel(originalUriInfo, URI::getScheme, this::getScheme, defaultHttpScheme);
|
return fromFrontChannel(originalUriInfo, URI::getScheme, this::getScheme, defaultHttpScheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,9 +77,8 @@ public final class DefaultHostnameProvider implements HostnameProvider, Hostname
|
||||||
return hostname;
|
return hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
// admin hostname has precedence over frontchannel
|
if (ADMIN.equals(urlType)) {
|
||||||
if (ADMIN.equals(urlType) && adminHostName != null) {
|
return getHostname(originalUriInfo);
|
||||||
return adminHostName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fromFrontChannel(originalUriInfo, URI::getHost, this::getHostname, frontChannelHostName);
|
return fromFrontChannel(originalUriInfo, URI::getHost, this::getHostname, frontChannelHostName);
|
||||||
|
@ -97,6 +102,10 @@ public final class DefaultHostnameProvider implements HostnameProvider, Hostname
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPort(UriInfo originalUriInfo, UrlType urlType) {
|
public int getPort(UriInfo originalUriInfo, UrlType urlType) {
|
||||||
|
if (ADMIN.equals(urlType)) {
|
||||||
|
return getRequestPort();
|
||||||
|
}
|
||||||
|
|
||||||
Integer port = forNonStrictBackChannel(originalUriInfo, urlType, this::getPort, this::getPort);
|
Integer port = forNonStrictBackChannel(originalUriInfo, urlType, this::getPort, this::getPort);
|
||||||
|
|
||||||
if (port != null) {
|
if (port != null) {
|
||||||
|
@ -105,17 +114,15 @@ public final class DefaultHostnameProvider implements HostnameProvider, Hostname
|
||||||
|
|
||||||
if (hostnameEnabled && !noProxy) {
|
if (hostnameEnabled && !noProxy) {
|
||||||
// if proxy is enabled and hostname is set, assume the server is exposed using default ports
|
// if proxy is enabled and hostname is set, assume the server is exposed using default ports
|
||||||
return -1;
|
return hostnamePort;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fromFrontChannel(originalUriInfo, URI::getPort, this::getPort, null);
|
return fromFrontChannel(originalUriInfo, URI::getPort, this::getPort, hostnamePort == -1 ? getPort(originalUriInfo) : hostnamePort);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPort(UriInfo originalUriInfo) {
|
public int getPort(UriInfo originalUriInfo) {
|
||||||
KeycloakSession session = Resteasy.getContextData(KeycloakSession.class);
|
return noProxy && strictHttps ? defaultTlsPort : getRequestPort();
|
||||||
int requestPort = session.getContext().getContextObject(HttpRequest.class).getUri().getBaseUri().getPort();
|
|
||||||
return noProxy ? defaultTlsPort : requestPort;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T forNonStrictBackChannel(UriInfo originalUriInfo, UrlType urlType,
|
private <T> T forNonStrictBackChannel(UriInfo originalUriInfo, UrlType urlType,
|
||||||
|
@ -202,7 +209,7 @@ public final class DefaultHostnameProvider implements HostnameProvider, Hostname
|
||||||
|
|
||||||
hostnameEnabled = frontChannelHostName != null;
|
hostnameEnabled = frontChannelHostName != null;
|
||||||
|
|
||||||
Boolean strictHttps = config.getBoolean("strict-https", false);
|
strictHttps = config.getBoolean("strict-https", false);
|
||||||
|
|
||||||
if (strictHttps) {
|
if (strictHttps) {
|
||||||
defaultHttpScheme = "https";
|
defaultHttpScheme = "https";
|
||||||
|
@ -211,14 +218,22 @@ public final class DefaultHostnameProvider implements HostnameProvider, Hostname
|
||||||
defaultPath = config.get("path");
|
defaultPath = config.get("path");
|
||||||
noProxy = Configuration.getConfigValue("kc.proxy").getValue().equals("false");
|
noProxy = Configuration.getConfigValue("kc.proxy").getValue().equals("false");
|
||||||
defaultTlsPort = Integer.parseInt(Configuration.getConfigValue("kc.https-port").getValue());
|
defaultTlsPort = Integer.parseInt(Configuration.getConfigValue("kc.https-port").getValue());
|
||||||
|
hostnamePort = Integer.parseInt(Configuration.getConfigValue("kc.hostname-port").getValue());
|
||||||
adminHostName = config.get("admin");
|
adminHostName = config.get("admin");
|
||||||
strictBackChannel = config.getBoolean("strict-backchannel", false);
|
strictBackChannel = config.getBoolean("strict-backchannel", false);
|
||||||
|
|
||||||
LOGGER.infov("Hostname settings: FrontEnd: {0}, Strict HTTPS: {1}, Path: {2}, Strict BackChannel: {3}, Admin: {4}",
|
LOGGER.infov("Hostname settings: FrontEnd: {0}, Strict HTTPS: {1}, Path: {2}, Strict BackChannel: {3}, Admin: {4}, Port: {5}, Proxied: {6}",
|
||||||
frontChannelHostName == null ? "<request>" : frontChannelHostName,
|
frontChannelHostName == null ? "<request>" : frontChannelHostName,
|
||||||
strictHttps,
|
strictHttps,
|
||||||
defaultPath == null ? "<request>" : defaultPath,
|
defaultPath == null ? "<request>" : defaultPath,
|
||||||
strictBackChannel,
|
strictBackChannel,
|
||||||
adminHostName == null ? "<request>" : adminHostName);
|
adminHostName == null ? "<request>" : adminHostName,
|
||||||
|
hostnamePort,
|
||||||
|
!noProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getRequestPort() {
|
||||||
|
KeycloakSession session = Resteasy.getContextData(KeycloakSession.class);
|
||||||
|
return session.getContext().getContextObject(HttpRequest.class).getUri().getBaseUri().getPort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,8 +113,14 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
||||||
public void afterEach(ExtensionContext context) throws Exception {
|
public void afterEach(ExtensionContext context) throws Exception {
|
||||||
DistributionTest distConfig = getDistributionConfig(context);
|
DistributionTest distConfig = getDistributionConfig(context);
|
||||||
|
|
||||||
if (distConfig != null && distConfig.keepAlive()) {
|
if (distConfig != null) {
|
||||||
dist.stop();
|
if (distConfig.keepAlive()) {
|
||||||
|
dist.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DistributionTest.ReInstall.BEFORE_TEST.equals(distConfig.reInstall())) {
|
||||||
|
dist = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.afterEach(context);
|
super.afterEach(context);
|
||||||
|
|
|
@ -265,7 +265,7 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
|
||||||
Path distPath = distRootPath.resolve(distDirName.substring(0, distDirName.lastIndexOf('.')));
|
Path distPath = distRootPath.resolve(distDirName.substring(0, distDirName.lastIndexOf('.')));
|
||||||
|
|
||||||
if (!inited || (reCreate || !distPath.toFile().exists())) {
|
if (!inited || (reCreate || !distPath.toFile().exists())) {
|
||||||
distPath.toFile().delete();
|
FileUtils.deleteDirectory(distPath.toFile());
|
||||||
ZipUtils.unzip(distFile.toPath(), distRootPath);
|
ZipUtils.unzip(distFile.toPath(), distRootPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,8 +319,6 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
|
||||||
builder.environment().put("KEYCLOAK_ADMIN", "admin");
|
builder.environment().put("KEYCLOAK_ADMIN", "admin");
|
||||||
builder.environment().put("KEYCLOAK_ADMIN_PASSWORD", "admin");
|
builder.environment().put("KEYCLOAK_ADMIN_PASSWORD", "admin");
|
||||||
|
|
||||||
FileUtils.deleteDirectory(distPath.resolve("data").toFile());
|
|
||||||
|
|
||||||
keycloak = builder.start();
|
keycloak = builder.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
126
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HostnameDistTest.java
vendored
Normal file
126
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HostnameDistTest.java
vendored
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.it.cli.dist;
|
||||||
|
|
||||||
|
import static io.restassured.RestAssured.when;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.keycloak.it.cli.dist.util.CopyTLSKeystore;
|
||||||
|
import org.keycloak.it.junit5.extension.BeforeStartDistribution;
|
||||||
|
import org.keycloak.it.junit5.extension.DistributionTest;
|
||||||
|
import org.keycloak.it.junit5.extension.RawDistOnly;
|
||||||
|
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
|
||||||
|
|
||||||
|
import io.quarkus.test.junit.main.Launch;
|
||||||
|
import io.restassured.RestAssured;
|
||||||
|
|
||||||
|
@DistributionTest(keepAlive = true, reInstall = DistributionTest.ReInstall.BEFORE_TEST)
|
||||||
|
@BeforeStartDistribution(CopyTLSKeystore.class)
|
||||||
|
@RawDistOnly(reason = "Containers are immutable")
|
||||||
|
public class HostnameDistTest {
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void onBeforeAll() {
|
||||||
|
RestAssured.useRelaxedHTTPSValidation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Launch({ "start-dev", "--hostname=mykeycloak.127.0.0.1.nip.io" })
|
||||||
|
public void testSchemeAndPortFromRequestWhenNoProxySet() {
|
||||||
|
assertFrontEndUrl("http://mykeycloak.127.0.0.1.nip.io:8080", "http://mykeycloak.127.0.0.1.nip.io:8080/");
|
||||||
|
assertFrontEndUrl("http://localhost:8080", "http://mykeycloak.127.0.0.1.nip.io:8080/");
|
||||||
|
assertFrontEndUrl("https://localhost:8443", "https://mykeycloak.127.0.0.1.nip.io:8443/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Launch({ "start-dev", "--hostname=mykeycloak.127.0.0.1.nip.io", "--hostname-strict-https=true" })
|
||||||
|
public void testForceHttpsSchemeAndPortWhenStrictHttpsEnabled() {
|
||||||
|
assertFrontEndUrl("http://mykeycloak.127.0.0.1.nip.io:8080", "https://mykeycloak.127.0.0.1.nip.io:8443/");
|
||||||
|
assertFrontEndUrl("http://localhost:8080", "https://mykeycloak.127.0.0.1.nip.io:8443/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Launch({ "start-dev", "--hostname=mykeycloak.127.0.0.1.nip.io", "--hostname-port=8443" })
|
||||||
|
public void testForceHostnamePortWhenNoProxyIsSet() {
|
||||||
|
assertFrontEndUrl("http://mykeycloak.127.0.0.1.nip.io:8080", "http://mykeycloak.127.0.0.1.nip.io:8443/");
|
||||||
|
assertFrontEndUrl("https://mykeycloak.127.0.0.1.nip.io:8443", "https://mykeycloak.127.0.0.1.nip.io:8443/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Launch({ "start-dev", "--hostname=mykeycloak.127.0.0.1.nip.io", "--proxy=edge" })
|
||||||
|
public void testUseDefaultPortsWhenProxyIsSet() {
|
||||||
|
assertFrontEndUrl("http://mykeycloak.127.0.0.1.nip.io:8080", "http://mykeycloak.127.0.0.1.nip.io/");
|
||||||
|
assertFrontEndUrl("https://mykeycloak.127.0.0.1.nip.io:8443", "https://mykeycloak.127.0.0.1.nip.io/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Launch({ "start-dev", "--hostname=mykeycloak.127.0.0.1.nip.io", "--proxy=edge", "--hostname-strict-https=true" })
|
||||||
|
public void testUseDefaultPortsAndHttpsSchemeWhenProxyIsSetAndStrictHttpsEnabled() {
|
||||||
|
assertFrontEndUrl("http://mykeycloak.127.0.0.1.nip.io:8080", "https://mykeycloak.127.0.0.1.nip.io/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Launch({ "start-dev", "--hostname=mykeycloak.127.0.0.1.nip.io" })
|
||||||
|
public void testBackEndUrlFromRequest() {
|
||||||
|
assertBackEndUrl("http://localhost:8080", "http://localhost:8080/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Launch({ "start-dev", "--hostname=mykeycloak.127.0.0.1.nip.io", "--hostname-strict-backchannel=true" })
|
||||||
|
public void testBackEndUrlSameAsFrontEndUrl() {
|
||||||
|
assertBackEndUrl("http://localhost:8080", "http://mykeycloak.127.0.0.1.nip.io:8080/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Launch({ "start-dev", "--hostname=mykeycloak.127.0.0.1.nip.io", "--hostname-path=/auth", "--hostname-strict=true", "--hostname-strict-backchannel=true" })
|
||||||
|
public void testSetHostnamePath() {
|
||||||
|
assertFrontEndUrl("http://localhost:8080", "http://mykeycloak.127.0.0.1.nip.io:8080/auth/");
|
||||||
|
assertBackEndUrl("http://localhost:8080", "http://mykeycloak.127.0.0.1.nip.io:8080/auth/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Launch({ "start-dev", "--hostname=mykeycloak.127.0.0.1.nip.io", "--https-port=8543", "--hostname-strict-https=true" })
|
||||||
|
public void testDefaultTlsPortChangeWhenHttpPortSet() {
|
||||||
|
assertFrontEndUrl("http://mykeycloak.127.0.0.1.nip.io:8080", "https://mykeycloak.127.0.0.1.nip.io:8543/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Launch({ "start-dev", "--hostname=mykeycloak.127.0.0.1.nip.io", "--hostname-strict-https=true", "--hostname-port=8543" })
|
||||||
|
public void testWelcomePageAdminUrl() {
|
||||||
|
Assert.assertTrue(when().get("http://mykeycloak.127.0.0.1.nip.io:8080").asString().contains("http://mykeycloak.127.0.0.1.nip.io:8080/admin/"));
|
||||||
|
Assert.assertTrue(when().get("https://mykeycloak.127.0.0.1.nip.io:8443").asString().contains("https://mykeycloak.127.0.0.1.nip.io:8443/admin/"));
|
||||||
|
Assert.assertTrue(when().get("http://localhost:8080").asString().contains("http://localhost:8080/admin/"));
|
||||||
|
Assert.assertTrue(when().get("https://localhost:8443").asString().contains("https://localhost:8443/admin/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private OIDCConfigurationRepresentation getServerMetadata(String baseUrl) {
|
||||||
|
return when().get(baseUrl + "/realms/master/.well-known/openid-configuration").as(OIDCConfigurationRepresentation.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertFrontEndUrl(String requestBaseUrl, String expectedBaseUrl) {
|
||||||
|
Assert.assertEquals(expectedBaseUrl + "realms/master/protocol/openid-connect/auth", getServerMetadata(requestBaseUrl)
|
||||||
|
.getAuthorizationEndpoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertBackEndUrl(String requestBaseUrl, String expectedBaseUrl) {
|
||||||
|
Assert.assertEquals(expectedBaseUrl + "realms/master/protocol/openid-connect/token", getServerMetadata(requestBaseUrl)
|
||||||
|
.getTokenEndpoint());
|
||||||
|
}
|
||||||
|
}
|
30
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/util/CopyTLSKeystore.java
vendored
Normal file
30
quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/util/CopyTLSKeystore.java
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.it.cli.dist.util;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.keycloak.it.utils.KeycloakDistribution;
|
||||||
|
|
||||||
|
public class CopyTLSKeystore implements Consumer<KeycloakDistribution> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(KeycloakDistribution distribution) {
|
||||||
|
distribution.copyOrReplaceFileFromClasspath("/server.keystore", Path.of("conf", "server.keystore"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,10 +42,11 @@ Hostname:
|
||||||
|
|
||||||
--hostname <hostname>
|
--hostname <hostname>
|
||||||
Hostname for the Keycloak server.
|
Hostname for the Keycloak server.
|
||||||
--hostname-admin <url>
|
|
||||||
Overrides the hostname for the admin console and APIs.
|
|
||||||
--hostname-path <path>
|
--hostname-path <path>
|
||||||
This should be set if proxy uses a different context-path for Keycloak.
|
This should be set if proxy uses a different context-path for Keycloak.
|
||||||
|
--hostname-port <port>
|
||||||
|
The port used by the proxy when exposing the hostname. Set this option if the
|
||||||
|
proxy uses a port other than the default HTTP and HTTPS ports. Default: -1.
|
||||||
--hostname-strict <true|false>
|
--hostname-strict <true|false>
|
||||||
Disables dynamically resolving the hostname from request headers. Should
|
Disables dynamically resolving the hostname from request headers. Should
|
||||||
always be set to true in production, unless proxy verifies the Host header.
|
always be set to true in production, unless proxy verifies the Host header.
|
||||||
|
@ -93,7 +94,7 @@ HTTP/TLS:
|
||||||
Proxy:
|
Proxy:
|
||||||
|
|
||||||
--proxy <mode> The proxy address forwarding mode if the server is behind a reverse proxy.
|
--proxy <mode> The proxy address forwarding mode if the server is behind a reverse proxy.
|
||||||
Possible values are: none,edge,reencrypt,passthrough Default: none.
|
Possible values are: edge,reencrypt,passthrough Default: none.
|
||||||
|
|
||||||
Vault:
|
Vault:
|
||||||
|
|
||||||
|
|
|
@ -68,10 +68,11 @@ Hostname:
|
||||||
|
|
||||||
--hostname <hostname>
|
--hostname <hostname>
|
||||||
Hostname for the Keycloak server.
|
Hostname for the Keycloak server.
|
||||||
--hostname-admin <url>
|
|
||||||
Overrides the hostname for the admin console and APIs.
|
|
||||||
--hostname-path <path>
|
--hostname-path <path>
|
||||||
This should be set if proxy uses a different context-path for Keycloak.
|
This should be set if proxy uses a different context-path for Keycloak.
|
||||||
|
--hostname-port <port>
|
||||||
|
The port used by the proxy when exposing the hostname. Set this option if the
|
||||||
|
proxy uses a port other than the default HTTP and HTTPS ports. Default: -1.
|
||||||
--hostname-strict <true|false>
|
--hostname-strict <true|false>
|
||||||
Disables dynamically resolving the hostname from request headers. Should
|
Disables dynamically resolving the hostname from request headers. Should
|
||||||
always be set to true in production, unless proxy verifies the Host header.
|
always be set to true in production, unless proxy verifies the Host header.
|
||||||
|
@ -134,7 +135,7 @@ Metrics:
|
||||||
Proxy:
|
Proxy:
|
||||||
|
|
||||||
--proxy <mode> The proxy address forwarding mode if the server is behind a reverse proxy.
|
--proxy <mode> The proxy address forwarding mode if the server is behind a reverse proxy.
|
||||||
Possible values are: none,edge,reencrypt,passthrough Default: none.
|
Possible values are: edge,reencrypt,passthrough Default: none.
|
||||||
|
|
||||||
Vault:
|
Vault:
|
||||||
|
|
||||||
|
|
|
@ -45,10 +45,11 @@ Hostname:
|
||||||
|
|
||||||
--hostname <hostname>
|
--hostname <hostname>
|
||||||
Hostname for the Keycloak server.
|
Hostname for the Keycloak server.
|
||||||
--hostname-admin <url>
|
|
||||||
Overrides the hostname for the admin console and APIs.
|
|
||||||
--hostname-path <path>
|
--hostname-path <path>
|
||||||
This should be set if proxy uses a different context-path for Keycloak.
|
This should be set if proxy uses a different context-path for Keycloak.
|
||||||
|
--hostname-port <port>
|
||||||
|
The port used by the proxy when exposing the hostname. Set this option if the
|
||||||
|
proxy uses a port other than the default HTTP and HTTPS ports. Default: -1.
|
||||||
--hostname-strict <true|false>
|
--hostname-strict <true|false>
|
||||||
Disables dynamically resolving the hostname from request headers. Should
|
Disables dynamically resolving the hostname from request headers. Should
|
||||||
always be set to true in production, unless proxy verifies the Host header.
|
always be set to true in production, unless proxy verifies the Host header.
|
||||||
|
@ -96,7 +97,7 @@ HTTP/TLS:
|
||||||
Proxy:
|
Proxy:
|
||||||
|
|
||||||
--proxy <mode> The proxy address forwarding mode if the server is behind a reverse proxy.
|
--proxy <mode> The proxy address forwarding mode if the server is behind a reverse proxy.
|
||||||
Possible values are: none,edge,reencrypt,passthrough Default: none.
|
Possible values are: edge,reencrypt,passthrough Default: none.
|
||||||
|
|
||||||
Vault:
|
Vault:
|
||||||
|
|
||||||
|
|
BIN
quarkus/tests/integration/src/test/resources/server.keystore
Normal file
BIN
quarkus/tests/integration/src/test/resources/server.keystore
Normal file
Binary file not shown.
|
@ -83,10 +83,6 @@ public abstract class AbstractHostnameTest extends AbstractKeycloakTest {
|
||||||
additionalArgs.add("--hostname-strict-https=true");
|
additionalArgs.add("--hostname-strict-https=true");
|
||||||
}
|
}
|
||||||
additionalArgs.add("--hostname-strict-backchannel="+ forceBackendUrlToFrontendUrl);
|
additionalArgs.add("--hostname-strict-backchannel="+ forceBackendUrlToFrontendUrl);
|
||||||
if (adminUrl != null) {
|
|
||||||
URI adminUri = URI.create(adminUrl);
|
|
||||||
additionalArgs.add("--hostname-admin=" + adminUri.getHost());
|
|
||||||
}
|
|
||||||
container.setAdditionalBuildArgs(additionalArgs);
|
container.setAdditionalBuildArgs(additionalArgs);
|
||||||
controller.start(suiteContext.getAuthServerInfo().getQualifier());
|
controller.start(suiteContext.getAuthServerInfo().getQualifier());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -40,13 +40,14 @@ import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.QUARKUS;
|
||||||
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE;
|
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE;
|
||||||
import static org.keycloak.testsuite.util.OAuthClient.AUTH_SERVER_ROOT;
|
import static org.keycloak.testsuite.util.OAuthClient.AUTH_SERVER_ROOT;
|
||||||
import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot;
|
import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot;
|
||||||
|
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
|
||||||
@AuthServerContainerExclude({REMOTE})
|
@AuthServerContainerExclude({REMOTE, QUARKUS})
|
||||||
public class DefaultHostnameTest extends AbstractHostnameTest {
|
public class DefaultHostnameTest extends AbstractHostnameTest {
|
||||||
|
|
||||||
@ArquillianResource
|
@ArquillianResource
|
||||||
|
|
Loading…
Reference in a new issue