diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/EnvironmentVariables.java b/operator/controller/src/main/java/io/apicurio/registry/operator/EnvironmentVariables.java index b98c620a42..354c029a1d 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/EnvironmentVariables.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/EnvironmentVariables.java @@ -11,6 +11,8 @@ public class EnvironmentVariables { public static final String QUARKUS_TLS_KEY_STORE_P12_PASSWORD = "QUARKUS_TLS_KEY_STORE_P12_PASSWORD"; public static final String QUARKUS_TLS_TRUST_STORE_P12_PATH = "QUARKUS_TLS_TRUST_STORE_P12_PATH"; public static final String QUARKUS_TLS_TRUST_STORE_P12_PASSWORD = "QUARKUS_TLS_TRUST_STORE_P12_PASSWORD"; + public static final String QUARKUS_OIDC_TLS_TLS_CONFIGURATION_NAME = "QUARKUS_OIDC_TLS_TLS_CONFIGURATION_NAME"; + public static final String APICURIO_REST_DELETION_ARTIFACT_VERSION_ENABLED = "APICURIO_REST_DELETION_ARTIFACT-VERSION_ENABLED"; public static final String APICURIO_REST_DELETION_ARTIFACT_ENABLED = "APICURIO_REST_DELETION_ARTIFACT_ENABLED"; public static final String APICURIO_REST_DELETION_GROUP_ENABLED = "APICURIO_REST_DELETION_GROUP_ENABLED"; @@ -33,6 +35,8 @@ public class EnvironmentVariables { public static final String APICURIO_UI_AUTH_OIDC_LOGOUT_URL = "APICURIO_UI_AUTH_OIDC_LOGOUT_URL"; public static final String APICURIO_REGISTRY_AUTH_SERVER_URL = "QUARKUS_OIDC_AUTH_SERVER_URL"; public static final String OIDC_TLS_VERIFICATION = "QUARKUS_OIDC_TLS_VERIFICATION"; + public static final String OIDC_TLS_TRUSTSTORE_LOCATION = "QUARKUS_OIDC_TLS_TRUST_STORE_FILE"; + public static final String OIDC_TLS_TRUSTSTORE_PASSWORD = "QUARKUS_OIDC_TLS_TRUST_STORE_PASSWORD"; public static final String APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_ENABLED = "APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_ENABLED"; public static final String APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_CACHE_EXPIRATION = "APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_CACHE_EXPIRATION"; diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Auth.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Auth.java index 473a4a2f27..c57a993a2d 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Auth.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/Auth.java @@ -3,6 +3,7 @@ import io.apicurio.registry.operator.EnvironmentVariables; import io.apicurio.registry.operator.api.v1.spec.auth.AuthSpec; import io.fabric8.kubernetes.api.model.EnvVar; +import io.fabric8.kubernetes.api.model.apps.Deployment; import java.util.Map; import java.util.Optional; @@ -19,10 +20,11 @@ public class Auth { * Configures authentication-related environment variables for the Apicurio Registry. * * @param env The map of environment variables to be configured. + * @param deployment The application deployment to configure TLS. * @param authSpec The authentication specification containing required auth settings. If null, no changes * will be made to envVars. */ - public static void configureAuth(AuthSpec authSpec, Map env) { + public static void configureAuth(AuthSpec authSpec, Deployment deployment, Map env) { if (authSpec == null) { return; } @@ -51,9 +53,7 @@ public static void configureAuth(AuthSpec authSpec, Map env) { authSpec.getBasicAuth().getCacheExpiration()); } - putIfNotBlank(env, EnvironmentVariables.OIDC_TLS_VERIFICATION, - authSpec.getTlsVerificationType()); - + AuthTLS.configureAuthTLS(authSpec, deployment, env); Authz.configureAuthz(authSpec.getAuthz(), env); } } diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AuthTLS.java b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AuthTLS.java new file mode 100644 index 0000000000..40f1911501 --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/feat/security/AuthTLS.java @@ -0,0 +1,51 @@ +package io.apicurio.registry.operator.feat.security; + +import io.apicurio.registry.operator.EnvironmentVariables; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthSpec; +import io.apicurio.registry.operator.api.v1.spec.auth.AuthTLSSpec; +import io.apicurio.registry.operator.utils.SecretKeyRefTool; +import io.fabric8.kubernetes.api.model.EnvVar; +import io.fabric8.kubernetes.api.model.apps.Deployment; + +import java.util.Map; +import java.util.Optional; + +import static io.apicurio.registry.operator.EnvironmentVariables.*; +import static io.apicurio.registry.operator.api.v1.ContainerNames.REGISTRY_APP_CONTAINER_NAME; +import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.addEnvVar; +import static io.apicurio.registry.operator.utils.Utils.putIfNotBlank; +import static java.util.Optional.ofNullable; + +public class AuthTLS { + + /** + * Configure TLS for OIDC authentication + */ + public static void configureAuthTLS(AuthSpec authSpec, Deployment deployment, Map env) { + + putIfNotBlank(env, EnvironmentVariables.OIDC_TLS_VERIFICATION, + authSpec.getTls().getTlsVerificationType()); + + // spotless:off + var truststore = new SecretKeyRefTool(getAuthTLSSpec(authSpec) + .map(AuthTLSSpec::getTruststoreSecretRef) + .orElse(null), "ca.p12"); + + var truststorePassword = new SecretKeyRefTool(getAuthTLSSpec(authSpec) + .map(AuthTLSSpec::getTruststorePasswordSecretRef) + .orElse(null), "ca.password"); + // spotless:on + if (truststore.isValid() && truststorePassword.isValid()) { + truststore.applySecretVolume(deployment, REGISTRY_APP_CONTAINER_NAME); + addEnvVar(env, OIDC_TLS_TRUSTSTORE_LOCATION, truststore.getSecretVolumeKeyPath()); + truststorePassword.applySecretEnvVar(env, OIDC_TLS_TRUSTSTORE_PASSWORD); + } + } + + private static Optional getAuthTLSSpec(AuthSpec primary) { + // spotless:off + return ofNullable(primary) + .map(AuthSpec::getTls); + // spotless:on + } +} diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceFactory.java b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceFactory.java index 4b53cbc1c1..359436b0c5 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceFactory.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceFactory.java @@ -58,8 +58,8 @@ public Deployment getDefaultAppDeployment(ApicurioRegistry3 primary) { ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getApp) .map(AppSpec::getPodTemplateSpec).orElse(null)); // TODO: - var readinessProbe = DEFAULT_READINESS_PROBE; - var livenessProbe = DEFAULT_LIVENESS_PROBE; + var readinessProbe = new ProbeBuilder().withHttpGet(new HTTPGetActionBuilder().withPath("/health/ready").withPort(new IntOrString(8080)).withScheme("HTTP").build()).build(); + var livenessProbe = new ProbeBuilder().withHttpGet(new HTTPGetActionBuilder().withPath("/health/live").withPort(new IntOrString(8080)).withScheme("HTTP").build()).build(); Optional tlsSpec = ofNullable(primary.getSpec()) .map(ApicurioRegistry3Spec::getApp) diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/app/AppDeploymentResource.java b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/app/AppDeploymentResource.java index eade75c5cd..645a57a77e 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/app/AppDeploymentResource.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/app/AppDeploymentResource.java @@ -82,7 +82,7 @@ protected Deployment desired(ApicurioRegistry3 primary, Context .withTargetPort(new IntOrStringBuilder().withValue(8443).build()) .build(); - if (tls.getInsecureRequests().equals("enabled")) { + if (tls.getInsecureRequests() != null && tls.getInsecureRequests().equals("enabled")) { s.getSpec().setPorts(List.of(httpsPort, httpPort)); } else { diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppSpec.java index 0691202da2..cafe58b910 100644 --- a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppSpec.java +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/AppSpec.java @@ -13,7 +13,7 @@ @JsonDeserialize(using = None.class) @JsonInclude(NON_NULL) -@JsonPropertyOrder({ "env", "ingress", "podTemplateSpec", "storage", "sql", "kafkasql", "features", "auth", "tls" }) +@JsonPropertyOrder({ "env", "ingress", "podTemplateSpec", "storage", "sql", "kafkasql", "features", "auth" }) @NoArgsConstructor @AllArgsConstructor(access = PRIVATE) @SuperBuilder(toBuilder = true) diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthSpec.java index fc4912c28d..5e16f6b07e 100644 --- a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthSpec.java +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthSpec.java @@ -22,7 +22,7 @@ @JsonDeserialize(using = JsonDeserializer.None.class) @JsonInclude(NON_NULL) @JsonPropertyOrder({ "enabled", "appClientId", "uiClientId", "redirectURI", "authServerUrl", "logoutURL", - "anonymousReads", "basicAuth", "tlsVerificationType", "authz" }) + "anonymousReads", "basicAuth", "tls", "authz" }) @NoArgsConstructor @AllArgsConstructor(access = PRIVATE) @SuperBuilder(toBuilder = true) @@ -83,14 +83,12 @@ public class AuthSpec { @JsonSetter(nulls = Nulls.SKIP) private BasicAuthSpec basicAuth; - /** - * Type of TLS verification for authentication. - */ - @JsonProperty("tlsVerificationType") + @JsonProperty("tls") @JsonPropertyDescription(""" - Verify the identity server certificate.""") + OIDC TLS configuration. + When custom certificates are used, this is the field to be used to configure the trustore""") @JsonSetter(nulls = Nulls.SKIP) - private String tlsVerificationType; + private AuthTLSSpec tls; @JsonProperty("authz") @JsonPropertyDescription(""" diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthTLSSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthTLSSpec.java new file mode 100644 index 0000000000..4a21d864c1 --- /dev/null +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/auth/AuthTLSSpec.java @@ -0,0 +1,55 @@ +package io.apicurio.registry.operator.api.v1.spec.auth; + +import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.JsonDeserializer.None; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.apicurio.registry.operator.api.v1.spec.SecretKeyRef; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import static lombok.AccessLevel.PRIVATE; + +@JsonDeserialize(using = None.class) +@JsonInclude(Include.NON_NULL) +@JsonPropertyOrder({ "tlsVerificationType", "truststoreSecretRef", "truststorePasswordSecretRef" }) +@NoArgsConstructor +@AllArgsConstructor(access = PRIVATE) +@SuperBuilder(toBuilder = true) +@Getter +@Setter +@EqualsAndHashCode +@ToString +public class AuthTLSSpec { + + /** + * Type of TLS verification. + */ + @JsonProperty("tlsVerificationType") + @JsonPropertyDescription(""" + Verify the identity server certificate.""") + @JsonSetter(nulls = Nulls.SKIP) + private String tlsVerificationType; + + /** + * Name of a Secret that contains the TLS truststore (in PKCS12 format). Key ca.p12 is + * assumed by default. + */ + @JsonProperty("truststoreSecretRef") + @JsonPropertyDescription(""" + Name of a Secret that contains the TLS truststore (in PKCS12 format). \ + Key `ca.p12` is assumed by default.""") + @JsonSetter(nulls = Nulls.SKIP) + private SecretKeyRef truststoreSecretRef; + + /** + * Name of a Secret that contains the TLS truststore password. Key ca.password is assumed by + * default. + */ + @JsonProperty("truststorePasswordSecretRef") + @JsonPropertyDescription(""" + Name of a Secret that contains the TLS truststore password. \ + Key `ca.password` is assumed by default.""") + @JsonSetter(nulls = Nulls.SKIP) + private SecretKeyRef truststorePasswordSecretRef; +}