mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
enhance: allow for control over what port health checks are exposed on (#41759)
closes: #39506 Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
parent
bcdbde38dd
commit
565e195f48
@ -14,6 +14,11 @@ For more information, see the link:{adminguide_link}#_update-email-workflow[Upda
|
||||
|
||||
There's a new option `http-management-scheme` that may be set to `http` to force the management interface to use HTTP rather than inheriting the HTTPS settings of the main interface.
|
||||
|
||||
= Option to expose health endpoints on the main HTTP(S) ports
|
||||
|
||||
With `health-enabled` set to true, you may set the `http-management-health-enabled` to `false` to indicate that health endpoints should be exposed on the main HTTP(s) ports instead of the
|
||||
management port. When this option is `false` you should block unwanted external traffic to `/health` at your proxy.
|
||||
|
||||
= Additional context information for log messages (preview)
|
||||
|
||||
You can now add context information to each log message like the realm or the client that initiated the request.
|
||||
@ -32,6 +37,7 @@ While access logs are often used for debugging and traffic analysis, they are al
|
||||
|
||||
For more information, see the https://www.keycloak.org/server/logging[Logging guide].
|
||||
|
||||
|
||||
= Supported passkeys
|
||||
|
||||
*Passkeys* integration is now a supported feature. This feature integrates passkeys seamlessly in the {project_name} forms using both conditional and modal UI. Although supported, *passkeys* are disabled by default. To activate the integration in the realm, the option *Enable Passkeys* in the *WebAuthn Passwordless Policy* (*Authentication* → *Policies* → *Webauthn Passwordless Policy*) needs to be enabled.
|
||||
|
||||
@ -11,6 +11,9 @@ includedOptions="health-enabled">
|
||||
{project_name} has built in support for health checks. This {section} describes how to enable and use the {project_name} health checks.
|
||||
The {project_name} health checks are exposed on the management port `9000` by default. For more details, see <@links.server id="management-interface" />
|
||||
|
||||
When the `http-management-health-enabled` option is `false` the health endpoints will remain on the main HTTP(S) ports, rather than being exposed on the management port.
|
||||
When this option is `false` you should block unwanted external traffic to `/health` at your proxy.
|
||||
|
||||
== {project_name} health check endpoints
|
||||
|
||||
{project_name} exposes 4 health endpoints:
|
||||
|
||||
@ -48,6 +48,7 @@ import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakSpec;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.ValueOrSecret;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.CacheSpec;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.HttpManagementSpec;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.HttpSpec;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.ProbeSpec;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.SchedulingSpec;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.Truststore;
|
||||
@ -83,6 +84,9 @@ import static org.keycloak.operator.crds.v2alpha1.deployment.spec.TracingSpec.co
|
||||
)
|
||||
public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependentResource<StatefulSet, Keycloak> {
|
||||
|
||||
public static final String HTTP_MANAGEMENT_HEALTH_ENABLED = "http-management-health-enabled";
|
||||
public static final String HTTP_MANAGEMENT_SCHEME = "http-management-scheme";
|
||||
|
||||
public static final String POD_IP = "POD_IP";
|
||||
|
||||
private static final List<String> COPY_ENV = Arrays.asList("HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY");
|
||||
@ -326,9 +330,20 @@ public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependent
|
||||
// Set bind address as this is required for JGroups to form a cluster in IPv6 envionments
|
||||
containerBuilder.addToArgs(0, "-Djgroups.bind.address=$(%s)".formatted(POD_IP));
|
||||
|
||||
boolean tls = isTlsConfigured(keycloakCR);
|
||||
String protocol = tls ? "HTTPS" : "HTTP";
|
||||
int port = -1;
|
||||
|
||||
if (readConfigurationValue(HTTP_MANAGEMENT_HEALTH_ENABLED, keycloakCR, context).map(Boolean::valueOf).orElse(true)) {
|
||||
port = HttpManagementSpec.managementPort(keycloakCR);
|
||||
if (readConfigurationValue(HTTP_MANAGEMENT_SCHEME, keycloakCR, context).filter("http"::equals).isPresent()) {
|
||||
protocol = "HTTP";
|
||||
}
|
||||
} else {
|
||||
port = tls ? HttpSpec.httpsPort(keycloakCR) : HttpSpec.httpPort(keycloakCR);
|
||||
}
|
||||
|
||||
// probes
|
||||
var protocol = isManagementHttps(keycloakCR) ? "HTTPS" : "HTTP";
|
||||
var port = HttpManagementSpec.managementPort(keycloakCR);
|
||||
var readinessOptionalSpec = Optional.ofNullable(keycloakCR.getSpec().getReadinessProbeSpec());
|
||||
var livenessOptionalSpec = Optional.ofNullable(keycloakCR.getSpec().getLivenessProbeSpec());
|
||||
var startupOptionalSpec = Optional.ofNullable(keycloakCR.getSpec().getStartupProbeSpec());
|
||||
@ -391,11 +406,6 @@ public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependent
|
||||
.endContainer().endSpec().endTemplate().endSpec().build();
|
||||
}
|
||||
|
||||
private boolean isManagementHttps(Keycloak keycloakCR) {
|
||||
return isTlsConfigured(keycloakCR) && keycloakCR.getSpec().getAdditionalOptions().stream()
|
||||
.noneMatch(v -> "http-management-scheme".equals(v.getName()) && "http".equals(v.getValue()));
|
||||
}
|
||||
|
||||
private void handleScheduling(Keycloak keycloakCR, Map<String, String> labels, PodSpecFluent<?> specBuilder) {
|
||||
SchedulingSpec schedulingSpec = keycloakCR.getSpec().getSchedulingSpec();
|
||||
if (schedulingSpec != null) {
|
||||
|
||||
@ -392,7 +392,7 @@ public class PodTemplateTest {
|
||||
@Test
|
||||
public void testHttpManagment() {
|
||||
var result = getDeployment(null, new StatefulSet(),
|
||||
spec -> spec.withAdditionalOptions(new ValueOrSecret("http-management-scheme", "http")))
|
||||
spec -> spec.withAdditionalOptions(new ValueOrSecret(KeycloakDeploymentDependentResource.HTTP_MANAGEMENT_SCHEME, "http")))
|
||||
.getSpec()
|
||||
.getTemplate()
|
||||
.getSpec()
|
||||
@ -400,6 +400,21 @@ public class PodTemplateTest {
|
||||
.get(0);
|
||||
|
||||
assertEquals("HTTP", result.getReadinessProbe().getHttpGet().getScheme());
|
||||
assertEquals(9000, result.getReadinessProbe().getHttpGet().getPort().getIntVal());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHealthOnMain() {
|
||||
var result = getDeployment(null, new StatefulSet(),
|
||||
spec -> spec.withAdditionalOptions(new ValueOrSecret(KeycloakDeploymentDependentResource.HTTP_MANAGEMENT_HEALTH_ENABLED, "false")))
|
||||
.getSpec()
|
||||
.getTemplate()
|
||||
.getSpec()
|
||||
.getContainers()
|
||||
.get(0);
|
||||
|
||||
assertEquals("HTTPS", result.getReadinessProbe().getHttpGet().getScheme());
|
||||
assertEquals(8443, result.getReadinessProbe().getHttpGet().getPort().getIntVal());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -31,6 +31,13 @@ public class ManagementOptions {
|
||||
.hidden()
|
||||
.build();
|
||||
|
||||
public static final Option<Boolean> HTTP_MANAGEMENT_HEALTH_ENABLED = new OptionBuilder<>("http-management-health-enabled", Boolean.class)
|
||||
.category(OptionCategory.MANAGEMENT)
|
||||
.description("If health endpoints should be exposed on the management interface. If false, health endpoints will be exposed on the main interface.")
|
||||
.defaultValue(true)
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
public static final Option<Boolean> LEGACY_OBSERVABILITY_INTERFACE = new OptionBuilder<>("legacy-observability-interface", Boolean.class)
|
||||
.category(OptionCategory.MANAGEMENT)
|
||||
.deprecated()
|
||||
|
||||
@ -40,6 +40,10 @@ public class ManagementPropertyMappers {
|
||||
.to("quarkus.management.enabled")
|
||||
.transformer((val, ctx) -> managementEnabledTransformer())
|
||||
.build(),
|
||||
fromOption(ManagementOptions.HTTP_MANAGEMENT_HEALTH_ENABLED)
|
||||
.to("quarkus.smallrye-health.management.enabled")
|
||||
.isEnabled(() -> isTrue(HealthOptions.HEALTH_ENABLED), "health is enabled")
|
||||
.build(),
|
||||
fromOption(ManagementOptions.LEGACY_OBSERVABILITY_INTERFACE)
|
||||
.build(),
|
||||
fromOption(ManagementOptions.HTTP_MANAGEMENT_RELATIVE_PATH)
|
||||
@ -122,7 +126,8 @@ public class ManagementPropertyMappers {
|
||||
if (isTrue(LEGACY_OBSERVABILITY_INTERFACE)) {
|
||||
return false;
|
||||
}
|
||||
var isManagementOccupied = isTrue(HealthOptions.HEALTH_ENABLED) || isTrue(MetricsOptions.METRICS_ENABLED);
|
||||
var isManagementOccupied = isTrue(MetricsOptions.METRICS_ENABLED)
|
||||
|| (isTrue(HealthOptions.HEALTH_ENABLED) && isTrue(ManagementOptions.HTTP_MANAGEMENT_HEALTH_ENABLED));
|
||||
return isManagementOccupied;
|
||||
}
|
||||
|
||||
|
||||
@ -937,4 +937,11 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||
nonRunningPicocli = pseudoLaunch("start-dev", "--http-access-log-enabled=true", "--http-access-log-exclude='/realms/my-realm/.*");
|
||||
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void healthEnabledRequired() {
|
||||
var nonRunningPicocli = pseudoLaunch("start-dev", "--http-management-health-enabled=false");
|
||||
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
|
||||
assertThat(nonRunningPicocli.getErrString(), containsString("Available only when health is enabled"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ public class HealthDistTest {
|
||||
|
||||
@Test
|
||||
@Launch({ "start-dev", "--health-enabled=true" })
|
||||
void testHealthEndpoint() {
|
||||
void testHealthEndpoint(KeycloakDistribution distribution) {
|
||||
when().get("/health").then()
|
||||
.statusCode(200);
|
||||
when().get("/health/live").then()
|
||||
@ -62,6 +62,18 @@ public class HealthDistTest {
|
||||
.statusCode(404);
|
||||
when().get("/lb-check").then()
|
||||
.statusCode(404);
|
||||
|
||||
// still nothing on main
|
||||
distribution.setRequestPort(8080);
|
||||
when().get("/health/ready").then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({ "start-dev", "--health-enabled=true", "--http-management-health-enabled=false" })
|
||||
void testHealthEndpointOnMain(KeycloakDistribution distribution) {
|
||||
distribution.setRequestPort(8080);
|
||||
when().get("/health/ready").then().statusCode(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -153,6 +153,10 @@ HTTP Access log:
|
||||
|
||||
Management:
|
||||
|
||||
--http-management-health-enabled <true|false>
|
||||
If health endpoints should be exposed on the management interface. If false,
|
||||
health endpoints will be exposed on the main interface. Default: true.
|
||||
Available only when health is enabled.
|
||||
--http-management-port <port>
|
||||
Port of the management interface. Relevant only when something is exposed on
|
||||
the management interface - see the guide for details. Default: 9000.
|
||||
|
||||
@ -153,6 +153,10 @@ HTTP Access log:
|
||||
|
||||
Management:
|
||||
|
||||
--http-management-health-enabled <true|false>
|
||||
If health endpoints should be exposed on the management interface. If false,
|
||||
health endpoints will be exposed on the main interface. Default: true.
|
||||
Available only when health is enabled.
|
||||
--http-management-port <port>
|
||||
Port of the management interface. Relevant only when something is exposed on
|
||||
the management interface - see the guide for details. Default: 9000.
|
||||
|
||||
@ -366,6 +366,10 @@ Health:
|
||||
|
||||
Management:
|
||||
|
||||
--http-management-health-enabled <true|false>
|
||||
If health endpoints should be exposed on the management interface. If false,
|
||||
health endpoints will be exposed on the main interface. Default: true.
|
||||
Available only when health is enabled.
|
||||
--http-management-port <port>
|
||||
Port of the management interface. Relevant only when something is exposed on
|
||||
the management interface - see the guide for details. Default: 9000.
|
||||
|
||||
@ -367,6 +367,10 @@ Health:
|
||||
|
||||
Management:
|
||||
|
||||
--http-management-health-enabled <true|false>
|
||||
If health endpoints should be exposed on the management interface. If false,
|
||||
health endpoints will be exposed on the main interface. Default: true.
|
||||
Available only when health is enabled.
|
||||
--http-management-port <port>
|
||||
Port of the management interface. Relevant only when something is exposed on
|
||||
the management interface - see the guide for details. Default: 9000.
|
||||
|
||||
@ -366,6 +366,10 @@ Health:
|
||||
|
||||
Management:
|
||||
|
||||
--http-management-health-enabled <true|false>
|
||||
If health endpoints should be exposed on the management interface. If false,
|
||||
health endpoints will be exposed on the main interface. Default: true.
|
||||
Available only when health is enabled.
|
||||
--http-management-port <port>
|
||||
Port of the management interface. Relevant only when something is exposed on
|
||||
the management interface - see the guide for details. Default: 9000.
|
||||
|
||||
@ -364,6 +364,10 @@ Health:
|
||||
|
||||
Management:
|
||||
|
||||
--http-management-health-enabled <true|false>
|
||||
If health endpoints should be exposed on the management interface. If false,
|
||||
health endpoints will be exposed on the main interface. Default: true.
|
||||
Available only when health is enabled.
|
||||
--http-management-port <port>
|
||||
Port of the management interface. Relevant only when something is exposed on
|
||||
the management interface - see the guide for details. Default: 9000.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user