diff --git a/docs/documentation/upgrading/topics/keycloak/changes-22_0_0.adoc b/docs/documentation/upgrading/topics/keycloak/changes-22_0_0.adoc index 119cb1d5de5..95a94c6e202 100644 --- a/docs/documentation/upgrading/topics/keycloak/changes-22_0_0.adoc +++ b/docs/documentation/upgrading/topics/keycloak/changes-22_0_0.adoc @@ -357,4 +357,11 @@ Multiple Keycloak CRs may be created in the same namespace and will be managed i = k8s.keycloak.org/v2alpha1 changes -The condition status field was changed from a boolean to a string for conformance with standard Kubernetes conditions. In the CRD it will temporarily be represented as accepting any content, but it will only ever be a string. Please make sure any of your usage of this field is updated to expect the values "True", "False", or "Unknown", rather than true or false. \ No newline at end of file +The condition status field was changed from a boolean to a string for conformance with standard Kubernetes conditions. In the CRD it will temporarily be represented as accepting any content, but it will only ever be a string. Please make sure any of your usage of this field is updated to expect the values "True", "False", or "Unknown", rather than true or false. + += Keycloak supports IPv4/IPv6 dual stack + +Keycloak supports the IPv4/IPv6 dual stack and can be accessible by default via the IPv4 and IPv6 addresses. +In the older versions of Keycloak, the default approach was to use only IPv4 addresses. + +For more details, see https://www.keycloak.org/server/configuration-production#_configure_keycloak_server_with_ipv4_or_ipv6[Configure Keycloak Server with IPv4 or IPv6]. diff --git a/docs/guides/server/configuration-production.adoc b/docs/guides/server/configuration-production.adoc index 447752bc19e..2df43c70ec9 100644 --- a/docs/guides/server/configuration-production.adoc +++ b/docs/guides/server/configuration-production.adoc @@ -36,21 +36,19 @@ Keycloak runs on top of JGroups and Infinispan, which provide a reliable, high-a To find out more about using multiple nodes, the different caches and an appropriate stack for your environment, see <@links.server id="caching"/>. -== Configure Keycloak Server with IPv6 or IPv4 - +== Configure Keycloak Server with IPv4 or IPv6 The system properties `java.net.preferIPv4Stack` and `java.net.preferIPv6Addresses` are used to configure the JVM for use with IPv4 or IPv6 addresses. -By default, Keycloak is configured to prefer IPv4 addresses. In order to run with IPv6 addresses, -you need to specify `java.net.preferIPv4Stack=false` (the JVM default) and `java.net.preferIPv6Addresses=true`. -The latter ensures that any hostname to IP address conversions always return IPv6 address variants. +By default, Keycloak is accessible via IPv4 and IPv6 addresses at the same time. +In order to run only with IPv4 addresses, you need to specify the property `java.net.preferIPv4Stack=true`. +The latter ensures that any hostname to IP address conversions always return IPv4 address variants. -These system properties are conveniently set by the `JAVA_OPTS` environment variable. For example, to change the IP stack preference from its default of IPv4 to IPv6, set an environment variable as follows: +These system properties are conveniently set by the `JAVA_OPTS_APPEND` environment variable. +For example, to change the IP stack preference to IPv4, set an environment variable as follows: [source, bash] ---- -export JAVA_OPTS="-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Dfile.encoding=UTF-8 -Djava.net.preferIPv4Stack=false -Djava.net.preferIPv6Addresses=true" +export JAVA_OPTS_APPEND="-Djava.net.preferIPv4Stack=true" ---- -Note that when setting the `JAVA_OPTS` you are replacing the default JVM settings. Make sure to adjust them accordingly to your need. - \ No newline at end of file diff --git a/quarkus/dist/src/main/content/bin/kc.bat b/quarkus/dist/src/main/content/bin/kc.bat index 7fa91eb2030..d455aaf4c6d 100644 --- a/quarkus/dist/src/main/content/bin/kc.bat +++ b/quarkus/dist/src/main/content/bin/kc.bat @@ -78,7 +78,7 @@ if not "x%JAVA_OPTS%" == "x" ( rem If the memory is not used, it will be freed. See https://developers.redhat.com/blog/2017/04/04/openjdk-and-containers for details. rem To optimize for large heap sizes or for throughput and better response time due to shorter GC pauses, consider ZGC and Shenandoah GC. rem Both ZGC and Shenandoah GC seem to be more eager to claim the maximum heap size. Tests showed that ZGC might need additional tuning as it is not as aggressive as ParallelGC in reclaiming dead objects. - set "JAVA_OPTS=-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.err.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -XX:+UseParallelGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90" + set "JAVA_OPTS=-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.err.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -XX:+UseParallelGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90" ) @REM See also https://github.com/wildfly/wildfly-core/blob/7e5624cf92ebe4b64a4793a8c0b2a340c0d6d363/core-feature-pack/common/src/main/resources/content/bin/common.sh#L57-L60 diff --git a/quarkus/dist/src/main/content/bin/kc.sh b/quarkus/dist/src/main/content/bin/kc.sh index 408b1f8c4ad..76f3abfddfe 100644 --- a/quarkus/dist/src/main/content/bin/kc.sh +++ b/quarkus/dist/src/main/content/bin/kc.sh @@ -89,7 +89,7 @@ if [ "x$JAVA_OPTS" = "x" ]; then # If the memory is not used, it will be freed. See https://developers.redhat.com/blog/2017/04/04/openjdk-and-containers for details. # To optimize for large heap sizes or for throughput and better response time due to shorter GC pauses, consider ZGC and Shenandoah GC. # Both ZGC and Shenandoah GC seem to be more eager to claim the maximum heap size. Tests showed that ZGC might need additional tuning as as it is not as aggressive as ParallelGC in reclaiming dead objects. - JAVA_OPTS="-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.err.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -XX:+UseParallelGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90" + JAVA_OPTS="-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.err.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -XX:+ExitOnOutOfMemoryError -Djava.security.egd=file:/dev/urandom -XX:+UseParallelGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90" else echo "JAVA_OPTS already set in environment; overriding default settings with values: $JAVA_OPTS" fi diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/IpStackDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/IpStackDistTest.java new file mode 100644 index 00000000000..1edfe6543dc --- /dev/null +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/IpStackDistTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2023 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 io.quarkus.test.junit.main.Launch; +import org.junit.jupiter.api.Test; +import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.RawDistOnly; + +import java.io.IOException; +import java.net.ConnectException; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@DistributionTest(keepAlive = true, defaultOptions = {"--http-enabled=true", "--hostname-strict=false"}) +@RawDistOnly(reason = "Containers are immutable") +public class IpStackDistTest { + + @Test + @Launch({"start"}) + public void dualStackEnabled() { + assertThat(given().when().get("http://localhost:8080").getStatusCode(), is(200)); + assertThat(given().when().get("http://127.0.0.1:8080").getStatusCode(), is(200)); + assertThat(given().when().get("http://[::1]:8080").getStatusCode(), is(200)); + } + + @Test + @Launch({"start", "-Djava.net.preferIPv4Stack=true"}) + public void ipv4Preferred() throws IOException { + assertThat(given().when().get("http://localhost:8080").getStatusCode(), is(200)); + assertThat(given().when().get("http://127.0.0.1:8080").getStatusCode(), is(200)); + + assertThrows(ConnectException.class, () -> given().when().get("http://[::1]:8080"), "Connection refused must be thrown"); + } + + @Test + @Launch({"start", "-Djava.net.preferIPv6Addresses=true"}) + public void ipv6Prefered() { + assertThat(given().when().get("http://localhost:8080").getStatusCode(), is(200)); + assertThat(given().when().get("http://127.0.0.1:8080").getStatusCode(), is(200)); + assertThat(given().when().get("http://[::1]:8080").getStatusCode(), is(200)); + } +} diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/JavaOptsScriptTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/JavaOptsScriptTest.java index 0770b61e492..15c20aa6be9 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/JavaOptsScriptTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/JavaOptsScriptTest.java @@ -34,7 +34,7 @@ import static org.hamcrest.Matchers.matchesPattern; @WithEnvVars({"PRINT_ENV", "true"}) public class JavaOptsScriptTest { - private static final String DEFAULT_OPTS = "(?:-\\S+ )*-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8(?: -\\S+)*"; + private static final String DEFAULT_OPTS = "(?:-\\S+ )*-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Dfile.encoding=UTF-8(?: -\\S+)*"; @Test @Launch({ "start-dev" })