mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
fix: adding the ability to set the ingress tlsSecret (#41426)
* fix: adding the ability to set the ingress tlsSecret closes: #34777 Signed-off-by: Steve Hawkins <shawkins@redhat.com> * Apply suggestions from code review Co-authored-by: Martin Bartoš <mabartos@redhat.com> 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
db01ff742b
commit
f5f93ef6e1
@ -21,6 +21,10 @@ This helps you to track down a warning or error message in the log to a specific
|
||||
|
||||
For more details on this opt-in feature, see the https://www.keycloak.org/server/logging[Logging guide].
|
||||
|
||||
= Ability to specify a `tlsSecret` on the Keycloak CR `ingress` spec
|
||||
|
||||
In order to support basic TLS termination (edge) deployments via the operator, you may now set the Keycloak CR `spec.ingress.tlsSecret` field to a TLS Secret name in the namespace.
|
||||
|
||||
= HTTP Access logging
|
||||
|
||||
{project_name} supports HTTP access logging to record details of incoming HTTP requests.
|
||||
|
||||
@ -103,7 +103,7 @@ NOTE: The name format of options defined in this way is identical to the key fo
|
||||
|
||||
=== Secret References
|
||||
|
||||
Secret References are used by some dedicated options in the Keycloak CR, such as `tlsSecret`, or as a value in `additionalOptions`.
|
||||
Secret References are used by some dedicated options in the Keycloak CR, such as a `tlsSecret`, or as a value in `additionalOptions`.
|
||||
|
||||
Similarly ConfigMap References are used by options such as the `configMapFile`.
|
||||
|
||||
@ -290,7 +290,7 @@ spec:
|
||||
----
|
||||
|
||||
NOTE: If you are using a custom image, the Operator is *unaware* of any configuration options that might've been specified there.
|
||||
For instance, it may cause that the management interface uses the `https` schema, but the Operator accesses it via `http` when the TLS settings is specified in the custom image.
|
||||
For instance, the management interface may use `https`, but the Operator accesses it via `http` when the TLS settings are specified in the custom image.
|
||||
To ensure proper TLS configuration, use the `tlsSecret` and `truststores` fields in the Keycloak CR so that the Operator can reflect that.
|
||||
|
||||
For more details, see <@links.server id="management-interface" />.
|
||||
|
||||
@ -211,6 +211,50 @@ spec:
|
||||
className: openshift-default
|
||||
----
|
||||
|
||||
NOTE: The operator annotates the Ingress to match expectations for TLS passthrough or TLS termination on OpenShift with the default IngressClass.
|
||||
See below for more on TLS termination.
|
||||
|
||||
==== Proxy modes with the basic Ingress
|
||||
|
||||
The operator annotates the Ingress to match expectations for TLS termination or passthrough on OpenShift with the default IngressClass.
|
||||
For this reason TLS reencryption is not yet considered supported by basic Ingress, but you may be able to specify the `tlsSecret` on both the `http` and `ingress` specs as a starting point.
|
||||
You should double check the requirements of your IngressClass and platform to see if additional Ingress or Service annotations are needed in your desired scenario.
|
||||
|
||||
TLS passthrough is shown in the preceding `example-kc` example. It is enabled when you associate a `tlsSecret` with the `http` configuration and leave Ingress enabled without specifying a `tlsSecret` on it.
|
||||
|
||||
TLS termination, or edge mode, is enabled by associating a `tlsSecret` with the `ingress` spec and by enabling HTTP access.
|
||||
|
||||
Example TLS Termination YAML:
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
apiVersion: k8s.keycloak.org/v2alpha1
|
||||
kind: Keycloak
|
||||
metadata:
|
||||
name: example-kc
|
||||
spec:
|
||||
instances: 1
|
||||
db:
|
||||
vendor: postgres
|
||||
host: postgres-db
|
||||
usernameSecret:
|
||||
name: keycloak-db-secret
|
||||
key: username
|
||||
passwordSecret:
|
||||
name: keycloak-db-secret
|
||||
key: password
|
||||
http:
|
||||
httpEnabled: true
|
||||
ingress:
|
||||
tlsSecret: example-tls-secret
|
||||
hostname:
|
||||
hostname: test.keycloak.org
|
||||
proxy:
|
||||
headers: xforwarded # double check your reverse proxy sets and overwrites the X-Forwarded-* headers
|
||||
----
|
||||
|
||||
==== Custom Access
|
||||
|
||||
If the default ingress does not fit your use case, disable it by setting `ingress` spec with `enabled` property to `false` value:
|
||||
|
||||
Edit YAML file `example-kc.yaml`:
|
||||
|
||||
@ -18,6 +18,7 @@ package org.keycloak.operator.controllers;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
|
||||
import io.fabric8.kubernetes.api.model.networking.v1.IngressBuilder;
|
||||
import io.fabric8.kubernetes.api.model.networking.v1.IngressTLSBuilder;
|
||||
import io.fabric8.kubernetes.client.KubernetesClientException;
|
||||
import io.javaoperatorsdk.operator.api.config.informer.Informer;
|
||||
import io.javaoperatorsdk.operator.api.reconciler.Context;
|
||||
@ -146,8 +147,9 @@ public class KeycloakIngressDependentResource extends CRUDKubernetesDependentRes
|
||||
.build();
|
||||
|
||||
final var hostnameSpec = keycloak.getSpec().getHostnameSpec();
|
||||
String hostname = null;
|
||||
if (hostnameSpec != null && hostnameSpec.getHostname() != null) {
|
||||
String hostname = hostnameSpec.getHostname();
|
||||
hostname = hostnameSpec.getHostname();
|
||||
|
||||
try {
|
||||
hostname = new URL(hostname).getHost();
|
||||
@ -160,6 +162,13 @@ public class KeycloakIngressDependentResource extends CRUDKubernetesDependentRes
|
||||
ingress.getSpec().getRules().get(0).setHost(hostname);
|
||||
}
|
||||
|
||||
if (hostname != null) {
|
||||
String[] hosts = new String[] {hostname};
|
||||
optionalSpec.map(IngressSpec::getTlsSecret).ifPresent(tlsSecret ->
|
||||
ingress.getSpec().getTls().add(new IngressTLSBuilder().addToHosts(hosts).withSecretName(tlsSecret).build())
|
||||
);
|
||||
}
|
||||
|
||||
return ingress;
|
||||
}
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ public class IngressSpec {
|
||||
|
||||
@JsonProperty("enabled")
|
||||
private boolean ingressEnabled = true;
|
||||
|
||||
|
||||
@JsonProperty("className")
|
||||
private String ingressClassName;
|
||||
|
||||
@ -38,6 +38,9 @@ public class IngressSpec {
|
||||
@JsonPropertyDescription("Additional annotations to be appended to the Ingress object")
|
||||
Map<String, String> annotations;
|
||||
|
||||
@JsonPropertyDescription("A secret containing the TLS configuration for re-encrypt or TLS termination scenarios. Reference: https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets.")
|
||||
private String tlsSecret;
|
||||
|
||||
public boolean isIngressEnabled() {
|
||||
return ingressEnabled;
|
||||
}
|
||||
@ -45,11 +48,11 @@ public class IngressSpec {
|
||||
public void setIngressEnabled(boolean enabled) {
|
||||
this.ingressEnabled = enabled;
|
||||
}
|
||||
|
||||
|
||||
public String getIngressClassName() {
|
||||
return ingressClassName;
|
||||
}
|
||||
|
||||
|
||||
public void setIngressClassName(String className) {
|
||||
this.ingressClassName = className;
|
||||
}
|
||||
@ -61,4 +64,12 @@ public class IngressSpec {
|
||||
public void setAnnotations(Map<String, String> annotations) {
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
public String getTlsSecret() {
|
||||
return tlsSecret;
|
||||
}
|
||||
|
||||
public void setTlsSecret(String tlsSecret) {
|
||||
this.tlsSecret = tlsSecret;
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,6 +109,34 @@ public class KeycloakIngressTest extends BaseOperatorTest {
|
||||
.anyMatch(e -> "KC_PROXY_HEADERS".equals(e.getName()) && "xforwarded".equals(e.getValue()));
|
||||
}
|
||||
|
||||
@DisabledIfApiServerTest
|
||||
@Test
|
||||
public void testIngressTLSTermination() {
|
||||
var kc = getTestKeycloakDeployment(false);
|
||||
var hostnameSpecBuilder = new HostnameSpecBuilder()
|
||||
.withStrict(false)
|
||||
.withStrictBackchannel(false);
|
||||
if (isOpenShift) {
|
||||
kc.getSpec().setIngressSpec(new IngressSpecBuilder().withIngressClassName(KeycloakController.OPENSHIFT_DEFAULT).build());
|
||||
}
|
||||
kc.getSpec().setHostnameSpec(hostnameSpecBuilder.build());
|
||||
String secret = kc.getSpec().getHttpSpec().getTlsSecret();
|
||||
kc.getSpec().getHttpSpec().setHttpEnabled(true);
|
||||
kc.getSpec().getHttpSpec().setTlsSecret(null);
|
||||
kc.getSpec().setIngressSpec(new IngressSpecBuilder().withTlsSecret(secret).build());
|
||||
|
||||
K8sUtils.deployKeycloak(k8sclient, kc, true);
|
||||
|
||||
String testHostname;
|
||||
if (isOpenShift) {
|
||||
testHostname = k8sclient.resource(kc).get().getSpec().getHostnameSpec().getHostname();
|
||||
} else {
|
||||
testHostname = kubernetesIp;
|
||||
}
|
||||
|
||||
testIngressURLs("https://" + testHostname + ":443");
|
||||
}
|
||||
|
||||
private void testIngressURLs(String baseUrl) {
|
||||
Awaitility.await()
|
||||
.ignoreExceptions()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user