| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package controllers |
| |
| import ( |
| "context" |
| "crypto/md5" |
| b64 "encoding/base64" |
| "fmt" |
| solrv1beta1 "github.com/apache/solr-operator/api/v1beta1" |
| "github.com/apache/solr-operator/controllers/util" |
| . "github.com/onsi/ginkgo/v2" |
| . "github.com/onsi/gomega" |
| appsv1 "k8s.io/api/apps/v1" |
| corev1 "k8s.io/api/core/v1" |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| "k8s.io/apimachinery/pkg/util/intstr" |
| "sigs.k8s.io/controller-runtime/pkg/client" |
| "strings" |
| ) |
| |
| var _ = FDescribe("SolrCloud controller - TLS", func() { |
| var ( |
| solrCloud *solrv1beta1.SolrCloud |
| ) |
| |
| BeforeEach(func() { |
| replicas := int32(1) |
| solrCloud = &solrv1beta1.SolrCloud{ |
| ObjectMeta: metav1.ObjectMeta{ |
| Name: "foo", |
| Namespace: "default", |
| }, |
| Spec: solrv1beta1.SolrCloudSpec{ |
| Replicas: &replicas, |
| ZookeeperRef: &solrv1beta1.ZookeeperRef{ |
| ConnectionInfo: &solrv1beta1.ZookeeperConnectionInfo{ |
| ChRoot: "tls-test", |
| InternalConnectionString: "host:7271", |
| }, |
| }, |
| SolrAddressability: solrv1beta1.SolrAddressabilityOptions{ |
| External: &solrv1beta1.ExternalAddressability{ |
| Method: solrv1beta1.Ingress, |
| UseExternalAddress: true, |
| DomainName: testDomain, |
| }, |
| }, |
| }, |
| } |
| }) |
| |
| JustBeforeEach(func(ctx context.Context) { |
| By("creating the SolrCloud") |
| Expect(k8sClient.Create(ctx, solrCloud)).To(Succeed()) |
| |
| By("defaulting the missing SolrCloud values") |
| solrCloud = expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) { |
| g.Expect(found.WithDefaults(logger)).To(BeFalse(), "The SolrCloud spec should not need to be defaulted eventually") |
| }) |
| }) |
| |
| AfterEach(func(ctx context.Context) { |
| cleanupTest(ctx, solrCloud) |
| }) |
| |
| FContext("Secret TLS - With Boostrap BasicAuth", func() { |
| tlsSecretName := "tls-cert-secret-from-user" |
| keystorePassKey := "some-password-key-thingy" |
| BeforeEach(func() { |
| solrCloud.Spec.SolrSecurity = &solrv1beta1.SolrSecurityOptions{AuthenticationType: solrv1beta1.Basic} // with basic-auth too |
| solrCloud.Spec.SolrTLS = createTLSOptions(tlsSecretName, keystorePassKey, false) |
| }) |
| FIt("has the correct resources", func(ctx context.Context) { |
| expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) { |
| g.Expect(found.Spec.SolrAddressability.External.NodePortOverride).To(Equal(443), "Node port override wrong after Spec defaulting") |
| }) |
| verifyUserSuppliedTLSConfig(solrCloud.Spec.SolrTLS, tlsSecretName, keystorePassKey, tlsSecretName) |
| By("checking that the User supplied TLS Config is correct in the generated StatefulSet") |
| verifyReconcileUserSuppliedTLS(ctx, solrCloud, false, false) |
| By("checking that the BasicAuth works as expected when using TLS") |
| foundStatefulSet := expectStatefulSetBasicAuthConfig(ctx, solrCloud, true) |
| |
| By("Checking that the Service has the correct settings") |
| expectTLSService(ctx, solrCloud, foundStatefulSet.Spec.Selector.MatchLabels, true) |
| |
| By("Checking that the Ingress will passthrough with Server TLS") |
| expectPassthroughIngressTLSConfig(ctx, solrCloud, tlsSecretName) |
| |
| By("Checking that the SolrCloud status has the correct scheme for URLs") |
| expectSolrCloudStatusWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloudStatus) { |
| g.Expect(found.InternalCommonAddress).To(Equal("https://"+solrCloud.Name+"-solrcloud-common."+solrCloud.Namespace), "Wrong internal common address in status") |
| g.Expect(found.ExternalCommonAddress).To(Not(BeNil()), "External common address in Status should not be nil.") |
| g.Expect(*found.ExternalCommonAddress).To(Equal("https://"+solrCloud.Namespace+"-"+solrCloud.Name+"-solrcloud."+testDomain), "Wrong external common address in status") |
| }) |
| }) |
| }) |
| |
| FContext("Secret TLS - With Boostrap BasicAuth - Custom Probes", func() { |
| tlsSecretName := "tls-cert-secret-from-user" |
| keystorePassKey := "some-password-key-thingy" |
| BeforeEach(func() { |
| solrCloud.Spec.SolrSecurity = &solrv1beta1.SolrSecurityOptions{ |
| AuthenticationType: solrv1beta1.Basic, |
| ProbesRequireAuth: true, |
| } |
| solrCloud.Spec.CustomSolrKubeOptions.PodOptions = &solrv1beta1.PodOptions{ |
| ReadinessProbe: &corev1.Probe{ |
| ProbeHandler: corev1.ProbeHandler{ |
| HTTPGet: &corev1.HTTPGetAction{ |
| Scheme: corev1.URISchemeHTTPS, |
| Path: "/solr/admin/customreadiness", |
| Port: intstr.FromInt(8983), |
| }, |
| }, |
| }, |
| } |
| solrCloud.Spec.SolrSecurity = &solrv1beta1.SolrSecurityOptions{AuthenticationType: solrv1beta1.Basic} |
| solrCloud.Spec.SolrTLS = createTLSOptions(tlsSecretName, keystorePassKey, false) |
| }) |
| FIt("has the correct resources", func(ctx context.Context) { |
| Expect(util.GetCustomProbePaths(solrCloud)).To(ConsistOf("/solr/admin/customreadiness"), "Utility Probe paths command gives wrong result") |
| |
| verifyUserSuppliedTLSConfig(solrCloud.Spec.SolrTLS, tlsSecretName, keystorePassKey, tlsSecretName) |
| By("checking that the User supplied TLS Config is correct in the generated StatefulSet") |
| verifyReconcileUserSuppliedTLS(ctx, solrCloud, false, false) |
| By("checking that the BasicAuth works as expected when using TLS") |
| foundStatefulSet := expectStatefulSetBasicAuthConfig(ctx, solrCloud, true) |
| |
| By("Checking that the Service has the correct settings") |
| expectTLSService(ctx, solrCloud, foundStatefulSet.Spec.Selector.MatchLabels, true) |
| }) |
| }) |
| |
| FContext("Mounted TLS", func() { |
| mountedDir := mountedTLSDir("/mounted-tls-dir") |
| BeforeEach(func() { |
| solrCloud.Spec.SolrTLS = &solrv1beta1.SolrTLSOptions{ |
| MountedTLSDir: mountedDir, |
| CheckPeerName: true, |
| ClientAuth: "Need", |
| VerifyClientHostname: true, |
| } |
| }) |
| FIt("has the correct resources", func(ctx context.Context) { |
| By("checking that the Mounted TLS Config is correct in the generated StatefulSet") |
| expectStatefulSetMountedTLSDirConfig(ctx, solrCloud) |
| }) |
| }) |
| |
| FContext("Mounted TLS - Non-default file names", func() { |
| mountedDir := &solrv1beta1.MountedTLSDirectory{ |
| Path: "/mounted-non-default", |
| KeystoreFile: "ks.p12", |
| TruststoreFile: "ts.p12", |
| KeystorePasswordFile: "ks-password", |
| TruststorePassword: "ts-password", |
| } |
| BeforeEach(func() { |
| solrCloud.Spec.SolrTLS = &solrv1beta1.SolrTLSOptions{ |
| MountedTLSDir: mountedDir, |
| CheckPeerName: true, |
| ClientAuth: "Need", |
| VerifyClientHostname: true, |
| } |
| }) |
| FIt("has the correct resources", func(ctx context.Context) { |
| By("checking that the Mounted TLS Config is correct in the generated StatefulSet") |
| expectStatefulSetMountedTLSDirConfig(ctx, solrCloud) |
| |
| By("Checking that the SolrCloud status has the correct scheme for URLs") |
| expectSolrCloudStatusWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloudStatus) { |
| g.Expect(found.InternalCommonAddress).To(Equal("https://"+solrCloud.Name+"-solrcloud-common."+solrCloud.Namespace), "Wrong internal common address in status") |
| g.Expect(found.ExternalCommonAddress).To(Not(BeNil()), "External common address in Status should not be nil.") |
| g.Expect(*found.ExternalCommonAddress).To(Equal("https://"+solrCloud.Namespace+"-"+solrCloud.Name+"-solrcloud."+testDomain), "Wrong external common address in status") |
| }) |
| }) |
| }) |
| |
| FContext("Mounted TLS - With Bootstrap BasicAuth", func() { |
| mountedDir := mountedTLSDir("/mounted-tls-dir") |
| BeforeEach(func() { |
| solrCloud.Spec.SolrTLS = &solrv1beta1.SolrTLSOptions{ |
| MountedTLSDir: mountedDir, |
| CheckPeerName: true, |
| ClientAuth: "Need", |
| VerifyClientHostname: true, |
| } |
| solrCloud.Spec.SolrSecurity = &solrv1beta1.SolrSecurityOptions{AuthenticationType: solrv1beta1.Basic} |
| }) |
| FIt("has the correct resources", func(ctx context.Context) { |
| By("checking that the Mounted TLS Config is correct in the generated StatefulSet") |
| expectStatefulSetMountedTLSDirConfig(ctx, solrCloud) |
| By("checking that the BasicAuth works as expected when using TLS") |
| expectStatefulSetBasicAuthConfig(ctx, solrCloud, true) |
| }) |
| }) |
| |
| FContext("Mounted TLS - Server and Client Certs", func() { |
| mountedServerTLSDir := mountedTLSDir("/mounted-tls-dir") |
| mountedClientTLSDir := mountedTLSDir("/mounted-client-tls-dir") |
| BeforeEach(func() { |
| solrCloud.Spec.SolrTLS = &solrv1beta1.SolrTLSOptions{ |
| MountedTLSDir: mountedServerTLSDir, |
| CheckPeerName: true, |
| ClientAuth: "Need", |
| VerifyClientHostname: true, |
| } |
| solrCloud.Spec.SolrClientTLS = &solrv1beta1.SolrTLSOptions{ |
| MountedTLSDir: mountedClientTLSDir, |
| CheckPeerName: true, |
| } |
| solrCloud.Spec.SolrAddressability.External.HideNodes = true |
| }) |
| FIt("has the correct resources", func(ctx context.Context) { |
| By("checking that the Mounted TLS Config is correct in the generated StatefulSet") |
| foundStatefulSet := expectStatefulSetMountedTLSDirConfig(ctx, solrCloud) |
| |
| By("Checking that the Service has the correct settings") |
| expectTLSService(ctx, solrCloud, foundStatefulSet.Spec.Selector.MatchLabels, true) |
| }) |
| }) |
| |
| FContext("Secret TLS - Separate Truststore", func() { |
| tlsSecretName := "tls-cert-secret-from-user" |
| keystorePassKey := "some-password-key-thingy" |
| trustStoreSecretName := "custom-truststore-secret" |
| trustStoreFile := "truststore.p12" |
| BeforeEach(func() { |
| solrCloud.Spec.SolrSecurity = &solrv1beta1.SolrSecurityOptions{AuthenticationType: solrv1beta1.Basic} |
| solrCloud.Spec.SolrTLS = createTLSOptions(tlsSecretName, keystorePassKey, false) |
| solrCloud.Spec.SolrTLS.TrustStoreSecret = &corev1.SecretKeySelector{ |
| LocalObjectReference: corev1.LocalObjectReference{Name: trustStoreSecretName}, |
| Key: trustStoreFile, |
| } |
| solrCloud.Spec.SolrTLS.TrustStorePasswordSecret = &corev1.SecretKeySelector{ |
| LocalObjectReference: corev1.LocalObjectReference{Name: trustStoreSecretName}, |
| Key: "truststore-pass", |
| } |
| solrCloud.Spec.SolrTLS.ClientAuth = solrv1beta1.Need // require client auth too (mTLS between the pods) |
| }) |
| FIt("has the correct resources", func(ctx context.Context) { |
| verifyUserSuppliedTLSConfig(solrCloud.Spec.SolrTLS, tlsSecretName, keystorePassKey, tlsSecretName) |
| By("checking that the User supplied TLS Config is correct in the generated StatefulSet") |
| verifyReconcileUserSuppliedTLS(ctx, solrCloud, false, false) |
| By("checking that the BasicAuth works as expected when using TLS") |
| expectStatefulSetBasicAuthConfig(ctx, solrCloud, true) |
| |
| By("Checking that the Ingress will passthrough with Server TLS") |
| expectPassthroughIngressTLSConfig(ctx, solrCloud, tlsSecretName) |
| }) |
| }) |
| |
| FContext("Secret TLS - Pkcs12 Conversion", func() { |
| tlsSecretName := "tls-cert-secret-from-user-no-pkcs12" |
| keystorePassKey := "some-password-key-thingy" |
| BeforeEach(func() { |
| solrCloud.Spec.SolrSecurity = &solrv1beta1.SolrSecurityOptions{AuthenticationType: solrv1beta1.Basic} |
| solrCloud.Spec.SolrTLS = createTLSOptions(tlsSecretName, keystorePassKey, false) |
| }) |
| FIt("has the correct resources", func(ctx context.Context) { |
| verifyUserSuppliedTLSConfig(solrCloud.Spec.SolrTLS, tlsSecretName, keystorePassKey, tlsSecretName) |
| By("checking that the User supplied TLS Config is correct in the generated StatefulSet") |
| verifyReconcileUserSuppliedTLS(ctx, solrCloud, true, false) |
| By("checking that the BasicAuth works as expected when using TLS") |
| expectStatefulSetBasicAuthConfig(ctx, solrCloud, true) |
| }) |
| }) |
| |
| FContext("Secret TLS - Restart on Secret Update", func() { |
| tlsSecretName := "tls-cert-secret-update" |
| keystorePassKey := "some-password-key-thingy" |
| BeforeEach(func() { |
| solrCloud.Spec.SolrSecurity = &solrv1beta1.SolrSecurityOptions{AuthenticationType: solrv1beta1.Basic} |
| solrCloud.Spec.SolrTLS = createTLSOptions(tlsSecretName, keystorePassKey, true) |
| solrCloud.Spec.SolrTLS.ClientAuth = solrv1beta1.Need |
| }) |
| FIt("has the correct resources", func(ctx context.Context) { |
| verifyUserSuppliedTLSConfig(solrCloud.Spec.SolrTLS, tlsSecretName, keystorePassKey, tlsSecretName) |
| By("checking that the User supplied TLS Config is correct in the generated StatefulSet") |
| verifyReconcileUserSuppliedTLS(ctx, solrCloud, false, true) |
| By("checking that the BasicAuth works as expected when using TLS") |
| expectStatefulSetBasicAuthConfig(ctx, solrCloud, true) |
| }) |
| }) |
| |
| FContext("Secret TLS - Server & Client Certs - Restart on Secret Update", func() { |
| serverCertSecret := "tls-server-cert" |
| clientCertSecret := "tls-client-cert" |
| keystorePassKey := "some-password-key-thingy" |
| BeforeEach(func() { |
| solrCloud.Spec.SolrTLS = createTLSOptions(serverCertSecret, keystorePassKey, true) |
| solrCloud.Spec.SolrTLS.ClientAuth = solrv1beta1.Need |
| |
| // Additional client cert |
| solrCloud.Spec.SolrClientTLS = &solrv1beta1.SolrTLSOptions{ |
| KeyStorePasswordSecret: &corev1.SecretKeySelector{ |
| LocalObjectReference: corev1.LocalObjectReference{Name: clientCertSecret}, |
| Key: keystorePassKey, |
| }, |
| PKCS12Secret: &corev1.SecretKeySelector{ |
| LocalObjectReference: corev1.LocalObjectReference{Name: clientCertSecret}, |
| Key: util.DefaultPkcs12KeystoreFile, |
| }, |
| TrustStoreSecret: &corev1.SecretKeySelector{ |
| LocalObjectReference: corev1.LocalObjectReference{Name: clientCertSecret}, |
| Key: util.DefaultPkcs12TruststoreFile, |
| }, |
| RestartOnTLSSecretUpdate: true, |
| } |
| }) |
| FIt("has the correct resources", func(ctx context.Context) { |
| By("checking that the User supplied TLS Config is correct in the generated StatefulSet") |
| verifyReconcileUserSuppliedTLS(ctx, solrCloud, false, true) |
| |
| By("Checking that the Ingress will passthrough with Server TLS") |
| expectPassthroughIngressTLSConfig(ctx, solrCloud, serverCertSecret) |
| }) |
| }) |
| |
| FContext("Secret TLS - Enable on Existing Cluster", func() { |
| tlsSecretName := "tls-cert-secret-from-user" |
| keystorePassKey := "some-password-key-thingy" |
| BeforeEach(func() { |
| solrCloud.Spec.SolrSecurity = &solrv1beta1.SolrSecurityOptions{AuthenticationType: solrv1beta1.Basic} |
| }) |
| FIt("has the correct resources", func(ctx context.Context) { |
| By("checking that the BasicAuth works as expected when using TLS") |
| expectStatefulSetBasicAuthConfig(ctx, solrCloud, true) |
| |
| By("Checking that the SolrCloud status has the correct scheme for URLs before TLS is enabled") |
| expectSolrCloudStatusWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloudStatus) { |
| g.Expect(found.InternalCommonAddress).To(Equal("http://"+solrCloud.Name+"-solrcloud-common."+solrCloud.Namespace), "Wrong internal common address in status") |
| g.Expect(found.ExternalCommonAddress).To(Not(BeNil()), "External common address in Status should not be nil.") |
| g.Expect(*found.ExternalCommonAddress).To(Equal("http://"+solrCloud.Namespace+"-"+solrCloud.Name+"-solrcloud."+testDomain), "Wrong external common address in status") |
| }) |
| |
| By("Adding TLS to the existing cluster") |
| solrCloudWithTlS := expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) { |
| found.Spec.SolrTLS = createTLSOptions(tlsSecretName, keystorePassKey, false) |
| g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Could not update SolrCloud to enable TLS") |
| }) |
| |
| By("checking that the User supplied TLS Config is correct in the generated StatefulSet") |
| verifyReconcileUserSuppliedTLS(ctx, solrCloudWithTlS, false, false) |
| |
| By("checking that the BasicAuth works as expected when using TLS") |
| expectStatefulSetBasicAuthConfig(ctx, solrCloudWithTlS, true) |
| |
| By("Checking that the Ingress will passthrough with Server TLS") |
| expectPassthroughIngressTLSConfig(ctx, solrCloudWithTlS, tlsSecretName) |
| |
| By("Checking that the SolrCloud status has the correct scheme for URLs after TLS is enabled") |
| expectSolrCloudStatusWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloudStatus) { |
| g.Expect(found.InternalCommonAddress).To(Equal("https://"+solrCloud.Name+"-solrcloud-common."+solrCloud.Namespace+":80"), "Wrong internal common address in status") |
| g.Expect(found.ExternalCommonAddress).To(Not(BeNil()), "External common address in Status should not be nil.") |
| g.Expect(*found.ExternalCommonAddress).To(Equal("https://"+solrCloud.Namespace+"-"+solrCloud.Name+"-solrcloud."+testDomain), "Wrong external common address in status") |
| }) |
| }) |
| }) |
| |
| FContext("Common Ingress TLS Termination", func() { |
| tlsSecretName := "tls-cert-secret-from-user" |
| BeforeEach(func() { |
| solrCloud.Spec.SolrSecurity = &solrv1beta1.SolrSecurityOptions{AuthenticationType: solrv1beta1.Basic} |
| solrCloud.Spec.SolrAddressability.External.IngressTLSTermination = &solrv1beta1.SolrIngressTLSTermination{ |
| TLSSecret: tlsSecretName, |
| } |
| }) |
| FIt("has the correct resources - Explicit Secret", func(ctx context.Context) { |
| By("Checking that the Ingress will terminate TLS") |
| expectTerminateIngressTLSConfig(ctx, solrCloud, tlsSecretName, false) |
| |
| By("Checking that the SolrCloud status has the correct scheme for URLs") |
| expectSolrCloudStatusWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloudStatus) { |
| g.Expect(found.InternalCommonAddress).To(Equal("http://"+solrCloud.Name+"-solrcloud-common."+solrCloud.Namespace), "Wrong internal common address in status") |
| g.Expect(found.ExternalCommonAddress).To(Not(BeNil()), "External common address in Status should not be nil.") |
| g.Expect(*found.ExternalCommonAddress).To(Equal("https://"+solrCloud.Namespace+"-"+solrCloud.Name+"-solrcloud."+testDomain), "Wrong external common address in status") |
| }) |
| |
| foundStatefulSet := expectStatefulSet(ctx, solrCloud, solrCloud.StatefulSetName()) |
| |
| By("Checking that the Service has the correct settings") |
| expectTLSService(ctx, solrCloud, foundStatefulSet.Spec.Selector.MatchLabels, false) |
| }) |
| }) |
| |
| FContext("Common Ingress TLS Termination - Default Secret", func() { |
| BeforeEach(func() { |
| solrCloud.Spec.SolrSecurity = &solrv1beta1.SolrSecurityOptions{AuthenticationType: solrv1beta1.Basic} |
| solrCloud.Spec.SolrAddressability.External.IngressTLSTermination = &solrv1beta1.SolrIngressTLSTermination{ |
| UseDefaultTLSSecret: true, |
| } |
| }) |
| FIt("has the correct resources", func(ctx context.Context) { |
| By("Checking that the Ingress will terminate TLS") |
| expectTerminateIngressTLSConfig(ctx, solrCloud, "", false) |
| |
| By("Checking that the SolrCloud status has the correct scheme for URLs") |
| expectSolrCloudStatusWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloudStatus) { |
| g.Expect(found.InternalCommonAddress).To(Equal("http://"+solrCloud.Name+"-solrcloud-common."+solrCloud.Namespace), "Wrong internal common address in status") |
| g.Expect(found.ExternalCommonAddress).To(Not(BeNil()), "External common address in Status should not be nil.") |
| g.Expect(*found.ExternalCommonAddress).To(Equal("https://"+solrCloud.Namespace+"-"+solrCloud.Name+"-solrcloud."+testDomain), "Wrong external common address in status") |
| }) |
| |
| foundStatefulSet := expectStatefulSet(ctx, solrCloud, solrCloud.StatefulSetName()) |
| |
| By("Checking that the Service has the correct settings") |
| expectTLSService(ctx, solrCloud, foundStatefulSet.Spec.Selector.MatchLabels, false) |
| }) |
| }) |
| }) |
| |
| // Ensures all the TLS env vars, volume mounts and initContainers are setup for the PodTemplateSpec |
| func expectTLSConfigOnPodTemplate(solrCloud *solrv1beta1.SolrCloud, tls *solrv1beta1.SolrTLSOptions, podTemplate *corev1.PodTemplateSpec, needsPkcs12InitContainer bool, clientOnly bool, clientTLS *solrv1beta1.SolrTLSOptions) *corev1.Container { |
| return expectTLSConfigOnPodTemplateWithGomega(Default, solrCloud, tls, podTemplate, needsPkcs12InitContainer, clientOnly, clientTLS) |
| } |
| |
| // Ensures all the TLS env vars, volume mounts and initContainers are setup for the PodTemplateSpec |
| func expectTLSConfigOnPodTemplateWithGomega(g Gomega, solrCloud *solrv1beta1.SolrCloud, tls *solrv1beta1.SolrTLSOptions, podTemplate *corev1.PodTemplateSpec, needsPkcs12InitContainer bool, clientOnly bool, clientTLS *solrv1beta1.SolrTLSOptions) *corev1.Container { |
| g.Expect(podTemplate.Spec.Volumes).To(Not(BeNil()), "Solr Pod Volumes should not be nil when using TLS") |
| |
| if tls.PKCS12Secret != nil { |
| var keystoreVol *corev1.Volume = nil |
| for _, vol := range podTemplate.Spec.Volumes { |
| if vol.Name == "keystore" { |
| keystoreVol = &vol |
| break |
| } |
| } |
| g.Expect(keystoreVol).To(Not(BeNil()), fmt.Sprintf("keystore volume not found in pod template; volumes: %v", podTemplate.Spec.Volumes)) |
| g.Expect(keystoreVol.VolumeSource.Secret).To(Not(BeNil()), "Didn't find TLS keystore volume in sts config! keystoreVol: "+keystoreVol.String()) |
| g.Expect(keystoreVol.VolumeSource.Secret.SecretName).To(Equal(tls.PKCS12Secret.Name), "Incorrect PKCS12Secret Secret") |
| //g.Expect(keystoreVol.VolumeSource.Secret.Items).To(Not(BeEmpty()), "PKCS12Secret Secret Volume does not specify a key to mount") |
| //g.Expect(keystoreVol.VolumeSource.Secret.Items[0].Key).To(Equal(tls.PKCS12Secret.Key), "Incorrect PKCS12Secret key in PKCS12 volume items") |
| } |
| |
| // check the SOLR_SSL_ related env vars on the sts |
| g.Expect(podTemplate.Spec.Containers).To(Not(BeEmpty()), "The Solr Pod should have at least 1 container") |
| mainContainer := podTemplate.Spec.Containers[0] |
| g.Expect(mainContainer).To(Not(BeNil()), "Didn't find the main solrcloud-node container in the sts!") |
| g.Expect(mainContainer.Env).To(Not(BeEmpty()), "Didn't find the main solrcloud-node container in the sts!") |
| |
| // is there a separate truststore? |
| expectedTrustStorePath := "" |
| if tls.TrustStoreSecret != nil { |
| expectedTrustStorePath = util.DefaultTrustStorePath + "/" + tls.TrustStoreSecret.Key |
| } |
| |
| if tls.PKCS12Secret != nil { |
| expectTLSEnvVarsWithGomega(g, mainContainer.Env, tls.KeyStorePasswordSecret.Name, tls.KeyStorePasswordSecret.Key, needsPkcs12InitContainer, expectedTrustStorePath, clientOnly, clientTLS) |
| } else if tls.TrustStoreSecret != nil { |
| envVars := mainContainer.Env |
| g.Expect(envVars).To(Not(BeEmpty()), "Env vars for Pod should not be empty") |
| envVars = filterVarsByName(envVars, func(n string) bool { |
| return strings.HasPrefix(n, "SOLR_SSL_") |
| }) |
| g.Expect(len(envVars)).To(Equal(3), "Wrong number of SSL env vars for Pod") |
| for _, envVar := range envVars { |
| if envVar.Name == "SOLR_SSL_CLIENT_TRUST_STORE" { |
| g.Expect(envVar.Value).To(Equal(expectedTrustStorePath), "Wrong envVar value for %s", envVar.Name) |
| } |
| if envVar.Name == "SOLR_SSL_CLIENT_TRUST_STORE_PASSWORD" { |
| g.Expect(envVar.Value).To(BeEmpty(), "EnvVar %s should not use an explicit Value, since it is populated from a secret", envVar.Name) |
| g.Expect(envVar.ValueFrom).To(Not(BeNil()), "EnvVar %s must have a ValueFrom, since it is populated from a secret", envVar.Name) |
| g.Expect(envVar.ValueFrom.SecretKeyRef).To(Not(BeNil()), "EnvVar %s must have a ValueFrom.SecretKeyRef, since it is populated from a secret", envVar.Name) |
| g.Expect(envVar.ValueFrom.SecretKeyRef.Name).To(Equal(tls.TrustStorePasswordSecret.Name), "EnvVar %s is using the wrong secret to populate the value", envVar.Name) |
| g.Expect(envVar.ValueFrom.SecretKeyRef.Key).To(Equal(tls.TrustStorePasswordSecret.Key), "EnvVar %s is using the wrong secret key to populate the value", envVar.Name) |
| } |
| } |
| } |
| |
| // different trust store? |
| if tls.TrustStoreSecret != nil { |
| var truststoreVol *corev1.Volume = nil |
| for _, vol := range podTemplate.Spec.Volumes { |
| if vol.Name == "truststore" { |
| truststoreVol = &vol |
| break |
| } |
| } |
| g.Expect(truststoreVol).To(Not(BeNil()), fmt.Sprintf("truststore volume not found in pod template; volumes: %v", podTemplate.Spec.Volumes)) |
| g.Expect(truststoreVol.VolumeSource.Secret).To(Not(BeNil()), "Didn't find TLS truststore volume in sts config!") |
| g.Expect(truststoreVol.VolumeSource.Secret.SecretName).To(Equal(tls.TrustStoreSecret.Name), "Truststore Volume is referencing the wrong Secret") |
| //g.Expect(truststoreVol.VolumeSource.Secret.Items).To(Not(BeEmpty()), "truststore Secret Volume does not specify a key to mount") |
| //g.Expect(truststoreVol.VolumeSource.Secret.Items[0].Key).To(Equal(tls.PKCS12Secret.Key), "Incorrect truststore Secret key in truststore volume items") |
| } |
| |
| // initContainers |
| if needsPkcs12InitContainer { |
| var pkcs12Vol *corev1.Volume = nil |
| for _, vol := range podTemplate.Spec.Volumes { |
| if vol.Name == "pkcs12" { |
| pkcs12Vol = &vol |
| break |
| } |
| } |
| |
| g.Expect(pkcs12Vol).To(Not(BeNil()), "Didn't find TLS keystore volume in sts config!") |
| g.Expect(pkcs12Vol.EmptyDir).To(Not(BeNil()), "pkcs12 vol should by an emptyDir") |
| |
| g.Expect(podTemplate.Spec.InitContainers).To(Not(BeEmpty()), "An init container should exist to do necessary SSL logic") |
| var expInitContainer *corev1.Container = nil |
| for _, cnt := range podTemplate.Spec.InitContainers { |
| if cnt.Name == "gen-pkcs12-keystore" { |
| expInitContainer = &cnt |
| break |
| } |
| } |
| expCmd := "openssl pkcs12 -export -in /var/solr/tls/tls.crt -in /var/solr/tls/ca.crt -inkey /var/solr/tls/tls.key -out /var/solr/tls/pkcs12/keystore.p12 -passout pass:${SOLR_SSL_KEY_STORE_PASSWORD}" |
| g.Expect(expInitContainer).To(Not(BeNil()), "Didn't find the gen-pkcs12-keystore InitContainer in the sts!") |
| g.Expect(expInitContainer.Command[2]).To(Equal(expCmd), "Wrong TLS initContainer command") |
| } |
| |
| if tls.MountedTLSDir != nil { |
| expectMountedTLSDirConfigOnPodTemplateWithGomega(g, podTemplate, solrCloud) |
| } |
| |
| if solrCloud != nil && (solrCloud.Spec.SolrTLS.ClientAuth == solrv1beta1.Need || solrCloud.Spec.SolrTLS.VerifyClientHostname) { |
| g.Expect(mainContainer.LivenessProbe).To(Not(BeNil()), "main container should have a liveness probe defined") |
| g.Expect(mainContainer.LivenessProbe.Exec).To(Not(BeNil()), "liveness probe should have an exec when mTLS is enabled") |
| g.Expect(mainContainer.LivenessProbe.Exec.Command).To(Not(BeNil()), "liveness probe should have an exec when mTLS is enabled") |
| g.Expect(mainContainer.LivenessProbe.Exec.Command).To(HaveLen(3), "liveness probe command has wrong number of args") |
| path := "/solr" + util.DefaultLivenessProbePath |
| if solrCloud.Spec.CustomSolrKubeOptions.PodOptions != nil && solrCloud.Spec.CustomSolrKubeOptions.PodOptions.LivenessProbe != nil { |
| path = solrCloud.Spec.CustomSolrKubeOptions.PodOptions.LivenessProbe.HTTPGet.Path |
| } |
| g.Expect(mainContainer.LivenessProbe.Exec.Command[2]).To(ContainSubstring(fmt.Sprintf("solr api -get \"%s://${SOLR_HOST}:%d%s\"", solrCloud.UrlScheme(false), solrCloud.Spec.SolrAddressability.PodPort, path)), "liveness probe should invoke solr api -get to contact Solr securely") |
| g.Expect(mainContainer.ReadinessProbe).To(Not(BeNil()), "main container should have a readiness probe defined") |
| g.Expect(mainContainer.ReadinessProbe.Exec).To(Not(BeNil()), "readiness probe should have an exec when mTLS is enabled") |
| g.Expect(mainContainer.ReadinessProbe.Exec.Command).To(HaveLen(3), "readiness probe command has wrong number of args") |
| path = "/solr" + util.DefaultReadinessProbePath |
| if solrCloud.Spec.CustomSolrKubeOptions.PodOptions != nil && solrCloud.Spec.CustomSolrKubeOptions.PodOptions.ReadinessProbe != nil { |
| path = solrCloud.Spec.CustomSolrKubeOptions.PodOptions.ReadinessProbe.HTTPGet.Path |
| } |
| g.Expect(mainContainer.ReadinessProbe.Exec.Command[2]).To(ContainSubstring(fmt.Sprintf("solr api -get \"%s://${SOLR_HOST}:%d%s\"", solrCloud.UrlScheme(false), solrCloud.Spec.SolrAddressability.PodPort, path)), "readiness probe should invoke solr api -get to contact Solr securely") |
| if solrCloud.Spec.CustomSolrKubeOptions.PodOptions != nil && solrCloud.Spec.CustomSolrKubeOptions.PodOptions.StartupProbe != nil { |
| g.Expect(mainContainer.StartupProbe).To(Not(BeNil()), "main container should have a startup probe defined") |
| g.Expect(mainContainer.StartupProbe.Exec).To(Not(BeNil()), "startup probe should have an exec when auth is enabled") |
| g.Expect(mainContainer.StartupProbe.Exec.Command).To(HaveLen(3), "startup probe command has wrong number of args") |
| path = "/solr" + util.DefaultStartupProbePath |
| if solrCloud.Spec.CustomSolrKubeOptions.PodOptions != nil && solrCloud.Spec.CustomSolrKubeOptions.PodOptions.StartupProbe != nil { |
| path = solrCloud.Spec.CustomSolrKubeOptions.PodOptions.StartupProbe.HTTPGet.Path |
| } |
| g.Expect(mainContainer.StartupProbe.Exec.Command[2]).To(ContainSubstring(fmt.Sprintf("solr api -get \"%s://${SOLR_HOST}:%d%s\"", solrCloud.UrlScheme(false), solrCloud.Spec.SolrAddressability.PodPort, path)), "startup probe should invoke solr api -get to contact Solr securely") |
| } |
| } else if solrCloud != nil { |
| g.Expect(mainContainer.LivenessProbe).To(Not(BeNil()), "main container should have a liveness probe defined") |
| g.Expect(mainContainer.LivenessProbe.HTTPGet).To(Not(BeNil()), "liveness probe should have an http get when client auth is not needed") |
| g.Expect(mainContainer.LivenessProbe.HTTPGet.HTTPHeaders).To(HaveLen(1), "liveness probe http get should have host header") |
| g.Expect(mainContainer.LivenessProbe.HTTPGet.HTTPHeaders[0].Name).To(Equal("Host"), "liveness probe http get should have host header") |
| g.Expect(mainContainer.LivenessProbe.HTTPGet.HTTPHeaders[0].Value).To(Equal(solrCloud.InternalCommonUrl(false)), "liveness probe http get should have a correct host header") |
| g.Expect(mainContainer.ReadinessProbe).To(Not(BeNil()), "main container should have a readiness probe defined") |
| g.Expect(mainContainer.ReadinessProbe.HTTPGet).To(Not(BeNil()), "readiness probe should have an http get when client auth is not needed") |
| g.Expect(mainContainer.ReadinessProbe.HTTPGet.HTTPHeaders).To(HaveLen(1), "readiness probe http get should have host header") |
| g.Expect(mainContainer.ReadinessProbe.HTTPGet.HTTPHeaders[0].Name).To(Equal("Host"), "readiness probe http get should have host header") |
| g.Expect(mainContainer.ReadinessProbe.HTTPGet.HTTPHeaders[0].Value).To(Equal(solrCloud.InternalCommonUrl(false)), "readiness probe http get should have a correct host header") |
| if solrCloud.Spec.CustomSolrKubeOptions.PodOptions != nil && solrCloud.Spec.CustomSolrKubeOptions.PodOptions.StartupProbe != nil { |
| g.Expect(mainContainer.StartupProbe).To(Not(BeNil()), "main container should have a startup probe defined") |
| g.Expect(mainContainer.StartupProbe.HTTPGet).To(Not(BeNil()), "startup probe should have an http get when client auth is not needed") |
| g.Expect(mainContainer.StartupProbe.HTTPGet.HTTPHeaders).To(HaveLen(1), "startup probe http get should have host header") |
| g.Expect(mainContainer.StartupProbe.HTTPGet.HTTPHeaders[0].Name).To(Equal("Host"), "startup probe http get should have host header") |
| g.Expect(mainContainer.StartupProbe.HTTPGet.HTTPHeaders[0].Value).To(Equal(solrCloud.InternalCommonUrl(false)), "startup probe http get should have a correct host header") |
| } |
| } |
| |
| return &mainContainer // return as a convenience in case tests want to do more checking on the main container |
| } |
| |
| func expectMountedTLSDirEnvVars(g Gomega, envVars []corev1.EnvVar, solrCloud *solrv1beta1.SolrCloud) { |
| g.Expect(envVars).To(Not(BeNil()), "Solr Pod must have env vars when using Mounted TLS Dir") |
| envVars = filterVarsByName(envVars, func(n string) bool { |
| return strings.HasPrefix(n, "SOLR_SSL_") |
| }) |
| |
| expectedTLSVarsCount := 8 |
| |
| if solrCloud.Spec.SolrClientTLS != nil { |
| expectedTLSVarsCount += 4 |
| if solrCloud.Spec.SolrClientTLS.MountedTLSDir != nil { |
| expectedTLSVarsCount -= 2 |
| if solrCloud.Spec.SolrClientTLS.MountedTLSDir.KeystorePasswordFile == "" && solrCloud.Spec.SolrClientTLS.MountedTLSDir.KeystorePassword != "" { |
| expectedTLSVarsCount += 1 |
| } |
| if solrCloud.Spec.SolrClientTLS.MountedTLSDir.TruststorePasswordFile == "" && solrCloud.Spec.SolrClientTLS.MountedTLSDir.TruststorePassword != "" { |
| expectedTLSVarsCount += 1 |
| } |
| } |
| } |
| |
| if !solrCloud.Spec.SolrTLS.VerifyClientHostname { |
| expectedTLSVarsCount += 1 |
| } |
| if solrCloud.Spec.SolrTLS.MountedTLSDir != nil { |
| expectedTLSVarsCount -= 2 |
| if solrCloud.Spec.SolrTLS.MountedTLSDir.KeystorePasswordFile == "" && solrCloud.Spec.SolrTLS.MountedTLSDir.KeystorePassword != "" { |
| expectedTLSVarsCount += 1 |
| } |
| if solrCloud.Spec.SolrTLS.MountedTLSDir.TruststorePasswordFile == "" && solrCloud.Spec.SolrTLS.MountedTLSDir.TruststorePassword != "" { |
| expectedTLSVarsCount += 1 |
| } |
| } |
| g.Expect(envVars).To(HaveLen(expectedTLSVarsCount), "expected SOLR_SSL (and maybe SOLR_SSL_CLIENT) related env vars not found") |
| |
| expectedKeystorePath := solrCloud.Spec.SolrTLS.MountedTLSDir.Path + "/" + solrCloud.Spec.SolrTLS.MountedTLSDir.KeystoreFile |
| expectedTruststorePath := solrCloud.Spec.SolrTLS.MountedTLSDir.Path + "/" + solrCloud.Spec.SolrTLS.MountedTLSDir.TruststoreFile |
| |
| for _, envVar := range envVars { |
| if envVar.Name == "SOLR_SSL_ENABLED" { |
| g.Expect(envVar.Value).To(Equal("true"), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_KEY_STORE" { |
| g.Expect(envVar.Value).To(Equal(expectedKeystorePath), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_TRUST_STORE" { |
| g.Expect(envVar.Value).To(Equal(expectedTruststorePath), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_WANT_CLIENT_AUTH" { |
| g.Expect(envVar.Value).To(Equal("false"), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_NEED_CLIENT_AUTH" { |
| g.Expect(envVar.Value).To(Equal("true"), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_CHECK_PEER_NAME" { |
| g.Expect(envVar.Value).To(Equal("true"), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_CLIENT_HOSTNAME_VERIFICATION" { |
| g.Expect(envVar.Value).To(Equal("false"), "Wrong envVar value for %s", envVar.Name) |
| } |
| } |
| |
| if solrCloud.Spec.SolrClientTLS != nil && solrCloud.Spec.SolrClientTLS.MountedTLSDir != nil { |
| for _, envVar := range envVars { |
| if envVar.Name == "SOLR_SSL_CLIENT_KEY_STORE" { |
| expectedKeystorePath = solrCloud.Spec.SolrClientTLS.MountedTLSDir.Path + "/" + solrCloud.Spec.SolrClientTLS.MountedTLSDir.KeystoreFile |
| g.Expect(envVar.Value).To(Equal(expectedKeystorePath), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_CLIENT_TRUST_STORE" { |
| expectedTruststorePath = solrCloud.Spec.SolrClientTLS.MountedTLSDir.Path + "/" + solrCloud.Spec.SolrClientTLS.MountedTLSDir.TruststoreFile |
| g.Expect(envVar.Value).To(Equal(expectedTruststorePath), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_CLIENT_KEY_STORE_PASSWORD" { |
| g.Expect(envVar.Value).To(Equal(solrCloud.Spec.SolrClientTLS.MountedTLSDir.KeystorePassword), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_CLIENT_TRUST_STORE_PASSWORD" { |
| g.Expect(envVar.Value).To(Equal(solrCloud.Spec.SolrClientTLS.MountedTLSDir.TruststorePassword), "Wrong envVar value for %s", envVar.Name) |
| } |
| } |
| } |
| } |
| |
| // ensure the TLS related env vars are set for the Solr pod |
| func expectTLSEnvVars(envVars []corev1.EnvVar, expectedKeystorePasswordSecretName string, expectedKeystorePasswordSecretKey string, needsPkcs12InitContainer bool, expectedTruststorePath string, clientOnly bool, clientTLS *solrv1beta1.SolrTLSOptions) { |
| expectTLSEnvVarsWithGomega(Default, envVars, expectedKeystorePasswordSecretName, expectedKeystorePasswordSecretKey, needsPkcs12InitContainer, expectedTruststorePath, clientOnly, clientTLS) |
| } |
| |
| // ensure the TLS related env vars are set for the Solr pod |
| func expectTLSEnvVarsWithGomega(g Gomega, envVars []corev1.EnvVar, expectedKeystorePasswordSecretName string, expectedKeystorePasswordSecretKey string, needsPkcs12InitContainer bool, expectedTruststorePath string, clientOnly bool, clientTLS *solrv1beta1.SolrTLSOptions) { |
| g.Expect(envVars).To(Not(BeNil()), "Env Vars should not be nil") |
| envVars = filterVarsByName(envVars, func(n string) bool { |
| return strings.HasPrefix(n, "SOLR_SSL_") |
| }) |
| |
| if clientOnly { |
| g.Expect(len(envVars)).To(Equal(5), "Wrong number of SSL env vars when only using Client Certs") |
| } else { |
| if clientTLS != nil { |
| g.Expect(len(envVars)).To(Equal(13), "Wrong number of SSL env vars when using Client & Server Certs") |
| } else { |
| g.Expect(len(envVars)).To(Equal(9), "Wrong number of SSL env vars when only using Server Certs") |
| } |
| } |
| |
| expectedKeystorePath := util.DefaultKeyStorePath + "/keystore.p12" |
| if needsPkcs12InitContainer { |
| expectedKeystorePath = util.DefaultWritableKeyStorePath + "/keystore.p12" |
| } |
| |
| if expectedTruststorePath == "" { |
| expectedTruststorePath = expectedKeystorePath |
| } |
| |
| for _, envVar := range envVars { |
| if envVar.Name == "SOLR_SSL_ENABLED" { |
| g.Expect(envVar.Value).To(Equal("true"), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_KEY_STORE" { |
| g.Expect(envVar.Value).To(Equal(expectedKeystorePath), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_TRUST_STORE" { |
| g.Expect(envVar.Value).To(Equal(expectedTruststorePath), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_KEY_STORE_PASSWORD" { |
| g.Expect(envVar.Value).To(BeEmpty(), "EnvVar %s should not use an explicit Value, since it is populated from a secret", envVar.Name) |
| g.Expect(envVar.ValueFrom).To(Not(BeNil()), "EnvVar %s must have a ValueFrom, since it is populated from a secret", envVar.Name) |
| g.Expect(envVar.ValueFrom.SecretKeyRef).To(Not(BeNil()), "EnvVar %s must have a ValueFrom.SecretKeyRef, since it is populated from a secret", envVar.Name) |
| g.Expect(envVar.ValueFrom.SecretKeyRef.Name).To(Equal(expectedKeystorePasswordSecretName), "EnvVar %s is using the wrong secret to populate the value", envVar.Name) |
| g.Expect(envVar.ValueFrom.SecretKeyRef.Key).To(Equal(expectedKeystorePasswordSecretKey), "EnvVar %s is using the wrong secret key to populate the value", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_TRUST_STORE_STORE_PASSWORD" { |
| g.Expect(envVar.Value).To(BeEmpty(), "EnvVar %s should not use an explicit Value, since it is populated from a secret", envVar.Name) |
| g.Expect(envVar.ValueFrom).To(Not(BeNil()), "EnvVar %s must have a ValueFrom, since it is populated from a secret", envVar.Name) |
| g.Expect(envVar.ValueFrom.SecretKeyRef).To(Not(BeNil()), "EnvVar %s must have a ValueFrom.SecretKeyRef, since it is populated from a secret", envVar.Name) |
| } |
| } |
| |
| if clientTLS != nil { |
| for _, envVar := range envVars { |
| |
| if envVar.Name == "SOLR_SSL_CLIENT_KEY_STORE" { |
| g.Expect(envVar.Value).To(Equal("/var/solr/client-tls/keystore.p12"), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_CLIENT_TRUST_STORE" { |
| g.Expect(envVar.Value).To(Equal("/var/solr/client-tls/truststore.p12"), "Wrong envVar value for %s", envVar.Name) |
| } |
| |
| if envVar.Name == "SOLR_SSL_CLIENT_KEY_STORE_PASSWORD" || envVar.Name == "SOLR_SSL_CLIENT_TRUST_STORE_PASSWORD" { |
| g.Expect(envVar.Value).To(BeEmpty(), "EnvVar %s should not use an explicit Value, since it is populated from a secret", envVar.Name) |
| g.Expect(envVar.ValueFrom).To(Not(BeNil()), "EnvVar %s must have a ValueFrom, since it is populated from a secret", envVar.Name) |
| g.Expect(envVar.ValueFrom.SecretKeyRef).To(Not(BeNil()), "EnvVar %s must have a ValueFrom.SecretKeyRef, since it is populated from a secret", envVar.Name) |
| } |
| } |
| } |
| } |
| |
| func verifyUserSuppliedTLSConfig(tls *solrv1beta1.SolrTLSOptions, expectedKeystorePasswordSecretName string, expectedKeystorePasswordSecretKey string, expectedTlsSecretName string) { |
| Expect(tls).To(Not(BeNil()), "TLS Options should not be nil") |
| Expect(tls.KeyStorePasswordSecret.Name).To(Equal(expectedKeystorePasswordSecretName), "Incorrect Keystore Password Secret") |
| Expect(tls.KeyStorePasswordSecret.Key).To(Equal(expectedKeystorePasswordSecretKey), "Incorrect Keystore Password Secret key") |
| Expect(tls.PKCS12Secret.Name).To(Equal(expectedTlsSecretName), "Incorrect PKCS12Secret Secret") |
| Expect(tls.PKCS12Secret.Key).To(Equal("keystore.p12"), "Incorrect PKCS12Secret Secret key") |
| } |
| |
| func createTLSOptions(tlsSecretName string, keystorePassKey string, restartOnTLSSecretUpdate bool) *solrv1beta1.SolrTLSOptions { |
| return &solrv1beta1.SolrTLSOptions{ |
| KeyStorePasswordSecret: &corev1.SecretKeySelector{ |
| LocalObjectReference: corev1.LocalObjectReference{Name: tlsSecretName}, |
| Key: keystorePassKey, |
| }, |
| PKCS12Secret: &corev1.SecretKeySelector{ |
| LocalObjectReference: corev1.LocalObjectReference{Name: tlsSecretName}, |
| Key: util.DefaultPkcs12KeystoreFile, |
| }, |
| RestartOnTLSSecretUpdate: restartOnTLSSecretUpdate, |
| } |
| } |
| |
| func createMockTLSSecret(ctx context.Context, parentObject client.Object, secretName string, secretKey string, keystorePasswordKey string, truststoreKey string) corev1.Secret { |
| secretData := map[string][]byte{} |
| secretData[secretKey] = []byte(b64.StdEncoding.EncodeToString([]byte("mock keystore"))) |
| secretData[util.TLSCertKey] = []byte(b64.StdEncoding.EncodeToString([]byte("mock tls.crt"))) |
| |
| if keystorePasswordKey != "" { |
| secretData[keystorePasswordKey] = []byte(b64.StdEncoding.EncodeToString([]byte("mock keystore password"))) |
| } |
| |
| if truststoreKey != "" { |
| secretData[truststoreKey] = []byte(b64.StdEncoding.EncodeToString([]byte("mock truststore"))) |
| } |
| |
| mockTLSSecret := corev1.Secret{ |
| ObjectMeta: metav1.ObjectMeta{Name: secretName, Namespace: parentObject.GetNamespace()}, |
| Data: secretData, |
| Type: corev1.SecretTypeOpaque, |
| } |
| Expect(k8sClient.Create(ctx, &mockTLSSecret)).To(Succeed(), "Could not create mock TLS Secret") |
| return mockTLSSecret |
| } |
| |
| func expectStatefulSetMountedTLSDirConfig(ctx context.Context, solrCloud *solrv1beta1.SolrCloud) *appsv1.StatefulSet { |
| statefulSet := expectStatefulSet(ctx, solrCloud, solrCloud.StatefulSetName()) |
| podTemplate := &statefulSet.Spec.Template |
| expectTLSConfigOnPodTemplateWithGomega(Default, solrCloud, solrCloud.Spec.SolrTLS, podTemplate, false, false, solrCloud.Spec.SolrClientTLS) |
| |
| // Check HTTPS cluster prop setup container |
| Expect(podTemplate.Spec.InitContainers).To(Not(BeEmpty()), "Init containers cannot be empty when using Mounted TLS") |
| expectZkSetupInitContainerForTLS(solrCloud, statefulSet) |
| |
| // should have a mount for the initdb on main container |
| expectInitdbVolumeMount(podTemplate) |
| return statefulSet |
| } |
| |
| func expectInitDBGeneratingInitContainer(g Gomega, podTemplate *corev1.PodTemplateSpec, solrCloud *solrv1beta1.SolrCloud) { |
| // verify the probes use a command with SSL opts |
| expectedServerKeystorePasswordSource := "" |
| expectedServerTruststorePasswordSource := "" |
| expectedClientKeystorePasswordSource := "" |
| expectedClientTruststorePasswordSource := "" |
| needsInitContainer := false |
| // if there's a client cert, then the probe should use that, else uses the server cert |
| if solrCloud.Spec.SolrTLS != nil && solrCloud.Spec.SolrTLS.MountedTLSDir != nil { |
| if solrCloud.Spec.SolrTLS.MountedTLSDir.KeystorePasswordFile != "" { |
| expectedServerKeystorePasswordSource = solrCloud.Spec.SolrTLS.MountedTLSDir.Path + "/" + solrCloud.Spec.SolrTLS.MountedTLSDir.KeystorePasswordFile |
| needsInitContainer = true |
| } else if solrCloud.Spec.SolrTLS.MountedTLSDir.KeystorePassword == "" { |
| expectedServerKeystorePasswordSource = solrCloud.Spec.SolrTLS.MountedTLSDir.Path + "/keystore-password" |
| needsInitContainer = true |
| } |
| if solrCloud.Spec.SolrTLS.MountedTLSDir.TruststorePasswordFile != "" { |
| expectedServerTruststorePasswordSource = solrCloud.Spec.SolrTLS.MountedTLSDir.Path + "/" + solrCloud.Spec.SolrTLS.MountedTLSDir.TruststorePasswordFile |
| needsInitContainer = true |
| } else if solrCloud.Spec.SolrTLS.MountedTLSDir.TruststorePassword == "" { |
| expectedServerTruststorePasswordSource = "${SOLR_SSL_KEY_STORE_PASSWORD}" |
| needsInitContainer = true |
| } |
| } |
| if solrCloud.Spec.SolrClientTLS != nil && solrCloud.Spec.SolrClientTLS.MountedTLSDir != nil { |
| if solrCloud.Spec.SolrClientTLS.MountedTLSDir.KeystorePasswordFile != "" { |
| expectedClientKeystorePasswordSource = solrCloud.Spec.SolrClientTLS.MountedTLSDir.Path + "/" + solrCloud.Spec.SolrClientTLS.MountedTLSDir.KeystorePasswordFile |
| needsInitContainer = true |
| } else if solrCloud.Spec.SolrClientTLS.MountedTLSDir.KeystorePassword == "" { |
| expectedClientKeystorePasswordSource = solrCloud.Spec.SolrClientTLS.MountedTLSDir.Path + "/keystore-password" |
| needsInitContainer = true |
| } |
| if solrCloud.Spec.SolrClientTLS.MountedTLSDir.TruststorePasswordFile != "" { |
| expectedClientTruststorePasswordSource = solrCloud.Spec.SolrClientTLS.MountedTLSDir.Path + "/" + solrCloud.Spec.SolrClientTLS.MountedTLSDir.TruststorePasswordFile |
| needsInitContainer = true |
| } else if solrCloud.Spec.SolrClientTLS.MountedTLSDir.TruststorePassword == "" { |
| expectedClientTruststorePasswordSource = "${SOLR_SSL_CLIENT_KEY_STORE_PASSWORD}" |
| needsInitContainer = true |
| } |
| } |
| |
| var initDBInitContainer *corev1.Container |
| for _, container := range podTemplate.Spec.InitContainers { |
| if container.Name == util.InitdbInitContainer { |
| initDBInitContainer = &container |
| } |
| } |
| if needsInitContainer { |
| g.Expect(initDBInitContainer).ToNot(BeNil(), "Did not find an initDB generating init container when TLS passwords/Basic Auth are stored in files") |
| |
| expectExportedVariable(g, initDBInitContainer.Command[2], "SOLR_SSL_KEY_STORE_PASSWORD", expectedServerKeystorePasswordSource) |
| expectExportedVariable(g, initDBInitContainer.Command[2], "SOLR_SSL_TRUST_STORE_PASSWORD", expectedServerTruststorePasswordSource) |
| expectExportedVariable(g, initDBInitContainer.Command[2], "SOLR_SSL_CLIENT_KEY_STORE_PASSWORD", expectedClientKeystorePasswordSource) |
| expectExportedVariable(g, initDBInitContainer.Command[2], "SOLR_SSL_CLIENT_TRUST_STORE_PASSWORD", expectedClientTruststorePasswordSource) |
| } else { |
| g.Expect(initDBInitContainer).To(BeNil(), "Found an initDB generating init container when TLS passwords/Basic Auth are NOT stored in files") |
| } |
| } |
| |
| func expectExportedVariable(g Gomega, command string, varName string, valueSource string) { |
| if valueSource != "" { |
| if strings.HasPrefix(valueSource, "${") { |
| // This is a passed-through variable |
| g.ExpectWithOffset(1, command).To(ContainSubstring(varName+"=\""+valueSource+"\""), "Exported variable not found in initDB script") |
| } else { |
| // This is a file path |
| g.ExpectWithOffset(1, command).To(ContainSubstring(varName+"=\"$(cat \""+valueSource+"\")\""), "Exported variable not found in initDB script") |
| } |
| } else { |
| g.ExpectWithOffset(1, command).ToNot(ContainSubstring(varName), "Exported variable found in initDB script that should not be there") |
| } |
| } |
| |
| func expectMountedTLSDirConfigOnPodTemplate(podTemplate *corev1.PodTemplateSpec, solrCloud *solrv1beta1.SolrCloud) { |
| expectMountedTLSDirConfigOnPodTemplateWithGomega(Default, podTemplate, solrCloud) |
| } |
| |
| func expectMountedTLSDirConfigOnPodTemplateWithGomega(g Gomega, podTemplate *corev1.PodTemplateSpec, solrCloud *solrv1beta1.SolrCloud) { |
| g.Expect(podTemplate.Spec.Containers).To(Not(BeEmpty()), "Solr Pod must have containers") |
| mainContainer := podTemplate.Spec.Containers[0] |
| g.Expect(mainContainer).To(Not(BeNil()), "Didn't find the main solrcloud-node container in the sts!") |
| g.Expect(mainContainer.Env).To(Not(BeEmpty()), "No Env vars for main solrcloud-node container in the sts!") |
| |
| expectMountedTLSDirEnvVars(g, mainContainer.Env, solrCloud) |
| |
| expectInitDBGeneratingInitContainer(g, podTemplate, solrCloud) |
| } |
| |
| // ensures the TLS settings are applied correctly to the STS |
| func expectStatefulSetTLSConfig(solrCloud *solrv1beta1.SolrCloud, statefulSet *appsv1.StatefulSet, needsPkcs12InitContainer bool) { |
| expectStatefulSetTLSConfigWithGomega(Default, solrCloud, statefulSet, needsPkcs12InitContainer) |
| } |
| |
| func expectStatefulSetTLSConfigWithGomega(g Gomega, solrCloud *solrv1beta1.SolrCloud, statefulSet *appsv1.StatefulSet, needsPkcs12InitContainer bool) { |
| podTemplate := &statefulSet.Spec.Template |
| expectTLSConfigOnPodTemplateWithGomega(g, solrCloud, solrCloud.Spec.SolrTLS, podTemplate, needsPkcs12InitContainer, false, solrCloud.Spec.SolrClientTLS) |
| |
| // Check HTTPS cluster prop setup container |
| g.Expect(podTemplate.Spec.InitContainers).To(Not(BeEmpty()), "Solr Pods with TLS require init containers") |
| expectZkSetupInitContainerForTLSWithGomega(g, solrCloud, statefulSet) |
| } |
| |
| func expectZkSetupInitContainerForTLS(solrCloud *solrv1beta1.SolrCloud, statefulSet *appsv1.StatefulSet) { |
| expectZkSetupInitContainerForTLSWithGomega(Default, solrCloud, statefulSet) |
| } |
| |
| func expectZkSetupInitContainerForTLSWithGomega(g Gomega, solrCloud *solrv1beta1.SolrCloud, statefulSet *appsv1.StatefulSet) { |
| var zkSetupInitContainer *corev1.Container = nil |
| podTemplate := &statefulSet.Spec.Template |
| for _, cnt := range podTemplate.Spec.InitContainers { |
| if cnt.Name == "setup-zk" { |
| zkSetupInitContainer = &cnt |
| break |
| } |
| } |
| expChrootCmd := "solr zk ls ${ZK_CHROOT} -z ${ZK_SERVER} || solr zk mkroot ${ZK_CHROOT} -z ${ZK_SERVER};" |
| expCmd := "/opt/solr/server/scripts/cloud-scripts/zkcli.sh -zkhost ${ZK_HOST} -cmd clusterprop -name urlScheme -val https" |
| if solrCloud.Spec.SolrTLS != nil { |
| g.Expect(zkSetupInitContainer).To(Not(BeNil()), "Didn't find the zk-setup InitContainer in the sts!") |
| if zkSetupInitContainer != nil { |
| g.Expect(zkSetupInitContainer.Image).To(Equal(statefulSet.Spec.Template.Spec.Containers[0].Image), "The zk-setup init container should use the same image as the Solr container") |
| g.Expect(zkSetupInitContainer.Command).To(HaveLen(3), "Wrong command length for zk-setup init container") |
| g.Expect(zkSetupInitContainer.Command[2]).To(ContainSubstring(expCmd), "ZK Setup command does not set urlScheme") |
| g.Expect(zkSetupInitContainer.Command[2]).To(ContainSubstring(expChrootCmd), "ZK Setup command does init the chroot") |
| expNumVars := 3 |
| if solrCloud.Spec.SolrSecurity != nil && solrCloud.Spec.SolrSecurity.BasicAuthSecret == "" { |
| expNumVars = 4 // one more for SECURITY_JSON |
| } |
| g.Expect(zkSetupInitContainer.Env).To(HaveLen(expNumVars), "Wrong number of envVars for zk-setup init container") |
| } |
| } else { |
| g.Expect(zkSetupInitContainer).To(BeNil(), "Shouldn't find the zk-setup InitContainer in the sts, when not using https!") |
| } |
| } |
| |
| func expectTLSService(ctx context.Context, solrCloud *solrv1beta1.SolrCloud, selectorLables map[string]string, podsHaveTLSEnabled bool) { |
| podPort := 8983 |
| servicePort := 80 |
| if podsHaveTLSEnabled { |
| servicePort = 443 |
| } |
| By("testing the Solr Common Service") |
| commonService := expectService(ctx, solrCloud, solrCloud.CommonServiceName(), selectorLables, false) |
| Expect(commonService.Spec.Ports[0].Name).To(Equal("solr-client"), "Wrong port name on common Service") |
| Expect(commonService.Spec.Ports[0].Port).To(Equal(int32(servicePort)), "Wrong port on common Service") |
| Expect(commonService.Spec.Ports[0].TargetPort.StrVal).To(Equal("solr-client"), "Wrong podPort name on common Service") |
| Expect(commonService.Spec.Ports[0].Protocol).To(Equal(corev1.ProtocolTCP), "Wrong protocol on common Service") |
| if podsHaveTLSEnabled { |
| Expect(commonService.Spec.Ports[0].AppProtocol).ToNot(BeNil(), "AppProtocol on common Service should not be nil") |
| Expect(*commonService.Spec.Ports[0].AppProtocol).To(Equal("https"), "Wrong appProtocol on common Service") |
| } else { |
| Expect(commonService.Spec.Ports[0].AppProtocol).To(BeNil(), "AppProtocol on common Service should be nil when not running with TLS") |
| } |
| |
| if solrCloud.Spec.SolrAddressability.External.UsesIndividualNodeServices() { |
| nodeNames := solrCloud.GetAllSolrPodNames() |
| By("testing the Solr Node Service(s)") |
| for _, nodeName := range nodeNames { |
| service := expectService(ctx, solrCloud, nodeName, util.MergeLabelsOrAnnotations(selectorLables, map[string]string{"statefulset.kubernetes.io/pod-name": nodeName}), false) |
| Expect(service.Spec.Ports[0].Name).To(Equal("solr-client"), "Wrong port name on common Service") |
| Expect(service.Spec.Ports[0].Port).To(Equal(int32(servicePort)), "Wrong port on node Service") |
| Expect(service.Spec.Ports[0].TargetPort.StrVal).To(Equal("solr-client"), "Wrong podPort name on node Service") |
| Expect(service.Spec.Ports[0].Protocol).To(Equal(corev1.ProtocolTCP), "Wrong protocol on node Service") |
| if podsHaveTLSEnabled { |
| Expect(service.Spec.Ports[0].AppProtocol).ToNot(BeNil(), "AppProtocol on node Service should not be nil") |
| Expect(*service.Spec.Ports[0].AppProtocol).To(Equal("https"), "Wrong appProtocol on node Service") |
| } else { |
| Expect(service.Spec.Ports[0].AppProtocol).To(BeNil(), "AppProtocol on node Service should be nil when not running with TLS") |
| } |
| } |
| } else { |
| By("testing the Solr Headless Service") |
| headlessService := expectService(ctx, solrCloud, solrCloud.HeadlessServiceName(), selectorLables, true) |
| Expect(headlessService.Spec.Ports[0].Name).To(Equal("solr-client"), "Wrong port name on common Service") |
| Expect(headlessService.Spec.Ports[0].Port).To(Equal(int32(podPort)), "Wrong port on headless Service") |
| Expect(headlessService.Spec.Ports[0].TargetPort.StrVal).To(Equal("solr-client"), "Wrong podPort name on headless Service") |
| Expect(headlessService.Spec.Ports[0].Protocol).To(Equal(corev1.ProtocolTCP), "Wrong protocol on headless Service") |
| if podsHaveTLSEnabled { |
| Expect(headlessService.Spec.Ports[0].AppProtocol).ToNot(BeNil(), "AppProtocol on headless Service should not be nil") |
| Expect(*headlessService.Spec.Ports[0].AppProtocol).To(Equal("https"), "Wrong appProtocol on headless Service") |
| } else { |
| Expect(headlessService.Spec.Ports[0].AppProtocol).To(BeNil(), "AppProtocol on headless Service should be nil when not running with TLS") |
| } |
| } |
| } |
| |
| func expectPassthroughIngressTLSConfig(ctx context.Context, solrCloud *solrv1beta1.SolrCloud, expectedTLSSecretName string) { |
| ingress := expectIngress(ctx, solrCloud, solrCloud.CommonIngressName()) |
| Expect(ingress.Spec.TLS).To(Not(BeEmpty()), "Ingress does not have TLS secrets provided") |
| Expect(ingress.Spec.TLS[0].SecretName).To(Equal(expectedTLSSecretName), "Wrong secretName for ingress TLS") |
| if solrCloud.Spec.SolrTLS != nil { |
| Expect(ingress.ObjectMeta.Annotations).To(HaveKeyWithValue("nginx.ingress.kubernetes.io/backend-protocol", "HTTPS"), "Ingress Backend Protocol annotation incorrect") |
| } else { |
| Expect(ingress.ObjectMeta.Annotations).To(HaveKeyWithValue("nginx.ingress.kubernetes.io/backend-protocol", "HTTP"), "Ingress Backend Protocol annotation incorrect") |
| } |
| if len(ingress.Spec.TLS) > 0 { |
| Expect(ingress.ObjectMeta.Annotations).To(HaveKeyWithValue("nginx.ingress.kubernetes.io/ssl-redirect", "true"), "Ingress SSL Redirect annotation incorrect") |
| } else { |
| Expect(ingress.ObjectMeta.Annotations).To(Not(HaveKey("nginx.ingress.kubernetes.io/ssl-redirect")), "Ingress should not have SSL Redirect annotation") |
| } |
| } |
| |
| func expectTerminateIngressTLSConfig(ctx context.Context, solrCloud *solrv1beta1.SolrCloud, expectedTLSSecretName string, isBackendTls bool) { |
| ingress := expectIngress(ctx, solrCloud, solrCloud.CommonIngressName()) |
| Expect(ingress.Spec.TLS).To(HaveLen(1), "Wrong number of TLS Secrets for ingress") |
| Expect(ingress.Spec.TLS[0].SecretName).To(Equal(expectedTLSSecretName), "Wrong secretName for ingress TLS") |
| Expect(ingress.Spec.TLS[0].Hosts).To(ConsistOf("default-foo-solrcloud."+testDomain, "default-foo-solrcloud-0."+testDomain), "Wrong hosts for Ingress TLS termination") |
| |
| if isBackendTls { |
| Expect(ingress.ObjectMeta.Annotations).To(HaveKeyWithValue("nginx.ingress.kubernetes.io/backend-protocol", "HTTPS"), "Ingress Backend Protocol annotation incorrect") |
| } else { |
| Expect(ingress.ObjectMeta.Annotations).To(HaveKeyWithValue("nginx.ingress.kubernetes.io/backend-protocol", "HTTP"), "Ingress Backend Protocol annotation incorrect") |
| } |
| Expect(ingress.ObjectMeta.Annotations).To(HaveKeyWithValue("nginx.ingress.kubernetes.io/ssl-redirect", "true"), "Ingress SSL Redirect annotation incorrect") |
| } |
| |
| func expectRestartOnTLSSecretUpdate(ctx context.Context, secretName string, solrCloud *solrv1beta1.SolrCloud, needsPkcs12InitContainer bool, certAnnotation string) { |
| foundTLSSecret := expectSecret(ctx, solrCloud, secretName) |
| // let's trigger an update to the TLS secret to simulate the cert getting renewed and the pods getting restarted |
| expectStatefulSetWithChecks(ctx, solrCloud, solrCloud.StatefulSetName(), func(g Gomega, found *appsv1.StatefulSet) { |
| expectedCertMd5 := fmt.Sprintf("%x", md5.Sum(foundTLSSecret.Data[util.TLSCertKey])) |
| g.Expect(found.Spec.Template.ObjectMeta.Annotations).To(HaveKeyWithValue(certAnnotation, expectedCertMd5), "TLS cert MD5 annotation on STS does not match the secret") |
| }) |
| |
| // change the tls.crt which should trigger a rolling restart |
| updatedTlsCertData := "certificate renewed" |
| foundTLSSecret.Data[util.TLSCertKey] = []byte(updatedTlsCertData) |
| Expect(k8sClient.Update(ctx, foundTLSSecret)).To(Succeed(), "Cannot update the TLS Secret") |
| |
| // Check the annotation on the pod template to make sure a rolling restart will take place |
| By("Checking that the cert MD5 has changed on the pod template annotations") |
| expectStatefulSetWithChecks(ctx, solrCloud, solrCloud.StatefulSetName(), func(g Gomega, found *appsv1.StatefulSet) { |
| expectStatefulSetTLSConfigWithGomega(g, solrCloud, found, needsPkcs12InitContainer) |
| |
| expectedCertMd5 := fmt.Sprintf("%x", md5.Sum(foundTLSSecret.Data[util.TLSCertKey])) |
| g.Expect(found.Spec.Template.ObjectMeta.Annotations).To(HaveKeyWithValue(certAnnotation, expectedCertMd5), "TLS cert MD5 annotation on STS does not match the UPDATED secret") |
| }) |
| } |
| |
| func verifyReconcileUserSuppliedTLS(ctx context.Context, solrCloud *solrv1beta1.SolrCloud, needsPkcs12InitContainer bool, restartOnTLSSecretUpdate bool) { |
| // Custom truststore? |
| if solrCloud.Spec.SolrTLS.TrustStoreSecret != nil { |
| // create the mock truststore secret |
| secretData := map[string][]byte{} |
| secretData[solrCloud.Spec.SolrTLS.TrustStoreSecret.Key] = []byte(b64.StdEncoding.EncodeToString([]byte("mock truststore"))) |
| secretData[solrCloud.Spec.SolrTLS.TrustStorePasswordSecret.Key] = []byte(b64.StdEncoding.EncodeToString([]byte("mock truststore password"))) |
| trustStoreSecret := corev1.Secret{ |
| ObjectMeta: metav1.ObjectMeta{Name: solrCloud.Spec.SolrTLS.TrustStoreSecret.Name, Namespace: solrCloud.Namespace}, |
| Data: secretData, |
| Type: corev1.SecretTypeOpaque, |
| } |
| Expect(k8sClient.Create(ctx, &trustStoreSecret)).To(Succeed(), "Cannot create mock truststore secret") |
| } |
| |
| // create the secret required for reconcile, it has both keys ... |
| tlsKey := "keystore.p12" |
| if needsPkcs12InitContainer { |
| tlsKey = "tls.key" // to trigger the initContainer creation, don't want keystore.p12 in the secret |
| } |
| |
| // Create a mock secret in the background so the isCert ready function returns |
| if solrCloud.Spec.SolrTLS != nil && solrCloud.Spec.SolrTLS.PKCS12Secret != nil { |
| createMockTLSSecret(ctx, solrCloud, solrCloud.Spec.SolrTLS.PKCS12Secret.Name, tlsKey, |
| solrCloud.Spec.SolrTLS.KeyStorePasswordSecret.Key, "") |
| } |
| |
| // need a secret for the client cert too? |
| if solrCloud.Spec.SolrClientTLS != nil && solrCloud.Spec.SolrClientTLS.PKCS12Secret != nil { |
| truststoreKey := "" |
| if solrCloud.Spec.SolrClientTLS.TrustStoreSecret == solrCloud.Spec.SolrClientTLS.PKCS12Secret { |
| truststoreKey = solrCloud.Spec.SolrClientTLS.TrustStoreSecret.Key |
| } |
| createMockTLSSecret(ctx, solrCloud, solrCloud.Spec.SolrClientTLS.PKCS12Secret.Name, util.DefaultPkcs12KeystoreFile, |
| solrCloud.Spec.SolrClientTLS.KeyStorePasswordSecret.Key, truststoreKey) |
| } |
| |
| statefulSet := expectStatefulSetWithChecks(ctx, solrCloud, solrCloud.StatefulSetName(), func(g Gomega, found *appsv1.StatefulSet) { |
| expectStatefulSetTLSConfigWithGomega(g, solrCloud, found, needsPkcs12InitContainer) |
| }) |
| |
| if !restartOnTLSSecretUpdate { |
| Expect(statefulSet.Spec.Template.ObjectMeta.Annotations).To(Not(HaveKey(util.SolrTlsCertMd5Annotation)), "Shouldn't have a cert MD5 as we're not tracking updates to the secret") |
| return |
| } |
| |
| // let's trigger an update to the TLS secret to simulate the cert getting renewed and the pods getting restarted |
| expectRestartOnTLSSecretUpdate(ctx, solrCloud.Spec.SolrTLS.PKCS12Secret.Name, solrCloud, needsPkcs12InitContainer, util.SolrTlsCertMd5Annotation) |
| |
| // Update the client cert and see the change get picked up |
| if solrCloud.Spec.SolrClientTLS != nil && solrCloud.Spec.SolrClientTLS.PKCS12Secret != nil && solrCloud.Spec.SolrClientTLS.RestartOnTLSSecretUpdate { |
| expectRestartOnTLSSecretUpdate(ctx, solrCloud.Spec.SolrClientTLS.PKCS12Secret.Name, solrCloud, false, util.SolrClientTlsCertMd5Annotation) |
| } |
| } |
| |
| func mountedTLSDir(path string) *solrv1beta1.MountedTLSDirectory { |
| return &solrv1beta1.MountedTLSDirectory{ |
| Path: path, |
| KeystoreFile: util.DefaultPkcs12KeystoreFile, |
| TruststoreFile: util.DefaultPkcs12TruststoreFile, |
| KeystorePasswordFile: util.DefaultKeystorePasswordFile, |
| } |
| } |