Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement tls support for the app #6007

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a nit, but * is not much good practice form my pov (different projects, different standards) :)

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);
}
Comment on lines +45 to +50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if I will use just PEM cert as truststore? Like

kind: ApicurioRegistry3
apiVersion: registry.apicur.io/v1
metadata:
  name: schema-registry
spec:
  app:
    storage:
      type: "kafkasql"
      kafkasql:
        bootstrapServers: anubis-kafka-bootstrap.strimzi-kafka.svc:9093
        tls:
          truststoreSecretRef:
            name: anubis-cluster-ca-cert
            key: ca.crt

wouldn't it cause failure? I would more expect that keystore password will be required

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the operator level we're supporting P12 (since it's usually a better practice than just specifying cert by cert). You will still be able to use PEM files using Quarkus own env vars.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks.


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() != null && !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() != null && 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
Loading