From cb3a0c0d558e794df86b6e12235abed393e244e3 Mon Sep 17 00:00:00 2001 From: Alec Merdler Date: Mon, 3 May 2021 11:49:48 -0700 Subject: [PATCH] tls: persist generated TLS cert/key pair (PROJQUAY-1838) Move the 'ssl.cert' and 'ssl.key' to a separate, persistent Secret to ensure that the cert/key pair is not re-generated on every reconcile. Use k8s projected volumes to mount the config and TLS Secrets to the same directory in the Quay container. Signed-off-by: Alec Merdler --- apis/quay/v1/quayregistry_types.go | 9 +- controllers/quay/features.go | 87 ++++++++++++++----- controllers/quay/quayregistry_controller.go | 19 ++-- .../quay/quayregistry_controller_test.go | 35 +++++++- kustomize/base/quay.deployment.yaml | 12 ++- kustomize/base/upgrade.deployment.yaml | 13 +-- .../components/clair/clair.deployment.yaml | 2 +- .../components/mirror/mirror.deployment.yaml | 13 ++- pkg/context/context.go | 4 + pkg/kustomize/kustomize.go | 20 ++++- pkg/kustomize/kustomize_test.go | 1 + pkg/kustomize/secrets.go | 18 ++-- pkg/kustomize/secrets_test.go | 4 +- 13 files changed, 180 insertions(+), 57 deletions(-) diff --git a/apis/quay/v1/quayregistry_types.go b/apis/quay/v1/quayregistry_types.go index ba2081df2..cb19cfa80 100644 --- a/apis/quay/v1/quayregistry_types.go +++ b/apis/quay/v1/quayregistry_types.go @@ -64,7 +64,10 @@ var requiredComponents = []ComponentKind{ ComponentRoute, } -const ManagedKeysName = "quay-registry-managed-secret-keys" +const ( + ManagedKeysName = "quay-registry-managed-secret-keys" + QuayConfigTLSSecretName = "quay-config-tls" +) // QuayRegistrySpec defines the desired state of QuayRegistry. type QuayRegistrySpec struct { @@ -387,6 +390,10 @@ func IsManagedKeysSecretFor(quay *QuayRegistry, secret *corev1.Secret) bool { return strings.Contains(secret.GetName(), quay.GetName()+"-"+ManagedKeysName) } +func IsManagedTLSSecretFor(quay *QuayRegistry, secret *corev1.Secret) bool { + return strings.Contains(secret.GetName(), quay.GetName()+"-"+QuayConfigTLSSecretName) +} + func init() { SchemeBuilder.Register(&QuayRegistry{}, &QuayRegistryList{}) } diff --git a/controllers/quay/features.go b/controllers/quay/features.go index 033b00054..7c86ec3a3 100644 --- a/controllers/quay/features.go +++ b/controllers/quay/features.go @@ -39,7 +39,11 @@ const ( GrafanaDashboardConfigNamespace = "openshift-config-managed" ) -func (r *QuayRegistryReconciler) checkManagedKeys(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, rawConfig []byte) (*quaycontext.QuayRegistryContext, *v1.QuayRegistry, error) { +// FeatureDetection is a method which should return an updated `QuayRegistryContext` after performing a feature detection task. +// TODO(alecmerdler): Refactor all "feature detection" functions to use a common function interface... +type FeatureDetection func(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, configBundle map[string][]byte) (*quaycontext.QuayRegistryContext, *v1.QuayRegistry, error) + +func (r *QuayRegistryReconciler) checkManagedKeys(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, configBundle map[string][]byte) (*quaycontext.QuayRegistryContext, *v1.QuayRegistry, error) { var secrets corev1.SecretList listOptions := &client.ListOptions{ Namespace: quay.GetNamespace(), @@ -64,7 +68,62 @@ func (r *QuayRegistryReconciler) checkManagedKeys(ctx *quaycontext.QuayRegistryC return ctx, quay, nil } -func (r *QuayRegistryReconciler) checkRoutesAvailable(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, rawConfig []byte) (*quaycontext.QuayRegistryContext, *v1.QuayRegistry, error) { +func (r *QuayRegistryReconciler) checkManagedTLS(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, configBundle map[string][]byte) (*quaycontext.QuayRegistryContext, *v1.QuayRegistry, error) { + providedTLSCert := configBundle["ssl.cert"] + providedTLSKey := configBundle["ssl.key"] + + if providedTLSCert != nil && providedTLSKey != nil { + r.Log.Info("provided TLS cert/key pair in `configBundleSecret` will be stored in persistent `Secret`") + ctx.TLSCert = providedTLSCert + ctx.TLSKey = providedTLSKey + + return ctx, quay, nil + } + + var secrets corev1.SecretList + listOptions := &client.ListOptions{ + Namespace: quay.GetNamespace(), + LabelSelector: labels.SelectorFromSet(map[string]string{ + kustomize.QuayRegistryNameLabel: quay.GetName(), + }), + } + + if err := r.List(context.Background(), &secrets, listOptions); err != nil { + return ctx, quay, err + } + + for _, secret := range secrets.Items { + if v1.IsManagedTLSSecretFor(quay, &secret) { + ctx.TLSCert = secret.Data["ssl.cert"] + ctx.TLSKey = secret.Data["ssl.key"] + break + } + } + + if ctx.TLSCert == nil || ctx.TLSKey == nil { + r.Log.Info("existing TLS cert/key pair not found, one will be generated") + } + + return ctx, quay, nil +} + +func (r *QuayRegistryReconciler) checkRoutesAvailable(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, configBundle map[string][]byte) (*quaycontext.QuayRegistryContext, *v1.QuayRegistry, error) { + // NOTE: The `route` component is unique because we allow users to set the `SERVER_HOSTNAME` field instead of controlling the entire fieldgroup. + // This value is then passed to the created `Route` using a Kustomize variable. + var config map[string]interface{} + if err := yaml.Unmarshal(configBundle["config.yaml"], &config); err != nil { + return ctx, quay, err + } + + fieldGroup, err := hostsettings.NewHostSettingsFieldGroup(config) + if err != nil { + return ctx, quay, err + } + + if fieldGroup.ServerHostname != "" { + ctx.ServerHostname = fieldGroup.ServerHostname + } + fakeRoute, err := v1.EnsureOwnerReference(quay, &routev1.Route{ ObjectMeta: metav1.ObjectMeta{ Name: quay.GetName() + "-test-route", @@ -101,21 +160,7 @@ func (r *QuayRegistryReconciler) checkRoutesAvailable(ctx *quaycontext.QuayRegis return ctx, quay, err } - // NOTE: The `route` component is unique because we allow users to set the `SERVER_HOSTNAME` field instead of controlling the entire fieldgroup. - // This value is then passed to the created `Route` using a Kustomize variable. - var config map[string]interface{} - if err := yaml.Unmarshal(rawConfig, &config); err != nil { - return ctx, quay, err - } - - fieldGroup, err := hostsettings.NewHostSettingsFieldGroup(config) - if err != nil { - return ctx, quay, err - } - - if fieldGroup.ServerHostname != "" { - ctx.ServerHostname = fieldGroup.ServerHostname - } else { + if ctx.ServerHostname == "" { ctx.ServerHostname = strings.Join([]string{ strings.Join([]string{quay.GetName(), "quay", quay.GetNamespace()}, "-"), ctx.ClusterHostname}, @@ -136,7 +181,7 @@ func (r *QuayRegistryReconciler) checkRoutesAvailable(ctx *quaycontext.QuayRegis return ctx, quay, nil } -func (r *QuayRegistryReconciler) checkObjectBucketClaimsAvailable(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, rawConfig []byte) (*quaycontext.QuayRegistryContext, *v1.QuayRegistry, error) { +func (r *QuayRegistryReconciler) checkObjectBucketClaimsAvailable(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, configBundle map[string][]byte) (*quaycontext.QuayRegistryContext, *v1.QuayRegistry, error) { datastoreName := types.NamespacedName{Namespace: quay.GetNamespace(), Name: quay.GetName() + "-quay-datastore"} var objectBucketClaims objectbucket.ObjectBucketClaimList if err := r.Client.List(context.Background(), &objectBucketClaims); err == nil { @@ -192,9 +237,9 @@ func (r *QuayRegistryReconciler) checkObjectBucketClaimsAvailable(ctx *quayconte } // TODO: Improve this once `builds` is a managed component. -func (r *QuayRegistryReconciler) checkBuildManagerAvailable(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, rawConfig []byte) (*quaycontext.QuayRegistryContext, *v1.QuayRegistry, error) { +func (r *QuayRegistryReconciler) checkBuildManagerAvailable(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, configBundle map[string][]byte) (*quaycontext.QuayRegistryContext, *v1.QuayRegistry, error) { var config map[string]interface{} - if err := yaml.Unmarshal(rawConfig, &config); err != nil { + if err := yaml.Unmarshal(configBundle["config.yaml"], &config); err != nil { return ctx, quay, err } @@ -208,7 +253,7 @@ func (r *QuayRegistryReconciler) checkBuildManagerAvailable(ctx *quaycontext.Qua // Validates if the monitoring component can be run. We assume that we are // running in an Openshift environment with cluster monitoring enabled for our // monitoring component to work -func (r *QuayRegistryReconciler) checkMonitoringAvailable(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, rawConfig []byte) (*quaycontext.QuayRegistryContext, *v1.QuayRegistry, error) { +func (r *QuayRegistryReconciler) checkMonitoringAvailable(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, configBundle map[string][]byte) (*quaycontext.QuayRegistryContext, *v1.QuayRegistry, error) { if len(r.WatchNamespace) > 0 { msg := "monitoring is only supported in AllNamespaces mode. Disabling component monitoring" r.Log.Info(msg) diff --git a/controllers/quay/quayregistry_controller.go b/controllers/quay/quayregistry_controller.go index 8460373ff..ca6b387e9 100644 --- a/controllers/quay/quayregistry_controller.go +++ b/controllers/quay/quayregistry_controller.go @@ -72,6 +72,8 @@ type QuayRegistryReconciler struct { Scheme *runtime.Scheme EventRecorder record.EventRecorder WatchNamespace string + + // TODO(alecmerdler): Somehow generalize feature detection functions so that they can match a type signature and be iterated over... } // +kubebuilder:rbac:groups=quay.redhat.com,resources=quayregistries,verbs=get;list;watch;create;update;patch;delete @@ -163,21 +165,28 @@ func (r *QuayRegistryReconciler) Reconcile(ctx context.Context, req ctrl.Request log.Info("successfully retrieved referenced `configBundleSecret`", "configBundleSecret", configBundle.GetName(), "resourceVersion", configBundle.GetResourceVersion()) - quayContext, updatedQuay, err := r.checkManagedKeys(quayContext, updatedQuay.DeepCopy(), configBundle.Data["config.yaml"]) + quayContext, updatedQuay, err := r.checkManagedKeys(quayContext, updatedQuay.DeepCopy(), configBundle.Data) if err != nil { msg := fmt.Sprintf("unable to retrieve managed keys `Secret`: %s", err) return r.reconcileWithCondition(&quay, v1.ConditionTypeRolloutBlocked, metav1.ConditionTrue, v1.ConditionReasonConfigInvalid, msg) } - quayContext, updatedQuay, err = r.checkRoutesAvailable(quayContext, updatedQuay.DeepCopy(), configBundle.Data["config.yaml"]) + quayContext, updatedQuay, err = r.checkManagedTLS(quayContext, updatedQuay.DeepCopy(), configBundle.Data) + if err != nil { + msg := fmt.Sprintf("unable to retrieve managed TLS `Secret`: %s", err) + + return r.reconcileWithCondition(&quay, v1.ConditionTypeRolloutBlocked, metav1.ConditionTrue, v1.ConditionReasonConfigInvalid, msg) + } + + quayContext, updatedQuay, err = r.checkRoutesAvailable(quayContext, updatedQuay.DeepCopy(), configBundle.Data) if err != nil && v1.ComponentIsManaged(updatedQuay.Spec.Components, v1.ComponentRoute) { msg := fmt.Sprintf("could not check for `Routes` API: %s", err) return r.reconcileWithCondition(&quay, v1.ConditionTypeRolloutBlocked, metav1.ConditionTrue, v1.ConditionReasonRouteComponentDependencyError, msg) } - quayContext, updatedQuay, err = r.checkObjectBucketClaimsAvailable(quayContext, updatedQuay.DeepCopy(), configBundle.Data["config.yaml"]) + quayContext, updatedQuay, err = r.checkObjectBucketClaimsAvailable(quayContext, updatedQuay.DeepCopy(), configBundle.Data) if err != nil && v1.ComponentIsManaged(updatedQuay.Spec.Components, v1.ComponentObjectStorage) { msg := fmt.Sprintf("could not check for `ObjectBucketClaims` API: %s", err) if _, err = r.updateWithCondition(&quay, v1.ConditionTypeRolloutBlocked, metav1.ConditionTrue, v1.ConditionReasonObjectStorageComponentDependencyError, msg); err != nil { @@ -187,14 +196,14 @@ func (r *QuayRegistryReconciler) Reconcile(ctx context.Context, req ctrl.Request return ctrl.Result{RequeueAfter: time.Millisecond * 1000}, nil } - quayContext, updatedQuay, err = r.checkBuildManagerAvailable(quayContext, updatedQuay.DeepCopy(), configBundle.Data["config.yaml"]) + quayContext, updatedQuay, err = r.checkBuildManagerAvailable(quayContext, updatedQuay.DeepCopy(), configBundle.Data) if err != nil { msg := fmt.Sprintf("could not check for build manager support: %s", err) return r.reconcileWithCondition(&quay, v1.ConditionTypeRolloutBlocked, metav1.ConditionTrue, v1.ConditionReasonObjectStorageComponentDependencyError, msg) } - quayContext, updatedQuay, err = r.checkMonitoringAvailable(quayContext, updatedQuay.DeepCopy(), configBundle.Data["config.yaml"]) + quayContext, updatedQuay, err = r.checkMonitoringAvailable(quayContext, updatedQuay.DeepCopy(), configBundle.Data) if err != nil && v1.ComponentIsManaged(updatedQuay.Spec.Components, v1.ComponentMonitoring) { msg := fmt.Sprintf("could not check for monitoring support: %s", err) diff --git a/controllers/quay/quayregistry_controller_test.go b/controllers/quay/quayregistry_controller_test.go index 0048680fc..0beb2e4a8 100644 --- a/controllers/quay/quayregistry_controller_test.go +++ b/controllers/quay/quayregistry_controller_test.go @@ -11,6 +11,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client" @@ -18,6 +19,7 @@ import ( v1 "github.com/quay/quay-operator/apis/quay/v1" quaycontext "github.com/quay/quay-operator/pkg/context" + "github.com/quay/quay-operator/pkg/kustomize" ) func newQuayRegistry(name, namespace string) *v1.QuayRegistry { @@ -55,6 +57,7 @@ func newConfigBundle(name, namespace string) corev1.Secret { }, Data: map[string][]byte{ "config.yaml": encode(config), + // FIXME(alecmerdler): Test providing TLS cert/key pair... }, } } @@ -139,7 +142,7 @@ var _ = Describe("Reconciling a QuayRegistry", func() { Name: updatedQuayRegistry.Spec.ConfigBundleSecret, Namespace: quayRegistry.GetNamespace()}, &configBundleSecret)). - Should(Succeed()) + To(Succeed()) }) It("will reference the same `configBundleSecret` when reconciled again", func() { @@ -163,7 +166,7 @@ var _ = Describe("Reconciling a QuayRegistry", func() { Name: updatedQuayRegistry.Spec.ConfigBundleSecret, Namespace: quayRegistry.GetNamespace()}, &configBundleSecret)). - Should(Succeed()) + To(Succeed()) }) }) @@ -262,6 +265,34 @@ var _ = Describe("Reconciling a QuayRegistry", func() { return updatedQuayRegistry.Status.CurrentVersion }, time.Second*30).Should(Equal(v1.QuayVersionCurrent)) }) + + It("should generate a self-signed TLS cert/key pair in a new `Secret`", func() { + // result, err = controller.Reconcile(context.Background(), reconcile.Request{NamespacedName: quayRegistryName}) + + // Expect(err).NotTo(HaveOccurred()) + // Expect(result.Requeue).To(BeFalse()) + + var secrets corev1.SecretList + listOptions := client.ListOptions{ + Namespace: namespace, + LabelSelector: labels.SelectorFromSet(map[string]string{ + kustomize.QuayRegistryNameLabel: quayRegistryName.Name, + })} + + Expect(k8sClient.List(context.Background(), &secrets, &listOptions)).To(Succeed()) + + found := false + for _, secret := range secrets.Items { + if v1.IsManagedTLSSecretFor(quayRegistry, &secret) { + found = true + + Expect(secret.Data).To(HaveKey("ssl.cert")) + Expect(secret.Data).To(HaveKey("ssl.key")) + } + } + + Expect(found).To(BeTrue()) + }) }) When("the current version in the `status` block is the same as the Operator", func() { diff --git a/kustomize/base/quay.deployment.yaml b/kustomize/base/quay.deployment.yaml index 55f035997..868536252 100644 --- a/kustomize/base/quay.deployment.yaml +++ b/kustomize/base/quay.deployment.yaml @@ -16,9 +16,13 @@ spec: spec: serviceAccountName: quay-app volumes: - - name: configvolume - secret: - secretName: quay-config-secret + - name: config + projected: + sources: + - secret: + name: quay-config-secret + - secret: + name: quay-config-tls - name: extra-ca-certs configMap: name: cluster-service-ca @@ -76,7 +80,7 @@ spec: port: 8443 scheme: HTTPS volumeMounts: - - name: configvolume + - name: config readOnly: false mountPath: /conf/stack - name: extra-ca-certs diff --git a/kustomize/base/upgrade.deployment.yaml b/kustomize/base/upgrade.deployment.yaml index a51453393..e61af94a9 100644 --- a/kustomize/base/upgrade.deployment.yaml +++ b/kustomize/base/upgrade.deployment.yaml @@ -18,9 +18,13 @@ spec: spec: serviceAccountName: quay-app volumes: - - name: configvolume - secret: - secretName: quay-config-secret + - name: config + projected: + sources: + - secret: + name: quay-config-secret + - secret: + name: quay-config-tls - name: extra-ca-certs configMap: name: cluster-service-ca @@ -76,10 +80,9 @@ spec: port: 8443 scheme: HTTPS volumeMounts: - - name: configvolume + - name: config readOnly: false mountPath: /conf/stack - name: extra-ca-certs readOnly: true - # FIXME: Cannot mount here because Quay attempts to write to this directory... mountPath: /conf/stack/extra_ca_certs diff --git a/kustomize/components/clair/clair.deployment.yaml b/kustomize/components/clair/clair.deployment.yaml index bb8a3b4b6..7f718dc41 100644 --- a/kustomize/components/clair/clair.deployment.yaml +++ b/kustomize/components/clair/clair.deployment.yaml @@ -55,7 +55,7 @@ spec: secretName: clair-config-secret - name: certs secret: - secretName: quay-config-secret + secretName: quay-config-tls # Mount just the public certificate because we are using storage proxying. items: - key: ssl.cert diff --git a/kustomize/components/mirror/mirror.deployment.yaml b/kustomize/components/mirror/mirror.deployment.yaml index 378bca9b8..447c786e6 100644 --- a/kustomize/components/mirror/mirror.deployment.yaml +++ b/kustomize/components/mirror/mirror.deployment.yaml @@ -18,14 +18,19 @@ spec: spec: serviceAccountName: quay-app volumes: - - name: configvolume - secret: - secretName: quay-config-secret + - name: config + projected: + sources: + - secret: + name: quay-config-secret + - secret: + name: quay-config-tls - name: extra-ca-certs projected: sources: - configMap: name: cluster-service-ca + # FIXME(alecmerdler): Have this read from `quay-config-tls` instead... - secret: name: quay-config-secret items: @@ -78,7 +83,7 @@ spec: port: 8443 scheme: HTTPS volumeMounts: - - name: configvolume + - name: config readOnly: false mountPath: /conf/stack - name: extra-ca-certs diff --git a/pkg/context/context.go b/pkg/context/context.go index c96c7ceb9..ca46abc95 100644 --- a/pkg/context/context.go +++ b/pkg/context/context.go @@ -8,6 +8,10 @@ type QuayRegistryContext struct { ServerHostname string BuildManagerHostname string + // TLS + TLSCert []byte + TLSKey []byte + // Object Storage SupportsObjectStorage bool ObjectStorageInitialized bool diff --git a/pkg/kustomize/kustomize.go b/pkg/kustomize/kustomize.go index 938272e6a..8c0ce72e3 100644 --- a/pkg/kustomize/kustomize.go +++ b/pkg/kustomize/kustomize.go @@ -285,6 +285,18 @@ func KustomizationFor(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistr }, }, }, + { + GeneratorArgs: types.GeneratorArgs{ + Name: v1.QuayConfigTLSSecretName, + KvPairSources: types.KvPairSources{ + LiteralSources: []string{ + // FIXME(alecmerdler): These may need to be encoded differntly than just `string()`... + "ssl.cert=" + string(ctx.TLSCert), + "ssl.key=" + string(ctx.TLSKey), + }, + }, + }, + }, { GeneratorArgs: types.GeneratorArgs{ Name: "quay-config-editor-credentials", @@ -497,14 +509,14 @@ func Inflate(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, baseCo } } - log.Info("Ensuring `ssl.cert` and `ssl.key` pair for Quay app TLS") - tlsCert, tlsKey, err := EnsureTLSFor(ctx, quay, baseConfigBundle.Data["ssl.cert"], baseConfigBundle.Data["ssl.key"]) + log.Info("Ensuring TLS cert/key pair for Quay app") + tlsCert, tlsKey, err := EnsureTLSFor(ctx, quay) if err != nil { return nil, err } - componentConfigFiles["ssl.cert"] = tlsCert - componentConfigFiles["ssl.key"] = tlsKey + ctx.TLSCert = tlsCert + ctx.TLSKey = tlsKey kustomization, err := KustomizationFor(ctx, quay, componentConfigFiles) check(err) diff --git a/pkg/kustomize/kustomize_test.go b/pkg/kustomize/kustomize_test.go index 0b57c2b2e..74654c009 100644 --- a/pkg/kustomize/kustomize_test.go +++ b/pkg/kustomize/kustomize_test.go @@ -211,6 +211,7 @@ var quayComponents = map[string][]client.Object{ &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "quay-app"}}, &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "quay-config-editor"}}, &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "quay-config-secret"}}, + &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "quay-config-tls"}}, &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "cluster-service-ca"}}, &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "quay-config-editor-credentials"}}, &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "quay-registry-managed-secret-keys"}}, diff --git a/pkg/kustomize/secrets.go b/pkg/kustomize/secrets.go index 09d5d3167..c5f233d03 100644 --- a/pkg/kustomize/secrets.go +++ b/pkg/kustomize/secrets.go @@ -159,10 +159,10 @@ func BaseConfig() map[string]interface{} { // EnsureTLSFor checks if given TLS cert/key pair are valid for the Quay registry to use for secure communication with clients, // and generates a TLS certificate/key pair if they are not provided. -func EnsureTLSFor(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, tlsCert, tlsKey []byte) ([]byte, []byte, error) { +func EnsureTLSFor(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry) ([]byte, []byte, error) { fieldGroup, err := FieldGroupFor(ctx, "route", quay) if err != nil { - return tlsCert, tlsKey, err + return ctx.TLSCert, ctx.TLSKey, err } routeFieldGroup := fieldGroup.(*hostsettings.HostSettingsFieldGroup) @@ -176,17 +176,17 @@ func EnsureTLSFor(ctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry, t hosts = append(hosts, strings.Split(ctx.BuildManagerHostname, ":")[0]) } - if tlsCert == nil && tlsKey == nil { + if ctx.TLSCert == nil || ctx.TLSKey == nil { return cert.GenerateSelfSignedCertKey(routeFieldGroup.ServerHostname, []net.IP{}, hosts) - } - - for _, host := range hosts { - if valid, validationErr := shared.ValidateCertPairWithHostname(tlsCert, tlsKey, host, fieldGroupNameFor("route")); !valid { - return nil, nil, fmt.Errorf("provided certificate/key pair not valid for host '%s': %s", host, validationErr.String()) + } else { + for _, host := range hosts { + if valid, validationErr := shared.ValidateCertPairWithHostname(ctx.TLSCert, ctx.TLSKey, host, fieldGroupNameFor("route")); !valid { + return nil, nil, fmt.Errorf("provided certificate/key pair not valid for host '%s': %s", host, validationErr.String()) + } } } - return tlsCert, tlsKey, nil + return ctx.TLSCert, ctx.TLSKey, nil } // ContainsComponentConfig accepts a full `config.yaml` and determines if it contains diff --git a/pkg/kustomize/secrets_test.go b/pkg/kustomize/secrets_test.go index 2c63cb994..6132f63e2 100644 --- a/pkg/kustomize/secrets_test.go +++ b/pkg/kustomize/secrets_test.go @@ -425,9 +425,11 @@ func TestEnsureTLSFor(t *testing.T) { quayContext := quaycontext.QuayRegistryContext{ ServerHostname: test.serverHostname, BuildManagerHostname: test.buildManagerHostname, + TLSCert: test.providedCertKeyPair[0], + TLSKey: test.providedCertKeyPair[1], } - tlsCert, tlsKey, err := EnsureTLSFor(&quayContext, quayRegistry, test.providedCertKeyPair[0], test.providedCertKeyPair[1]) + tlsCert, tlsKey, err := EnsureTLSFor(&quayContext, quayRegistry) assert.Equal(test.expectedErr, err, test.name)