diff --git a/docs/documentation/release_notes/topics/24_0_0.adoc b/docs/documentation/release_notes/topics/24_0_0.adoc index 37f7ca37fe..49fe997e8d 100644 --- a/docs/documentation/release_notes/topics/24_0_0.adoc +++ b/docs/documentation/release_notes/topics/24_0_0.adoc @@ -24,6 +24,10 @@ spec: Currently only Secrets are supported. +== Trust Kubernetes CA + +The cert for the Kubernetes CA is added automatically to your {project_name} Pods managed by the Operator. + = Automatic certificate management for SAML identity providers The SAML identity providers can now be configured to automatically download the signing certificates from the IDP entity metadata descriptor endpoint. In order to use the new feature the option `Metadata descriptor URL` should be configured in the provider (URL where the IDP metadata information with the certificates is published) and `Use metadata descriptor URL` needs to be `ON`. The certificates are automatically downloaded and cached in the `public-key-storage` SPI from that URL. The certificates can also be reloaded or imported from the admin console, using the action combo in the provider page. diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeploymentDependentResource.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeploymentDependentResource.java index 46bbdf22a1..181a3e9e3a 100644 --- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeploymentDependentResource.java +++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeploymentDependentResource.java @@ -67,6 +67,8 @@ import static org.keycloak.operator.crds.v2alpha1.CRDUtils.isTlsConfigured; @KubernetesDependent(labelSelector = Constants.DEFAULT_LABELS_AS_STRING) public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependentResource { + public static final String KC_TRUSTSTORE_PATHS = "KC_TRUSTSTORE_PATHS"; + static final String JGROUPS_DNS_QUERY_PARAM = "-Djgroups.dns.query="; public static final String OPTIMIZED_ARG = "--optimized"; @@ -323,9 +325,13 @@ public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependent var env = Optional.ofNullable(baseDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv()).orElse(List.of()); // accumulate the env vars in priority order - unsupported, first class, additional - var envVars = new ArrayList<>(Stream.concat(Stream.concat(env.stream(), firstClasssEnvVars.stream()), additionalEnvVars.stream()) - .collect(Collectors.toMap(EnvVar::getName, Function.identity(), (e1, e2) -> e1, LinkedHashMap::new)) - .values()); + LinkedHashMap varMap = Stream.concat(Stream.concat(env.stream(), firstClasssEnvVars.stream()), additionalEnvVars.stream()) + .collect(Collectors.toMap(EnvVar::getName, Function.identity(), (e1, e2) -> e1, LinkedHashMap::new)); + + // include the kube CA if the user is not controlling KC_TRUSTSTORE_PATHS via the unsupported or the additional + varMap.putIfAbsent(KC_TRUSTSTORE_PATHS, new EnvVarBuilder().withName(KC_TRUSTSTORE_PATHS).withValue("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt").build()); + + var envVars = new ArrayList<>(varMap.values()); baseDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars); // watch the secrets used by secret key - we don't currently expect configmaps, optional refs, or watch the initial-admin diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/unit/PodTemplateTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/unit/PodTemplateTest.java index cb4ae93f96..4dc2d26cf3 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/unit/PodTemplateTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/unit/PodTemplateTest.java @@ -363,7 +363,7 @@ public class PodTemplateTest { } @Test - public void testDefaultArgs() { + public void testDefaults() { // Arrange PodTemplateSpec additionalPodTemplate = null; @@ -372,6 +372,7 @@ public class PodTemplateTest { // Assert assertThat(podTemplate.getSpec().getContainers().get(0).getArgs()).doesNotContain(KeycloakDeploymentDependentResource.OPTIMIZED_ARG); + assertThat(podTemplate.getSpec().getContainers().get(0).getEnv().stream().anyMatch(envVar -> envVar.getName().equals(KeycloakDeploymentDependentResource.KC_TRUSTSTORE_PATHS))); } @Test @@ -388,6 +389,22 @@ public class PodTemplateTest { assertThat(podTemplate.getSpec().getContainers().get(0).getArgs()).doesNotContain(KeycloakDeploymentDependentResource.OPTIMIZED_ARG); } + @Test + public void testAdditionalOptionTruststorePath() { + // Arrange + PodTemplateSpec additionalPodTemplate = null; + + // Act + var podTemplate = getDeployment(additionalPodTemplate, null, + s -> s.addToAdditionalOptions(new ValueOrSecret(KeycloakDeploymentDependentResource.KC_TRUSTSTORE_PATHS, "/something"))) + .getSpec().getTemplate(); + + // Assert + assertThat(podTemplate.getSpec().getContainers().get(0).getEnv().stream() + .anyMatch(envVar -> envVar.getName().equals(KeycloakDeploymentDependentResource.KC_TRUSTSTORE_PATHS) + && envVar.getValue().equals("/something"))); + } + @Test public void testImageForceOptimized() { // Arrange