Skip to content

Commit

Permalink
Implement tls support for the app
Browse files Browse the repository at this point in the history
  • Loading branch information
carlesarnal committed Mar 3, 2025
1 parent e8d71ac commit 2f29977
Show file tree
Hide file tree
Showing 16 changed files with 454 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ public class Constants {
.withInitialDelaySeconds(15).withTimeoutSeconds(5).withPeriodSeconds(10).withSuccessThreshold(1)
.withFailureThreshold(3).build();

public static final Probe TLS_DEFAULT_READINESS_PROBE = new ProbeBuilder().withNewHttpGet()
.withScheme("HTTPS").withPath("/health/ready").withNewPort().withValue(8443).endPort().endHttpGet()
.withInitialDelaySeconds(15).withTimeoutSeconds(5).withPeriodSeconds(10).withSuccessThreshold(1)
.withFailureThreshold(3).build();

public static final Probe TLS_DEFAULT_LIVENESS_PROBE = new ProbeBuilder().withNewHttpGet()
.withScheme("HTTPS").withPath("/health/live").withNewPort().withValue(8443).endPort().endHttpGet()
.withInitialDelaySeconds(15).withTimeoutSeconds(5).withPeriodSeconds(10).withSuccessThreshold(1)
.withFailureThreshold(3).build();

public static final Map<String, String> BASIC_LABELS = Map.of(MANAGED_BY_LABEL, MANAGED_BY_VALUE,
LABEL_SELECTOR_KEY, LABEL_SELECTOR_VALUE);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ public class EnvironmentVariables {
public static final String QUARKUS_HTTP_ACCESS_LOG_ENABLED = "QUARKUS_HTTP_ACCESS_LOG_ENABLED";
public static final String QUARKUS_HTTP_CORS_ORIGINS = "QUARKUS_HTTP_CORS_ORIGINS";

public static final String QUARKUS_HTTP_INSECURE_REQUESTS = "QUARKUS_HTTP_INSECURE_REQUESTS";
public static final String QUARKUS_TLS_KEY_STORE_P12_PATH = "QUARKUS_TLS_KEY_STORE_P12_PATH";
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 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";

public static final String APICURIO_REST_MUTABILITY_ARTIFACT_VERSION_CONTENT_ENABLED = "APICURIO_REST_MUTABILITY_ARTIFACT-VERSION-CONTENT_ENABLED";

private static final String KAFKA_PREFIX = "APICURIO_KAFKA_COMMON_";
Expand All @@ -29,8 +33,6 @@ 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";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package io.apicurio.registry.operator.feat;

import io.apicurio.registry.operator.api.v1.ApicurioRegistry3;
import io.apicurio.registry.operator.api.v1.ApicurioRegistry3Spec;
import io.apicurio.registry.operator.api.v1.spec.AppSpec;
import io.apicurio.registry.operator.api.v1.spec.TLSSpec;
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.resource.app.AppDeploymentResource.addEnvVar;
import static java.util.Optional.ofNullable;

public class TLS {

public static void configureTLS(ApicurioRegistry3 primary, Deployment deployment,
String containerName, Map<String, EnvVar> env) {

addEnvVar(env, QUARKUS_HTTP_INSECURE_REQUESTS, Optional.ofNullable(primary.getSpec())
.map(ApicurioRegistry3Spec::getApp)
.map(AppSpec::getTls)
.map(TLSSpec::getInsecureRequests)
.orElse("enabled"));

var keystore = new SecretKeyRefTool(getTlsSpec(primary)
.map(TLSSpec::getKeystoreSecretRef)
.orElse(null), "user.p12");

var keystorePassword = new SecretKeyRefTool(getTlsSpec(primary)
.map(TLSSpec::getKeystorePasswordSecretRef)
.orElse(null), "user.password");

var truststore = new SecretKeyRefTool(getTlsSpec(primary)
.map(TLSSpec::getTruststoreSecretRef)
.orElse(null), "ca.p12");

var truststorePassword = new SecretKeyRefTool(getTlsSpec(primary)
.map(TLSSpec::getTruststorePasswordSecretRef)
.orElse(null), "ca.password");

if (truststore.isValid() && truststorePassword.isValid()) {
// ===== Truststore
truststore.applySecretVolume(deployment, containerName);
addEnvVar(env, QUARKUS_TLS_TRUST_STORE_P12_PATH, truststore.getSecretVolumeKeyPath());
truststorePassword.applySecretEnvVar(env, QUARKUS_TLS_TRUST_STORE_P12_PASSWORD);
}

if (keystore.isValid()
&& keystorePassword.isValid()) {
// ===== Keystore
keystore.applySecretVolume(deployment, containerName);
addEnvVar(env, QUARKUS_TLS_KEY_STORE_P12_PATH, keystore.getSecretVolumeKeyPath());
keystorePassword.applySecretEnvVar(env, QUARKUS_TLS_KEY_STORE_P12_PASSWORD);
}
}

private static Optional<TLSSpec> getTlsSpec(ApicurioRegistry3 primary) {
return ofNullable(primary)
.map(ApicurioRegistry3::getSpec)
.map(ApicurioRegistry3Spec::getApp)
.map(AppSpec::getTls);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
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;
Expand All @@ -20,11 +19,10 @@ 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, Deployment deployment, Map<String, EnvVar> env) {
public static void configureAuth(AuthSpec authSpec, Map<String, EnvVar> env) {
if (authSpec == null) {
return;
}
Expand Down Expand Up @@ -53,7 +51,9 @@ public static void configureAuth(AuthSpec authSpec, Deployment deployment, Map<S
authSpec.getBasicAuth().getCacheExpiration());
}

AuthTLS.configureAuthTLS(authSpec, deployment, env);
putIfNotBlank(env, EnvironmentVariables.OIDC_TLS_VERIFICATION,
authSpec.getTlsVerificationType());

Authz.configureAuthz(authSpec.getAuthz(), env);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.apicurio.registry.operator.api.v1.ApicurioRegistry3Spec;
import io.apicurio.registry.operator.api.v1.spec.AppSpec;
import io.apicurio.registry.operator.api.v1.spec.StudioUiSpec;
import io.apicurio.registry.operator.api.v1.spec.TLSSpec;
import io.apicurio.registry.operator.api.v1.spec.UiSpec;
import io.apicurio.registry.operator.status.ValidationErrorConditionManager;
import io.apicurio.registry.operator.status.StatusManager;
Expand All @@ -23,7 +24,7 @@
import java.util.Map;
import java.util.Optional;

import static io.apicurio.registry.operator.Constants.DEFAULT_REPLICAS;
import static io.apicurio.registry.operator.Constants.*;
import static io.apicurio.registry.operator.api.v1.ContainerNames.*;
import static io.apicurio.registry.operator.resource.Labels.getSelectorLabels;
import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.getContainerFromPodTemplateSpec;
Expand Down Expand Up @@ -56,6 +57,19 @@ public Deployment getDefaultAppDeployment(ApicurioRegistry3 primary) {
.map(AppSpec::getReplicas).orElse(DEFAULT_REPLICAS),
ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getApp)
.map(AppSpec::getPodTemplateSpec).orElse(null)); // TODO:

var readinessProbe = DEFAULT_READINESS_PROBE;
var livenessProbe = DEFAULT_LIVENESS_PROBE;

Optional<TLSSpec> tlsSpec = ofNullable(primary.getSpec())
.map(ApicurioRegistry3Spec::getApp)
.map(AppSpec::getTls);

if (tlsSpec.isPresent()) {
readinessProbe = TLS_DEFAULT_READINESS_PROBE;
livenessProbe = TLS_DEFAULT_LIVENESS_PROBE;
}

// Replicas
mergeDeploymentPodTemplateSpec(
COMPONENT_APP_SPEC_FIELD_NAME,
Expand All @@ -64,11 +78,12 @@ public Deployment getDefaultAppDeployment(ApicurioRegistry3 primary) {
REGISTRY_APP_CONTAINER_NAME,
Configuration.getAppImage(),
List.of(new ContainerPortBuilder().withName("http").withProtocol("TCP").withContainerPort(8080).build()),
new ProbeBuilder().withHttpGet(new HTTPGetActionBuilder().withPath("/health/ready").withPort(new IntOrString(8080)).withScheme("HTTP").build()).build(),
new ProbeBuilder().withHttpGet(new HTTPGetActionBuilder().withPath("/health/live").withPort(new IntOrString(8080)).withScheme("HTTP").build()).build(),
readinessProbe,
livenessProbe,
Map.of("cpu", new Quantity("500m"), "memory", new Quantity("512Mi")),
Map.of("cpu", new Quantity("1"), "memory", new Quantity("1Gi"))
);

addDefaultLabels(r.getMetadata().getLabels(), primary, COMPONENT_APP);
addSelectorLabels(r.getSpec().getSelector().getMatchLabels(), primary, COMPONENT_APP);
addDefaultLabels(r.getSpec().getTemplate().getMetadata().getLabels(), primary, COMPONENT_APP);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.apicurio.registry.operator.feat.Cors;
import io.apicurio.registry.operator.feat.KafkaSql;
import io.apicurio.registry.operator.feat.PostgresSql;
import io.apicurio.registry.operator.feat.TLS;
import io.apicurio.registry.operator.feat.security.Auth;
import io.apicurio.registry.operator.status.ReadyConditionManager;
import io.apicurio.registry.operator.status.StatusManager;
Expand Down Expand Up @@ -81,12 +82,15 @@ protected Deployment desired(ApicurioRegistry3 primary, Context<ApicurioRegistry
if (authEnabled) {
Auth.configureAuth(requireNonNull(ofNullable(primary.getSpec().getApp())
.map(AppSpec::getAuth)
.orElse(null)), deployment, envVars);
.orElse(null)), envVars);
}

// Configure the CORS_ALLOWED_ORIGINS env var based on the ingress host
Cors.configureAllowedOrigins(primary, envVars);

// Configure the TLS env vars
TLS.configureTLS(primary, deployment, REGISTRY_APP_CONTAINER_NAME, envVars);

// Enable the "mutability" feature in Registry, but only if Studio is deployed. It is based on Service
// in case a custom Ingress is used.
var sOpt = context.getSecondaryResource(STUDIO_UI_SERVICE_KEY.getKlass(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
package io.apicurio.registry.operator.resource.app;

import io.apicurio.registry.operator.api.v1.ApicurioRegistry3;
import io.apicurio.registry.operator.api.v1.ApicurioRegistry3Spec;
import io.apicurio.registry.operator.api.v1.spec.AppSpec;
import io.apicurio.registry.operator.resource.LabelDiscriminators.AppNetworkPolicyDiscriminator;
import io.fabric8.kubernetes.api.model.IntOrStringBuilder;
import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy;
import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicyIngressRuleBuilder;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Optional;

import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_APP;
import static io.apicurio.registry.operator.resource.ResourceKey.APP_NETWORK_POLICY_KEY;
import static io.apicurio.registry.operator.utils.Mapper.toYAML;
Expand All @@ -31,6 +38,30 @@ public AppNetworkPolicyResource() {
@Override
protected NetworkPolicy desired(ApicurioRegistry3 primary, Context<ApicurioRegistry3> context) {
var networkPolicy = APP_NETWORK_POLICY_KEY.getFactory().apply(primary);

Optional.ofNullable(primary.getSpec())
.map(ApicurioRegistry3Spec::getApp)
.map(AppSpec::getTls)
.ifPresent(tls -> {

var httpsPolicy = new io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicyPortBuilder()
.withPort(new IntOrStringBuilder().withValue(8443).build()).build();

var httpPolicy = new io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicyPortBuilder()
.withPort(new IntOrStringBuilder().withValue(8080).build()).build();

if (!tls.getInsecureRequests().equals("enabled")) {
networkPolicy.getSpec().setIngress(List.of(new NetworkPolicyIngressRuleBuilder()
.withPorts(httpsPolicy)
.build()));
}
else {
networkPolicy.getSpec().setIngress(List.of(new NetworkPolicyIngressRuleBuilder()
.withPorts(httpsPolicy, httpPolicy)
.build()));
}
});

log.trace("Desired {} is {}", APP_NETWORK_POLICY_KEY.getId(), toYAML(networkPolicy));
return networkPolicy;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
package io.apicurio.registry.operator.resource.app;

import io.apicurio.registry.operator.api.v1.ApicurioRegistry3;
import io.apicurio.registry.operator.api.v1.ApicurioRegistry3Spec;
import io.apicurio.registry.operator.api.v1.spec.AppSpec;
import io.fabric8.kubernetes.api.model.IntOrStringBuilder;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServicePortBuilder;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Optional;

import static io.apicurio.registry.operator.resource.LabelDiscriminators.AppServiceDiscriminator;
import static io.apicurio.registry.operator.resource.ResourceKey.APP_SERVICE_KEY;
import static io.apicurio.registry.operator.utils.Mapper.toYAML;
Expand All @@ -24,6 +31,31 @@ public AppServiceResource() {
@Override
protected Service desired(ApicurioRegistry3 primary, Context<ApicurioRegistry3> context) {
var s = APP_SERVICE_KEY.getFactory().apply(primary);

Optional.ofNullable(primary.getSpec())
.map(ApicurioRegistry3Spec::getApp)
.map(AppSpec::getTls)
.ifPresent(tls -> {
var httpPort = new ServicePortBuilder()
.withName("http")
.withPort(8080)
.withTargetPort(new IntOrStringBuilder().withValue(8080).build())
.build();

var httpsPort = new ServicePortBuilder()
.withName("https")
.withPort(443)
.withTargetPort(new IntOrStringBuilder().withValue(8443).build())
.build();

if (tls.getInsecureRequests().equals("enabled")) {
s.getSpec().setPorts(List.of(httpsPort, httpPort));
}
else {
s.getSpec().setPorts(List.of(httpsPort));
}
});

log.trace("Desired {} is {}", APP_SERVICE_KEY.getId(), toYAML(s));
return s;
}
Expand Down
Loading

0 comments on commit 2f29977

Please sign in to comment.