diff --git a/api/v1beta1/activemqartemis_types.go b/api/v1beta1/activemqartemis_types.go index 49726bc55..04aafd417 100644 --- a/api/v1beta1/activemqartemis_types.go +++ b/api/v1beta1/activemqartemis_types.go @@ -582,6 +582,9 @@ type AcceptorType struct { // Host for Ingress and Route resources of the acceptor. It supports the following variables: $(CR_NAME), $(CR_NAMESPACE), $(BROKER_ORDINAL), $(ITEM_NAME), $(RES_NAME) and $(INGRESS_DOMAIN). It is required for the acceptors exposed with the ingress mode when the ingress domain is not specified. //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Ingress Host",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:text"} IngressHost string `json:"ingressHost,omitempty"` + // The name of the truststore secret. + //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Trust Secret",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:text"} + TrustSecret *string `json:"trustSecret,omitempty"` } type ConnectorType struct { @@ -642,6 +645,9 @@ type ConnectorType struct { // Host for Ingress and Route resources of the acceptor. It supports the following variables: $(CR_NAME), $(CR_NAMESPACE), $(BROKER_ORDINAL), $(ITEM_NAME), $(RES_NAME) and $(INGRESS_DOMAIN). It is required for the connectors exposed with the ingress mode when the ingress domain is not specified. //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Ingress Host",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:text"} IngressHost string `json:"ingressHost,omitempty"` + // The name of the truststore secret. + //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Trust Secret",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:text"} + TrustSecret *string `json:"trustSecret,omitempty"` } type ConsoleType struct { @@ -666,6 +672,9 @@ type ConsoleType struct { // Host for Ingress and Route resources of the acceptor. It supports the following variables: $(CR_NAME), $(CR_NAMESPACE), $(BROKER_ORDINAL), $(ITEM_NAME), $(RES_NAME) and $(INGRESS_DOMAIN). It is required for the console exposed with the ingress mode when the ingress domain is not specified. //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Ingress Host",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:text"} IngressHost string `json:"ingressHost,omitempty"` + // The name of the truststore secret. + //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Trust Secret",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:text"} + TrustSecret *string `json:"trustSecret,omitempty"` } // ActiveMQArtemis App product upgrade flags, this is deprecated in v1beta1, specifying the Version is sufficient @@ -802,6 +811,7 @@ const ( ValidConditionFailedDuplicateAcceptorPort = "DuplicateAcceptorPort" ValidConditionFailedInvalidExposeMode = "InvalidExposeMode" ValidConditionFailedInvalidIngressSettings = "InvalidIngressSettings" + ValidConditionInvalidCertSecretReason = "InvalidCertSecret" ReadyConditionType = "Ready" ReadyConditionReason = "ResourceReady" diff --git a/api/v1beta1/activemqartemisaddress_webhook.go b/api/v1beta1/activemqartemisaddress_webhook.go index ba217c507..b25798586 100644 --- a/api/v1beta1/activemqartemisaddress_webhook.go +++ b/api/v1beta1/activemqartemisaddress_webhook.go @@ -69,7 +69,7 @@ func (r *ActiveMQArtemisAddress) ValidateUpdate(old runtime.Object) (warnings ad // ValidateDelete implements webhook.Validator so a webhook will be registered for the type func (r *ActiveMQArtemisAddress) ValidateDelete() (warnings admission.Warnings, err error) { - activemqartemisaddresslog.V(1).Info("validate delete", "name", r.Name) + activemqartemisaddresslog.Info("validate delete", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. return nil, nil diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 3c18b0243..215743c03 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -51,6 +51,11 @@ func (in *AcceptorType) DeepCopyInto(out *AcceptorType) { *out = new(bool) **out = **in } + if in.TrustSecret != nil { + in, out := &in.TrustSecret, &out.TrustSecret + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AcceptorType. @@ -1130,6 +1135,11 @@ func (in *ConnectorType) DeepCopyInto(out *ConnectorType) { *out = new(ExposeMode) **out = **in } + if in.TrustSecret != nil { + in, out := &in.TrustSecret, &out.TrustSecret + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConnectorType. @@ -1150,6 +1160,11 @@ func (in *ConsoleType) DeepCopyInto(out *ConsoleType) { *out = new(ExposeMode) **out = **in } + if in.TrustSecret != nil { + in, out := &in.TrustSecret, &out.TrustSecret + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConsoleType. diff --git a/bundle/manifests/activemq-artemis-operator.clusterserviceversion.yaml b/bundle/manifests/activemq-artemis-operator.clusterserviceversion.yaml index 60c556b01..f2f3b351c 100644 --- a/bundle/manifests/activemq-artemis-operator.clusterserviceversion.yaml +++ b/bundle/manifests/activemq-artemis-operator.clusterserviceversion.yaml @@ -598,6 +598,11 @@ spec: path: acceptors[0].suppressInternalManagementObjects x-descriptors: - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: The name of the truststore secret. + displayName: Trust Secret + path: acceptors[0].trustSecret + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:text - description: Provider used for the truststore; "SUN", "SunJCE", etc. Default in broker is null displayName: TrustStore Provider @@ -1111,6 +1116,11 @@ spec: path: connectors[0].sslSecret x-descriptors: - urn:alm:descriptor:com.tectonic.ui:text + - description: The name of the truststore secret. + displayName: Trust Secret + path: connectors[0].trustSecret + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:text - description: Provider used for the truststore; "SUN", "SunJCE", etc. Default in broker is null displayName: TrustStore Provider @@ -1180,6 +1190,11 @@ spec: path: console.sslSecret x-descriptors: - urn:alm:descriptor:com.tectonic.ui:text + - description: The name of the truststore secret. + displayName: Trust Secret + path: console.trustSecret + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:text - description: If the embedded server requires client authentication displayName: Use Client Auth path: console.useClientAuth diff --git a/bundle/manifests/broker.amq.io_activemqartemises.yaml b/bundle/manifests/broker.amq.io_activemqartemises.yaml index 4bef076cd..82de52e82 100644 --- a/bundle/manifests/broker.amq.io_activemqartemises.yaml +++ b/bundle/manifests/broker.amq.io_activemqartemises.yaml @@ -125,6 +125,9 @@ spec: description: If prevents advisory addresses/queues to be registered to management service, default false type: boolean + trustSecret: + description: The name of the truststore secret. + type: string trustStoreProvider: description: Provider used for the truststore; "SUN", "SunJCE", etc. Default in broker is null @@ -539,6 +542,9 @@ spec: sslSecret: description: Name of the secret to use for ssl information type: string + trustSecret: + description: The name of the truststore secret. + type: string trustStoreProvider: description: Provider used for the truststore; "SUN", "SunJCE", etc. Default in broker is null @@ -598,6 +604,9 @@ spec: sslSecret: description: Name of the secret to use for ssl information type: string + trustSecret: + description: The name of the truststore secret. + type: string useClientAuth: description: If the embedded server requires client authentication type: boolean diff --git a/config/crd/bases/broker.amq.io_activemqartemises.yaml b/config/crd/bases/broker.amq.io_activemqartemises.yaml index 6618949f5..1abebd4e3 100644 --- a/config/crd/bases/broker.amq.io_activemqartemises.yaml +++ b/config/crd/bases/broker.amq.io_activemqartemises.yaml @@ -126,6 +126,9 @@ spec: description: If prevents advisory addresses/queues to be registered to management service, default false type: boolean + trustSecret: + description: The name of the truststore secret. + type: string trustStoreProvider: description: Provider used for the truststore; "SUN", "SunJCE", etc. Default in broker is null @@ -540,6 +543,9 @@ spec: sslSecret: description: Name of the secret to use for ssl information type: string + trustSecret: + description: The name of the truststore secret. + type: string trustStoreProvider: description: Provider used for the truststore; "SUN", "SunJCE", etc. Default in broker is null @@ -599,6 +605,9 @@ spec: sslSecret: description: Name of the secret to use for ssl information type: string + trustSecret: + description: The name of the truststore secret. + type: string useClientAuth: description: If the embedded server requires client authentication type: boolean diff --git a/config/manifests/bases/activemq-artemis-operator.clusterserviceversion.yaml b/config/manifests/bases/activemq-artemis-operator.clusterserviceversion.yaml index 6c1a8c6b2..3af09227e 100644 --- a/config/manifests/bases/activemq-artemis-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/activemq-artemis-operator.clusterserviceversion.yaml @@ -372,6 +372,11 @@ spec: path: acceptors[0].suppressInternalManagementObjects x-descriptors: - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: The name of the truststore secret. + displayName: Trust Secret + path: acceptors[0].trustSecret + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:text - description: Provider used for the truststore; "SUN", "SunJCE", etc. Default in broker is null displayName: TrustStore Provider @@ -885,6 +890,11 @@ spec: path: connectors[0].sslSecret x-descriptors: - urn:alm:descriptor:com.tectonic.ui:text + - description: The name of the truststore secret. + displayName: Trust Secret + path: connectors[0].trustSecret + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:text - description: Provider used for the truststore; "SUN", "SunJCE", etc. Default in broker is null displayName: TrustStore Provider @@ -954,6 +964,11 @@ spec: path: console.sslSecret x-descriptors: - urn:alm:descriptor:com.tectonic.ui:text + - description: The name of the truststore secret. + displayName: Trust Secret + path: console.trustSecret + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:text - description: If the embedded server requires client authentication displayName: Use Client Auth path: console.useClientAuth diff --git a/controllers/activemqartemis_controller.go b/controllers/activemqartemis_controller.go index a6b71da34..244177542 100644 --- a/controllers/activemqartemis_controller.go +++ b/controllers/activemqartemis_controller.go @@ -37,6 +37,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" "github.com/artemiscloud/activemq-artemis-operator/pkg/resources" + "github.com/artemiscloud/activemq-artemis-operator/pkg/utils/certutil" "github.com/artemiscloud/activemq-artemis-operator/pkg/utils/namer" "github.com/go-logr/logr" "github.com/pkg/errors" @@ -383,6 +384,9 @@ func validateSSLEnabledSecrets(customResource *brokerv1beta1.ActiveMQArtemis, cl if customResource.Spec.Console.SSLEnabled { secretName := namer.SecretsConsoleNameBuilder.Name() + if customResource.Spec.Console.SSLSecret != "" { + secretName = customResource.Spec.Console.SSLSecret + } secret := corev1.Secret{} found := retrieveResource(secretName, customResource.Namespace, &secret, client) @@ -561,6 +565,18 @@ func AssertConfigMapContainsKey(configMap corev1.ConfigMap, key string, contextM } func AssertSecretContainsKey(secret corev1.Secret, key string, contextMessage string) *metav1.Condition { + isCertSecret, isValid := certutil.IsSecretFromCert(&secret) + if isCertSecret { + if isValid { + return nil + } + return &metav1.Condition{ + Type: brokerv1beta1.ValidConditionType, + Status: metav1.ConditionFalse, + Reason: brokerv1beta1.ValidConditionInvalidCertSecretReason, + Message: fmt.Sprintf("%s certificate secret %s not valid, must have keys ca.crt tls.crt tls.key", contextMessage, secret.Name), + } + } if _, present := secret.Data[key]; !present { return &metav1.Condition{ Type: brokerv1beta1.ValidConditionType, @@ -573,6 +589,18 @@ func AssertSecretContainsKey(secret corev1.Secret, key string, contextMessage st } func AssertSecretContainsOneOf(secret corev1.Secret, keys []string, contextMessage string) *metav1.Condition { + ok, valid := certutil.IsSecretFromCert(&secret) + if ok { + if valid { + return nil + } + return &metav1.Condition{ + Type: brokerv1beta1.ValidConditionType, + Status: metav1.ConditionFalse, + Reason: brokerv1beta1.ValidConditionInvalidCertSecretReason, + Message: fmt.Sprintf("%s secret %s must contain keys %v", contextMessage, secret.Name, "ca.crt,tls.crt,tls.key"), + } + } for _, key := range keys { _, present := secret.Data[key] if present { @@ -627,6 +655,7 @@ func MakeNamers(customResource *brokerv1beta1.ActiveMQArtemis) *common.Namers { newNamers.SecretsConsoleNameBuilder.Prefix(customResource.Name).Base("console").Suffix("secret").Generate() } newNamers.SecretsNettyNameBuilder.Prefix(customResource.Name).Base("netty").Suffix("secret").Generate() + newNamers.LabelBuilder.Base(customResource.Name).Suffix("app").Generate() return &newNamers diff --git a/controllers/activemqartemis_controller_cert_manager_test.go b/controllers/activemqartemis_controller_cert_manager_test.go new file mode 100644 index 000000000..9b1119b95 --- /dev/null +++ b/controllers/activemqartemis_controller_cert_manager_test.go @@ -0,0 +1,335 @@ +/* +Licensed 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. +*/ +// +kubebuilder:docs-gen:collapse=Apache License + +package controllers + +import ( + "encoding/json" + "os" + + brokerv1beta1 "github.com/artemiscloud/activemq-artemis-operator/api/v1beta1" + "github.com/artemiscloud/activemq-artemis-operator/pkg/resources" + "github.com/artemiscloud/activemq-artemis-operator/pkg/utils/certutil" + "github.com/artemiscloud/activemq-artemis-operator/pkg/utils/common" + cmv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" + cmmetav1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +const ( + brokerCrNameBase = "broker-cert-mgr" + + rootIssuerName = "root-issuer" + rootCertName = "root-cert" + rootCertNamespce = "cert-manager" + rootCertSecretName = "artemis-root-cert-secret" + caIssuerName = "broker-ca-issuer" + caPemTrustStoreName = "ca-truststore.pem" + caTrustStorePassword = "changeit" +) + +var ( + serverCert = "server-cert" + rootIssuer = &cmv1.ClusterIssuer{} + rootCert = &cmv1.Certificate{} + caIssuer = &cmv1.ClusterIssuer{} + caBundleName = "ca-bundle" +) + +type ConnectorConfig struct { + Name string + FactoryClassName string + Params map[string]string +} + +var _ = Describe("artemis controller with cert manager test", Label("controller-cert-mgr-test"), func() { + var installedCertManager bool = false + + BeforeEach(func() { + if os.Getenv("USE_EXISTING_CLUSTER") == "true" { + //if cert manager/trust manager is not installed, install it + if !CertManagerInstalled() { + Expect(InstallCertManager()).To(Succeed()) + installedCertManager = true + } + + rootIssuer = InstallClusteredIssuer(rootIssuerName, nil) + + rootCert = InstallCert(rootCertName, rootCertNamespce, func(candidate *cmv1.Certificate) { + candidate.Spec.IsCA = true + candidate.Spec.CommonName = "artemis.root.ca" + candidate.Spec.SecretName = rootCertSecretName + candidate.Spec.IssuerRef = cmmetav1.ObjectReference{ + Name: rootIssuer.Name, + Kind: "ClusterIssuer", + } + }) + + caIssuer = InstallClusteredIssuer(caIssuerName, func(candidate *cmv1.ClusterIssuer) { + candidate.Spec.SelfSigned = nil + candidate.Spec.CA = &cmv1.CAIssuer{ + SecretName: rootCertSecretName, + } + }) + InstallCaBundle(caBundleName, rootCertSecretName, caPemTrustStoreName) + } + }) + + AfterEach(func() { + if os.Getenv("USE_EXISTING_CLUSTER") == "true" { + UnInstallCaBundle(caBundleName) + UninstallClusteredIssuer(caIssuerName) + UninstallCert(rootCert.Name, rootCert.Namespace) + UninstallClusteredIssuer(rootIssuerName) + + if installedCertManager { + Expect(UninstallCertManager()).To(Succeed()) + installedCertManager = false + } + } + }) + + Context("tls exposure with cert manager", func() { + BeforeEach(func() { + if os.Getenv("USE_EXISTING_CLUSTER") == "true" { + InstallCert(serverCert, defaultNamespace, func(candidate *cmv1.Certificate) { + candidate.Spec.DNSNames = []string{brokerCrNameBase + "0-ss-0", brokerCrNameBase + "1-ss-0", brokerCrNameBase + "2-ss-0"} + candidate.Spec.IssuerRef = cmmetav1.ObjectReference{ + Name: caIssuer.Name, + Kind: "ClusterIssuer", + } + }) + } + }) + AfterEach(func() { + if os.Getenv("USE_EXISTING_CLUSTER") == "true" { + UninstallCert(serverCert, defaultNamespace) + } + }) + It("test configured with cert and ca bundle", func() { + if os.Getenv("USE_EXISTING_CLUSTER") == "true" { + testConfiguredWithCertAndBundle(serverCert+"-secret", caBundleName) + } + }) + It("test ssl args with keystore secrets only", func() { + if os.Getenv("USE_EXISTING_CLUSTER") == "true" { + certKey := types.NamespacedName{Name: serverCert + "-secret", Namespace: defaultNamespace} + certSecret := corev1.Secret{} + Expect(resources.Retrieve(certKey, k8sClient, &certSecret)).To(Succeed()) + sslArgs, err := certutil.GetSslArgumentsFromSecret(&certSecret, "any", nil, false) + Expect(err).To(Succeed()) + sslFlags := sslArgs.ToFlags() + Expect(sslFlags).To(Not(ContainSubstring("trust"))) + sslArgs, err = certutil.GetSslArgumentsFromSecret(&certSecret, "any", nil, true) + + Expect(err).To(Succeed()) + sslFlags = sslArgs.ToFlags() + Expect(sslFlags).To(Not(ContainSubstring("trust"))) + sslProps := sslArgs.ToSystemProperties() + Expect(sslProps).To(Not(ContainSubstring("trust"))) + Expect(sslProps).To(Not(ContainSubstring("trust"))) + } + }) + }) + Context("certutil functions", Label("check-cert-secret"), func() { + It("certutil - is secret from cert", func() { + secret := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mysecret", + }, + Data: map[string][]byte{ + "tls.crt": []byte("some cert"), + }, + } + ok, valid := certutil.IsSecretFromCert(&secret) + Expect(ok).To(BeFalse()) + Expect(valid).To(BeFalse()) + + secret.ObjectMeta.Annotations = map[string]string{ + certutil.Cert_annotation_key: "caissuer", + } + ok, valid = certutil.IsSecretFromCert(&secret) + Expect(ok).To(BeTrue()) + Expect(valid).To(BeFalse()) + + secret.Data["tls.key"] = []byte("somekey") + ok, valid = certutil.IsSecretFromCert(&secret) + Expect(ok).To(BeTrue()) + Expect(valid).To(BeTrue()) + }) + }) +}) + +func getConnectorConfig(podName string, crName string, connectorName string, g Gomega) map[string]string { + curlUrl := "http://" + podName + ":8161/console/jolokia/read/org.apache.activemq.artemis:broker=\"amq-broker\"/ConnectorsAsJSON" + command := []string{"curl", "-k", "-s", "-u", "testuser:testpassword", curlUrl} + + result := ExecOnPod(podName, crName, defaultNamespace, command, g) + + var rootMap map[string]any + g.Expect(json.Unmarshal([]byte(result), &rootMap)).To(Succeed()) + + rootMapValue := rootMap["value"] + g.Expect(rootMapValue).ShouldNot(BeNil()) + connectors := rootMapValue.(string) + + var listOfConnectors []ConnectorConfig + g.Expect(json.Unmarshal([]byte(connectors), &listOfConnectors)) + + for _, v := range listOfConnectors { + if v.Name == connectorName { + return v.Params + } + } + return nil +} + +func checkReadPodStatus(podName string, crName string, g Gomega) { + curlUrl := "https://" + podName + ":8161/console/jolokia/read/org.apache.activemq.artemis:broker=\"amq-broker\"/Status" + command := []string{"curl", "-k", "-s", "-u", "testuser:testpassword", curlUrl} + + result := ExecOnPod(podName, crName, defaultNamespace, command, g) + var rootMap map[string]any + g.Expect(json.Unmarshal([]byte(result), &rootMap)).To(Succeed()) + value := rootMap["value"].(string) + var valueMap map[string]any + g.Expect(json.Unmarshal([]byte(value), &valueMap)).To(Succeed()) + serverInfo := valueMap["server"].(map[string]any) + serverState := serverInfo["state"].(string) + g.Expect(serverState).To(Equal("STARTED")) +} + +func checkMessagingInPod(podName string, crName string, portNumber string, trustStoreLoc string, g Gomega) { + tcpUrl := "tcp://" + podName + ":" + portNumber + "?sslEnabled=true&trustStorePath=" + trustStoreLoc + "&trustStoreType=PEM" + sendCommand := []string{"amq-broker/bin/artemis", "producer", "--user", "testuser", "--password", "testpassword", "--url", tcpUrl, "--message-count", "1", "--destination", "queue://DLQ", "--verbose"} + result := ExecOnPod(podName, crName, defaultNamespace, sendCommand, g) + g.Expect(result).To(ContainSubstring("Produced: 1 messages")) + receiveCommand := []string{"amq-broker/bin/artemis", "consumer", "--user", "testuser", "--password", "testpassword", "--url", tcpUrl, "--message-count", "1", "--destination", "queue://DLQ", "--verbose"} + result = ExecOnPod(podName, crName, defaultNamespace, receiveCommand, g) + g.Expect(result).To(ContainSubstring("Consumed: 1 messages")) +} + +func testConfiguredWithCertAndBundle(certSecret string, caSecret string) { + // it should use PEM store type + By("Deploying the broker cr") + brokerCrName := brokerCrNameBase + "0" + brokerCr, createdBrokerCr := DeployCustomBroker(defaultNamespace, func(candidate *brokerv1beta1.ActiveMQArtemis) { + + candidate.Name = brokerCrName + + candidate.Spec.DeploymentPlan.Size = common.Int32ToPtr(1) + candidate.Spec.DeploymentPlan.ReadinessProbe = &corev1.Probe{ + InitialDelaySeconds: 1, + PeriodSeconds: 1, + TimeoutSeconds: 5, + } + candidate.Spec.Console.Expose = true + candidate.Spec.Console.SSLEnabled = true + candidate.Spec.Console.UseClientAuth = false + candidate.Spec.Console.SSLSecret = certSecret + candidate.Spec.Console.TrustSecret = &caSecret + candidate.Spec.IngressDomain = defaultTestIngressDomain + }) + pod0Name := createdBrokerCr.Name + "-ss-0" + By("Checking the broker status reflect the truth") + Eventually(func(g Gomega) { + crdRef := types.NamespacedName{ + Namespace: brokerCr.Namespace, + Name: brokerCr.Name, + } + g.Expect(k8sClient.Get(ctx, crdRef, createdBrokerCr)).Should(Succeed()) + + condition := meta.FindStatusCondition(createdBrokerCr.Status.Conditions, brokerv1beta1.DeployedConditionType) + g.Expect(condition).NotTo(BeNil()) + g.Expect(condition.Status).Should(Equal(metav1.ConditionTrue)) + checkReadPodStatus(pod0Name, createdBrokerCr.Name, g) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + CleanResource(createdBrokerCr, brokerCr.Name, createdBrokerCr.Namespace) + + By("Deploying the broker cr exposing acceptor ssl and connector ssl") + brokerCrName = brokerCrNameBase + "1" + pod0Name = brokerCrName + "-ss-0" + brokerCr, createdBrokerCr = DeployCustomBroker(defaultNamespace, func(candidate *brokerv1beta1.ActiveMQArtemis) { + + candidate.Name = brokerCrName + candidate.Spec.DeploymentPlan.Size = common.Int32ToPtr(1) + candidate.Spec.IngressDomain = defaultTestIngressDomain + candidate.Spec.DeploymentPlan.ReadinessProbe = &corev1.Probe{ + InitialDelaySeconds: 1, + PeriodSeconds: 1, + TimeoutSeconds: 5, + } + candidate.Spec.Acceptors = []brokerv1beta1.AcceptorType{{ + Name: "new-acceptor", + Port: 62666, + Protocols: "all", + Expose: true, + SSLEnabled: true, + SSLSecret: certSecret, + TrustSecret: &caSecret, + }} + candidate.Spec.Connectors = []brokerv1beta1.ConnectorType{{ + Name: "new-connector", + Host: pod0Name, + Port: 62666, + Expose: true, + SSLEnabled: true, + SSLSecret: certSecret, + TrustSecret: &caSecret, + }} + }) + + crdRef := types.NamespacedName{ + Namespace: brokerCr.Namespace, + Name: brokerCr.Name, + } + + By("checking the broker status reflect the truth") + + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, crdRef, createdBrokerCr)).Should(Succeed()) + + condition := meta.FindStatusCondition(createdBrokerCr.Status.Conditions, brokerv1beta1.DeployedConditionType) + g.Expect(condition).NotTo(BeNil()) + g.Expect(condition.Status).Should(Equal(metav1.ConditionTrue)) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + By("checking the broker message send and receive") + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, crdRef, createdBrokerCr)).Should(Succeed()) + checkMessagingInPod(pod0Name, createdBrokerCr.Name, "62666", "/etc/"+caBundleName+"-volume/"+caPemTrustStoreName, g) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + By("checking connector parameters") + Eventually(func(g Gomega) { + connectorCfg := getConnectorConfig(pod0Name, createdBrokerCr.Name, "new-connector", g) + g.Expect(connectorCfg).NotTo(BeNil()) + g.Expect(connectorCfg["keyStoreType"]).To(Equal("PEMCFG")) + g.Expect(connectorCfg["port"]).To(Equal("62666")) + g.Expect(connectorCfg["sslEnabled"]).To(Equal("true")) + g.Expect(connectorCfg["host"]).To(Equal(pod0Name)) + g.Expect(connectorCfg["trustStorePath"]).To(Equal("/etc/" + caBundleName + "-volume/" + caPemTrustStoreName)) + g.Expect(connectorCfg["trustStoreType"]).To(Equal("PEM")) + g.Expect(connectorCfg["keyStorePath"]).To(Equal("/etc/secret-server-cert-secret-pemcfg/" + certSecret + ".pemcfg")) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + CleanResource(createdBrokerCr, brokerCr.Name, createdBrokerCr.Namespace) +} diff --git a/controllers/activemqartemis_controller_test.go b/controllers/activemqartemis_controller_test.go index ab380a4df..37d13b872 100644 --- a/controllers/activemqartemis_controller_test.go +++ b/controllers/activemqartemis_controller_test.go @@ -3728,6 +3728,10 @@ var _ = Describe("artemis controller", func() { reconcilerImpl := NewActiveMQArtemisReconcilerImpl(&crd, ctrl.Log, k8sClient.Scheme()) defaultConsoleSecretName := crd.Name + "-console-secret" + tlsSecret, err := CreateTlsSecret(defaultConsoleSecretName, defaultNamespace, "password", nil) + Expect(err).To(BeNil()) + Expect(k8sClient.Create(ctx, tlsSecret)).To(Succeed()) + currentSS := &appsv1.StatefulSet{} currentSS.Name = namer.CrToSS(crd.Name) currentSS.Namespace = defaultNamespace @@ -3749,7 +3753,7 @@ var _ = Describe("artemis controller", func() { foundInternalSecret := false defaultSecretPath := "/etc/" + defaultConsoleSecretName + "-volume/" defaultSslArgs := " --ssl-key " + defaultSecretPath + "broker.ks --ssl-key-password password --ssl-trust " + defaultSecretPath + "client.ts --ssl-trust-password password" - for _, reqres := range reconcilerImpl.requestedResources { + for _, reqres := range common.ToResourceList(reconcilerImpl.requestedResources) { if reqres.GetObjectKind().GroupVersionKind().Kind == "Secret" { secret := reqres.(*corev1.Secret) if secret.Name == secretName { @@ -3779,6 +3783,7 @@ var _ = Describe("artemis controller", func() { } Expect(foundSecretRef).To(BeTrue()) Expect(foundSecretKey).To(BeTrue()) + CleanResource(tlsSecret, tlsSecret.Name, tlsSecret.Namespace) }) It("reconcile verify internal secret owner ref", func() { @@ -3808,6 +3813,11 @@ var _ = Describe("artemis controller", func() { reconcilerImpl := NewActiveMQArtemisReconcilerImpl(&crd, ctrl.Log, k8sClient.Scheme()) namer := MakeNamers(&crd) defaultConsoleSecretName := crd.Name + "-console-secret" + + tlsSecret, err := CreateTlsSecret(defaultConsoleSecretName, defaultNamespace, "password", nil) + Expect(err).To(BeNil()) + Expect(k8sClient.Create(ctx, tlsSecret)).To(Succeed()) + internalSecretName := defaultConsoleSecretName + "-internal" internalConsoleSecretKey := types.NamespacedName{Namespace: crd.Namespace, Name: internalSecretName} @@ -3911,6 +3921,10 @@ var _ = Describe("artemis controller", func() { internalSecretName := defaultConsoleSecretName + "-internal" internalConsoleSecretKey := types.NamespacedName{Namespace: crd.Namespace, Name: internalSecretName} + tlsSecret, err := CreateTlsSecret(defaultConsoleSecretName, defaultNamespace, "password", nil) + Expect(err).To(BeNil()) + Expect(k8sClient.Create(ctx, tlsSecret)).To(Succeed()) + By("making abandoned internal secret") createdInternalSecret := &corev1.Secret{} createdInternalSecret.Name = internalConsoleSecretKey.Name @@ -3947,6 +3961,7 @@ var _ = Describe("artemis controller", func() { By("cleanup") k8sClient.Delete(ctx, createdInternalSecret) k8sClient.Delete(ctx, createdCrd) + CleanResource(tlsSecret, tlsSecret.Name, tlsSecret.Namespace) }) }) @@ -5941,7 +5956,7 @@ var _ = Describe("artemis controller", func() { // it requires the key to be logging.properties loggingData["logging-configuration"] = "someproperty=somevalue" loggingSecretKey := types.NamespacedName{Name: loggingSecretName, Namespace: defaultNamespace} - loggingSecret := secrets.NewSecret(loggingSecretKey, loggingSecretName, loggingData, nil) + loggingSecret := secrets.NewSecret(loggingSecretKey, loggingData, nil) Eventually(func() bool { err := k8sClient.Create(ctx, loggingSecret, &client.CreateOptions{}) return err == nil @@ -6104,7 +6119,7 @@ var _ = Describe("artemis controller", func() { loggingData := make(map[string]string) loggingData[LoggingConfigKey] = "someproperty=somevalue" - secret := secrets.NewSecret(types.NamespacedName{Name: loggingSecretName, Namespace: defaultNamespace}, loggingSecretName, loggingData, nil) + secret := secrets.NewSecret(types.NamespacedName{Name: loggingSecretName, Namespace: defaultNamespace}, loggingData, nil) Eventually(func() bool { err := k8sClient.Create(ctx, secret, &client.CreateOptions{}) return err == nil @@ -6357,8 +6372,8 @@ var _ = Describe("artemis controller", func() { }, timeout, interval).Should(Succeed()) // cleanup - Expect(k8sClient.Delete(ctx, createdCrd)).Should(Succeed()) - Expect(k8sClient.Delete(ctx, createdAddressCr)).Should(Succeed()) + CleanResource(createdCrd, createdCrd.Name, createdCrd.Namespace) + CleanResource(createdAddressCr, createdAddressCr.Name, createdAddressCr.Namespace) }) }) @@ -6870,6 +6885,16 @@ var _ = Describe("artemis controller", func() { } By("updating with expose=true and tls") + sslSecretName := crd.Name + "-" + crd.Spec.Acceptors[0].Name + "-secret" + sslSecret, err := CreateTlsSecret(sslSecretName, defaultNamespace, "password", nil) + Expect(err).To(BeNil()) + Expect(k8sClient.Create(ctx, sslSecret)).Should(Succeed()) + + sslSecretName1 := crd.Name + "-" + crd.Spec.Connectors[0].Name + "-secret" + sslSecret1, err := CreateTlsSecret(sslSecretName1, defaultNamespace, "password", nil) + Expect(err).To(BeNil()) + Expect(k8sClient.Create(ctx, sslSecret1)).Should(Succeed()) + Eventually(func(g Gomega) { key := types.NamespacedName{Name: crd.Name, Namespace: defaultNamespace} g.Expect(k8sClient.Get(ctx, key, &crd)).Should(Succeed()) @@ -6922,6 +6947,8 @@ var _ = Describe("artemis controller", func() { } CleanResource(&crd, crd.Name, defaultNamespace) + CleanResource(sslSecret, sslSecret.Name, defaultNamespace) + CleanResource(sslSecret1, sslSecret1.Name, defaultNamespace) } }) }) @@ -7233,14 +7260,19 @@ var _ = Describe("artemis controller", func() { Size: common.Int32ToPtr(1), } keyStoreProvider := "SunJCE" + acceptorName := "new-acceptor" cr.Spec.Acceptors = []brokerv1beta1.AcceptorType{ { - Name: "new-acceptor", + Name: acceptorName, Port: 61666, SSLEnabled: true, KeyStoreProvider: keyStoreProvider, }, } + sslSecretName := cr.Name + "-" + acceptorName + "-secret" + sslSecret, err := CreateTlsSecret(sslSecretName, defaultNamespace, "password", nil) + Expect(err).To(BeNil()) + Expect(k8sClient.Create(ctx, sslSecret)).Should(Succeed()) Expect(k8sClient.Create(ctx, &cr)).Should(Succeed()) ssNamespacedName := types.NamespacedName{Name: namer.CrToSS(cr.Name), Namespace: defaultNamespace} @@ -7271,6 +7303,7 @@ var _ = Describe("artemis controller", func() { }, timeout, interval).Should(Succeed()) CleanResource(&cr, cr.Name, defaultNamespace) + CleanResource(sslSecret, sslSecret.Name, defaultNamespace) }) It("Testing acceptor trustStoreType being set and unset", func() { @@ -7282,6 +7315,7 @@ var _ = Describe("artemis controller", func() { Size: common.Int32ToPtr(1), } trustStoreType := "JCEKS" + acceptorName := "new-acceptor" cr.Spec.Acceptors = []brokerv1beta1.AcceptorType{ { Name: "new-acceptor", @@ -7290,6 +7324,10 @@ var _ = Describe("artemis controller", func() { TrustStoreType: trustStoreType, }, } + sslSecretName := cr.Name + "-" + acceptorName + "-secret" + sslSecret, err := CreateTlsSecret(sslSecretName, defaultNamespace, "password", nil) + Expect(err).To(BeNil()) + Expect(k8sClient.Create(ctx, sslSecret)).Should(Succeed()) Expect(k8sClient.Create(ctx, &cr)).Should(Succeed()) ssResourceVersionWithSslEnabled := "" @@ -7373,6 +7411,7 @@ var _ = Describe("artemis controller", func() { }, timeout, interval).Should(Succeed()) CleanResource(&cr, cr.Name, defaultNamespace) + CleanResource(sslSecret, sslSecret.Name, defaultNamespace) }) It("Testing acceptor trustStoreProvider being set", func() { @@ -7384,6 +7423,7 @@ var _ = Describe("artemis controller", func() { Size: common.Int32ToPtr(1), } trustStoreProvider := "SUN" + acceptorName := "new-acceptor" cr.Spec.Acceptors = []brokerv1beta1.AcceptorType{ { Name: "new-acceptor", @@ -7392,6 +7432,11 @@ var _ = Describe("artemis controller", func() { TrustStoreProvider: trustStoreProvider, }, } + sslSecretName := cr.Name + "-" + acceptorName + "-secret" + sslSecret, err := CreateTlsSecret(sslSecretName, defaultNamespace, "password", nil) + Expect(err).To(BeNil()) + Expect(k8sClient.Create(ctx, sslSecret)).Should(Succeed()) + Expect(k8sClient.Create(ctx, &cr)).Should(Succeed()) ssNamespacedName := types.NamespacedName{Name: namer.CrToSS(cr.Name), Namespace: defaultNamespace} @@ -7421,6 +7466,7 @@ var _ = Describe("artemis controller", func() { }, timeout, interval).Should(Succeed()) CleanResource(&cr, cr.Name, defaultNamespace) + CleanResource(sslSecret, sslSecret.Name, defaultNamespace) }) }) diff --git a/controllers/activemqartemis_reconciler.go b/controllers/activemqartemis_reconciler.go index a157585cb..5e49e52cd 100644 --- a/controllers/activemqartemis_reconciler.go +++ b/controllers/activemqartemis_reconciler.go @@ -22,6 +22,7 @@ import ( "github.com/artemiscloud/activemq-artemis-operator/pkg/resources/secrets" "github.com/artemiscloud/activemq-artemis-operator/pkg/resources/serviceports" ss "github.com/artemiscloud/activemq-artemis-operator/pkg/resources/statefulsets" + "github.com/artemiscloud/activemq-artemis-operator/pkg/utils/certutil" "github.com/artemiscloud/activemq-artemis-operator/pkg/utils/common" "github.com/artemiscloud/activemq-artemis-operator/pkg/utils/cr2jinja2" "github.com/artemiscloud/activemq-artemis-operator/pkg/utils/jolokia_client" @@ -99,7 +100,7 @@ var defApplyRule string = "merge_all" var yacfgProfileVersion = version.YacfgProfileVersionFromFullVersion[version.LatestVersion] type ActiveMQArtemisReconcilerImpl struct { - requestedResources []rtclient.Object + requestedResources map[reflect.Type]map[string]rtclient.Object deployed map[reflect.Type][]rtclient.Object log logr.Logger customResource *brokerv1beta1.ActiveMQArtemis @@ -108,9 +109,10 @@ type ActiveMQArtemisReconcilerImpl struct { func NewActiveMQArtemisReconcilerImpl(customResource *brokerv1beta1.ActiveMQArtemis, logger logr.Logger, schemeArg *runtime.Scheme) *ActiveMQArtemisReconcilerImpl { return &ActiveMQArtemisReconcilerImpl{ - log: logger, - customResource: customResource, - scheme: schemeArg, + log: logger, + customResource: customResource, + scheme: schemeArg, + requestedResources: make(map[reflect.Type]map[string]rtclient.Object), } } @@ -121,8 +123,8 @@ type ValueInfo struct { } type ActiveMQArtemisIReconciler interface { - Process(customResource *brokerv1beta1.ActiveMQArtemis, client rtclient.Client, scheme *runtime.Scheme, firstTime bool) uint32 - ProcessStatefulSet(customResource *brokerv1beta1.ActiveMQArtemis, client rtclient.Client, log logr.Logger, firstTime bool) (*appsv1.StatefulSet, bool) + Process(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers, client rtclient.Client, scheme *runtime.Scheme) error + ProcessStatefulSet(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers, client rtclient.Client, log logr.Logger) (*appsv1.StatefulSet, error) ProcessCredentials(customResource *brokerv1beta1.ActiveMQArtemis, client rtclient.Client, scheme *runtime.Scheme, currentStatefulSet *appsv1.StatefulSet) uint32 ProcessDeploymentPlan(customResource *brokerv1beta1.ActiveMQArtemis, client rtclient.Client, scheme *runtime.Scheme, currentStatefulSet *appsv1.StatefulSet, firstTime bool) uint32 ProcessAcceptorsAndConnectors(customResource *brokerv1beta1.ActiveMQArtemis, client rtclient.Client, scheme *runtime.Scheme, currentStatefulSet *appsv1.StatefulSet) uint32 @@ -151,13 +153,23 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) Process(customResource *brokerv reconciler.ProcessCredentials(customResource, namer, client, scheme, desiredStatefulSet) - reconciler.ProcessAcceptorsAndConnectors(customResource, namer, client, scheme, desiredStatefulSet) + err = reconciler.ProcessAcceptorsAndConnectors(customResource, namer, client, scheme, desiredStatefulSet) - reconciler.ProcessConsole(customResource, namer, client, scheme, desiredStatefulSet) + if err != nil { + reconciler.log.Error(err, "error processing acceptors and connectors") + return err + } + + err = reconciler.ProcessConsole(customResource, namer, client, scheme, desiredStatefulSet) + + if err != nil { + reconciler.log.Error(err, "Error processing console") + return err + } // mods to env var values sourced from secrets are not detected by process resources // track updates in trigger env var that has a total checksum - trackSecretCheckSumInEnvVar(reconciler.requestedResources, desiredStatefulSet.Spec.Template.Spec.Containers) + trackSecretCheckSumInEnvVar(common.ToResourceList(reconciler.requestedResources), desiredStatefulSet.Spec.Template.Spec.Containers) reconciler.trackDesired(desiredStatefulSet) @@ -169,7 +181,7 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) Process(customResource *brokerv } //empty the collected objects - reconciler.requestedResources = nil + reconciler.requestedResources = make(map[reflect.Type]map[string]rtclient.Object) reconciler.log.V(1).Info("Reconciler Processing... complete", "CRD ver:", customResource.ObjectMeta.ResourceVersion, "CRD Gen:", customResource.ObjectMeta.Generation) @@ -383,10 +395,16 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) applyPodDisruptionBudget(custom reconciler.trackDesired(desired) } -func (reconciler *ActiveMQArtemisReconcilerImpl) ProcessAcceptorsAndConnectors(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers, client rtclient.Client, scheme *runtime.Scheme, currentStatefulSet *appsv1.StatefulSet) { +func (reconciler *ActiveMQArtemisReconcilerImpl) ProcessAcceptorsAndConnectors(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers, client rtclient.Client, scheme *runtime.Scheme, currentStatefulSet *appsv1.StatefulSet) error { - acceptorEntry := generateAcceptorsString(customResource, namer, client) - connectorEntry := generateConnectorsString(customResource, namer, client) + acceptorEntry, err := reconciler.generateAcceptorsString(customResource, client, currentStatefulSet) + if err != nil { + return err + } + connectorEntry, err := reconciler.generateConnectorsString(customResource, client, currentStatefulSet) + if err != nil { + return err + } reconciler.configureAcceptorsExposure(customResource, namer, client) reconciler.configureConnectorsExposure(customResource, namer, client) @@ -405,24 +423,54 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) ProcessAcceptorsAndConnectors(c secretName := namer.SecretsNettyNameBuilder.Name() reconciler.sourceEnvVarFromSecret(customResource, namer, currentStatefulSet, &envVars, secretName, client) + return nil } -func (reconciler *ActiveMQArtemisReconcilerImpl) ProcessConsole(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers, client rtclient.Client, scheme *runtime.Scheme, currentStatefulSet *appsv1.StatefulSet) { +func (reconciler *ActiveMQArtemisReconcilerImpl) ProcessConsole(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers, client rtclient.Client, scheme *runtime.Scheme, currentStatefulSet *appsv1.StatefulSet) error { reconciler.configureConsoleExposure(customResource, namer, client) if !customResource.Spec.Console.SSLEnabled { - return + return nil } + var err error + secretName := namer.SecretsConsoleNameBuilder.Name() + if customResource.Spec.Console.SSLSecret != "" { + secretName = customResource.Spec.Console.SSLSecret + } + sslArgs, sslFlags, err := reconciler.generateCommonSSLFlags(customResource, secretName, customResource.Spec.Console.TrustSecret, "", client, true) + if err != nil { + return err + } - envVars := map[string]ValueInfo{"AMQ_CONSOLE_ARGS": { - Value: generateConsoleSSLFlags(customResource, namer, client, secretName), - AutoGen: true, - Internal: true, - }} + if customResource.Spec.Console.UseClientAuth { + sslFlags = sslFlags + " --use-client-auth" + } - reconciler.sourceEnvVarFromSecret(customResource, namer, currentStatefulSet, &envVars, secretName, client) + if len(sslArgs.PemCfgs) > 2 { + reconciler.addPemConfigFileSecret(currentStatefulSet, sslArgs.PemCfgs) + reconciler.appendSystemPropertiesForConsole(currentStatefulSet, sslArgs.ToSystemProperties()) + } else { + envVars := map[string]ValueInfo{"AMQ_CONSOLE_ARGS": { + Value: sslFlags, + AutoGen: true, + Internal: true, + }} + + reconciler.sourceEnvVarFromSecret(customResource, namer, currentStatefulSet, &envVars, secretName, client) + } + + return nil +} + +func (r *ActiveMQArtemisReconcilerImpl) appendSystemPropertiesForConsole(currentSS *appsv1.StatefulSet, systemArgs string) { + r.log.V(1).Info("Appending console system prop", "value", systemArgs) + consoleProps := corev1.EnvVar{ + Name: "JAVA_ARGS_APPEND", + Value: systemArgs, + } + environments.CreateOrAppend(currentSS.Spec.Template.Spec.Containers, &consoleProps) } func (r *ActiveMQArtemisReconcilerImpl) syncMessageMigration(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers, client rtclient.Client, scheme *runtime.Scheme) { @@ -520,9 +568,9 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) sourceEnvVarFromSecret(customRe } } - secretDefinition := secrets.NewSecret(namespacedName, secretName, stringDataMap, namer.LabelBuilder.Labels()) - desired := false + secretDefinition := secrets.NewSecret(namespacedName, stringDataMap, namer.LabelBuilder.Labels()) + if err := resources.Retrieve(namespacedName, client, secretDefinition); err != nil { if k8serrors.IsNotFound(err) { log.V(1).Info("Did not find secret", "key", namespacedName) @@ -564,6 +612,11 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) sourceEnvVarFromSecret(customRe } internalSecretName := secretName + "-internal" + internalNamespacedName := types.NamespacedName{ + Name: internalSecretName, + Namespace: currentStatefulSet.Namespace, + } + if len(internalStringDataMap) > 0 { var internalSecretDefinition *corev1.Secret @@ -572,7 +625,7 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) sourceEnvVarFromSecret(customRe log.V(2).Info("updating from " + internalSecretName) internalSecretDefinition = obj.(*corev1.Secret) } else { - internalSecretDefinition = secrets.NewSecret(namespacedName, internalSecretName, internalStringDataMap, namer.LabelBuilder.Labels()) + internalSecretDefinition = secrets.NewSecret(internalNamespacedName, internalStringDataMap, namer.LabelBuilder.Labels()) reconciler.adoptExistingSecretWithNoOwnerRefForUpdate(customResource, internalSecretDefinition, client) } @@ -628,7 +681,33 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) sourceEnvVarFromSecret(customRe } } -func generateAcceptorsString(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers, client rtclient.Client) string { +func (reconciler *ActiveMQArtemisReconcilerImpl) processSSLSecret(secretName string, customResource *brokerv1beta1.ActiveMQArtemis, client rtclient.Client) (*corev1.Secret, error) { + + var trackedSecret *corev1.Secret = nil + + // validate if secret exists + secretKey := types.NamespacedName{Name: secretName, Namespace: customResource.Namespace} + theSecret := &corev1.Secret{} + if err := resources.Retrieve(secretKey, client, theSecret); err != nil { + if !k8serrors.IsNotFound(err) { + return nil, err + } + return nil, fmt.Errorf("required secret not exist %s", secretName) + } + + trackedSecret = theSecret + //if operator doesn't own it, don't track + if len(trackedSecret.OwnerReferences) > 0 { + for _, or := range trackedSecret.OwnerReferences { + if or.Kind == "ActiveMQArtemis" && or.Name == customResource.Name { + reconciler.trackDesired(trackedSecret) + } + } + } + return trackedSecret, nil +} + +func (reconciler *ActiveMQArtemisReconcilerImpl) generateAcceptorsString(customResource *brokerv1beta1.ActiveMQArtemis, client rtclient.Client, currentSS *appsv1.StatefulSet) (string, error) { // TODO: Optimize for the single broker configuration ensureCOREOn61616Exists := true // as clustered is no longer an option but true by default @@ -667,17 +746,34 @@ func generateAcceptorsString(customResource *brokerv1beta1.ActiveMQArtemis, name !strings.Contains(strings.ToUpper(acceptor.Protocols), "CORE") { acceptorEntry = acceptorEntry + ",CORE" } + if acceptor.SSLEnabled { secretName := customResource.Name + "-" + acceptor.Name + "-secret" if acceptor.SSLSecret != "" { secretName = acceptor.SSLSecret } - acceptorEntry = acceptorEntry + ";" + generateAcceptorConnectorSSLArguments(customResource, namer, client, secretName) - sslOptionalArguments := generateAcceptorSSLOptionalArguments(acceptor) + secretToUse, err := reconciler.processSSLSecret(secretName, customResource, client) + if err != nil { + return "", err + } + + sslArgs, sslFlags, err := reconciler.generateCommonSSLFlags(customResource, secretToUse.Name, acceptor.TrustSecret, acceptor.TrustStoreType, client, false) + if err != nil { + return "", err + } + acceptorEntry = acceptorEntry + ";" + sslFlags + + if len(sslArgs.PemCfgs) > 2 { + reconciler.addPemConfigFileSecret(currentSS, sslArgs.PemCfgs) + } + + sslOptionalArguments := reconciler.generateAcceptorSSLOptionalArguments(acceptor) + if sslOptionalArguments != "" { acceptorEntry = acceptorEntry + ";" + sslOptionalArguments } } + if acceptor.AnycastPrefix != "" { safeAnycastPrefix := strings.Replace(acceptor.AnycastPrefix, "/", "\\/", -1) acceptorEntry = acceptorEntry + ";" + "anycastPrefix=" + safeAnycastPrefix @@ -717,10 +813,10 @@ func generateAcceptorsString(customResource *brokerv1beta1.ActiveMQArtemis, name acceptorEntry = acceptorEntry + "<\\/acceptor>" } - return acceptorEntry + return acceptorEntry, nil } -func generateConnectorsString(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers, client rtclient.Client) string { +func (reconciler *ActiveMQArtemisReconcilerImpl) generateConnectorsString(customResource *brokerv1beta1.ActiveMQArtemis, client rtclient.Client, currentSS *appsv1.StatefulSet) (string, error) { connectorEntry := "" connectors := customResource.Spec.Connectors @@ -737,8 +833,24 @@ func generateConnectorsString(customResource *brokerv1beta1.ActiveMQArtemis, nam if connector.SSLSecret != "" { secretName = connector.SSLSecret } - connectorEntry = connectorEntry + ";" + generateAcceptorConnectorSSLArguments(customResource, namer, client, secretName) - sslOptionalArguments := generateConnectorSSLOptionalArguments(connector) + secretToUse, err := reconciler.processSSLSecret(secretName, customResource, client) + if err != nil { + return "", err + } + + sslArgs, sslOpts, err := reconciler.generateCommonSSLFlags(customResource, secretToUse.Name, connector.TrustSecret, connector.TrustStoreType, client, false) + + if err != nil { + return "", err + } + connectorEntry = connectorEntry + "?" + sslOpts + + if len(sslArgs.PemCfgs) > 2 { + reconciler.addPemConfigFileSecret(currentSS, sslArgs.PemCfgs) + } + + sslOptionalArguments := reconciler.generateConnectorSSLOptionalArguments(connector) + if sslOptionalArguments != "" { connectorEntry = connectorEntry + ";" + sslOptionalArguments } @@ -746,7 +858,7 @@ func generateConnectorsString(customResource *brokerv1beta1.ActiveMQArtemis, nam connectorEntry = connectorEntry + "<\\/connector>" } - return connectorEntry + return connectorEntry, nil } func (reconciler *ActiveMQArtemisReconcilerImpl) configureAcceptorsExposure(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers, client rtclient.Client) { @@ -820,7 +932,17 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) ExposureDefinitionForCR(customR } func (reconciler *ActiveMQArtemisReconcilerImpl) trackDesired(desired rtclient.Object) { - reconciler.requestedResources = append(reconciler.requestedResources, desired) + desiredType := reflect.TypeOf(desired) + if reconciler.requestedResources == nil { + reconciler.requestedResources = make(map[reflect.Type]map[string]rtclient.Object) + } + resMap, ok := reconciler.requestedResources[desiredType] + if !ok { + resMap = make(map[string]rtclient.Object) + reconciler.requestedResources[desiredType] = resMap + } + resName := desired.GetName() + resMap[resName] = desired } func (reconciler *ActiveMQArtemisReconcilerImpl) applyTemplates(desired rtclient.Object) (err error) { @@ -1104,95 +1226,45 @@ func formatTemplatedString(customResource *brokerv1beta1.ActiveMQArtemis, templa return template } -func generateConsoleSSLFlags(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers, client rtclient.Client, secretName string) string { +func (r *ActiveMQArtemisReconcilerImpl) generateCommonSSLFlags(customResource *brokerv1beta1.ActiveMQArtemis, secretName string, caSecretName *string, trustStoreType string, client rtclient.Client, isConsole bool) (*certutil.SslArguments, string, error) { - sslFlags := "" secretNamespacedName := types.NamespacedName{ Name: secretName, Namespace: customResource.Namespace, } - namespacedName := types.NamespacedName{ - Name: customResource.Name, - Namespace: customResource.Namespace, - } - stringDataMap := map[string]string{} - userPasswordSecret := secrets.NewSecret(namespacedName, secretName, stringDataMap, namer.LabelBuilder.Labels()) - keyStorePassword := "password" - keyStorePath := "/etc/" + secretName + "-volume/broker.ks" - trustStorePassword := "password" - trustStorePath := "/etc/" + secretName + "-volume/client.ts" - if err := resources.Retrieve(secretNamespacedName, client, userPasswordSecret); err == nil { - if string(userPasswordSecret.Data["keyStorePassword"]) != "" { - keyStorePassword = string(userPasswordSecret.Data["keyStorePassword"]) - } - if string(userPasswordSecret.Data["keyStorePath"]) != "" { - keyStorePath = string(userPasswordSecret.Data["keyStorePath"]) - } - if string(userPasswordSecret.Data["trustStorePassword"]) != "" { - trustStorePassword = string(userPasswordSecret.Data["trustStorePassword"]) - } - if string(userPasswordSecret.Data["trustStorePath"]) != "" { - trustStorePath = string(userPasswordSecret.Data["trustStorePath"]) - } - } - - sslFlags = sslFlags + " " + "--ssl-key" + " " + keyStorePath - sslFlags = sslFlags + " " + "--ssl-key-password" + " " + keyStorePassword - sslFlags = sslFlags + " " + "--ssl-trust" + " " + trustStorePath - sslFlags = sslFlags + " " + "--ssl-trust-password" + " " + trustStorePassword - if customResource.Spec.Console.UseClientAuth { - sslFlags = sslFlags + " " + "--use-client-auth" + sslSecret := &corev1.Secret{} + if err := resources.Retrieve(secretNamespacedName, client, sslSecret); err != nil { + return nil, "", err } - return sslFlags -} + // for trust manager ca bundle. + // in user secret case it can have both keystore and truststore so a separate ca bundle is optional + var caSecret *corev1.Secret = nil + if caSecretName != nil { + trustSecretNamespacedName := types.NamespacedName{ + Name: *caSecretName, + Namespace: customResource.Namespace, + } -func generateAcceptorConnectorSSLArguments(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers, client rtclient.Client, secretName string) string { + caSecret = &corev1.Secret{} - sslArguments := "sslEnabled=true" - secretNamespacedName := types.NamespacedName{ - Name: secretName, - Namespace: customResource.Namespace, - } - namespacedName := types.NamespacedName{ - Name: customResource.Name, - Namespace: customResource.Namespace, + if err := resources.Retrieve(trustSecretNamespacedName, client, caSecret); err != nil { + return nil, "", err + } } - stringDataMap := map[string]string{} - userPasswordSecret := secrets.NewSecret(namespacedName, secretName, stringDataMap, namer.LabelBuilder.Labels()) - keyStorePassword := "password" - keyStorePath := "\\/etc\\/" + secretName + "-volume\\/broker.ks" - trustStorePassword := "password" - trustStorePath := "\\/etc\\/" + secretName + "-volume\\/client.ts" - if err := resources.Retrieve(secretNamespacedName, client, userPasswordSecret); err == nil { - if string(userPasswordSecret.Data["keyStorePassword"]) != "" { - //noinspection GoUnresolvedReference - keyStorePassword = strings.ReplaceAll(string(userPasswordSecret.Data["keyStorePassword"]), "/", "\\/") - } - if string(userPasswordSecret.Data["keyStorePath"]) != "" { - //noinspection GoUnresolvedReference - keyStorePath = strings.ReplaceAll(string(userPasswordSecret.Data["keyStorePath"]), "/", "\\/") - } - if string(userPasswordSecret.Data["trustStorePassword"]) != "" { - //noinspection GoUnresolvedReference - trustStorePassword = strings.ReplaceAll(string(userPasswordSecret.Data["trustStorePassword"]), "/", "\\/") - } - if string(userPasswordSecret.Data["trustStorePath"]) != "" { - //noinspection GoUnresolvedReference - trustStorePath = strings.ReplaceAll(string(userPasswordSecret.Data["trustStorePath"]), "/", "\\/") - } + sslArgs, err := certutil.GetSslArgumentsFromSecret(sslSecret, trustStoreType, caSecret, isConsole) + if err != nil { + return nil, "", err } - sslArguments = sslArguments + ";" + "keyStorePath=" + keyStorePath - sslArguments = sslArguments + ";" + "keyStorePassword=" + keyStorePassword - sslArguments = sslArguments + ";" + "trustStorePath=" + trustStorePath - sslArguments = sslArguments + ";" + "trustStorePassword=" + trustStorePassword - return sslArguments + sslFlags := sslArgs.ToFlags() + + return sslArgs, sslFlags, nil } -func generateAcceptorSSLOptionalArguments(acceptor brokerv1beta1.AcceptorType) string { +func (r *ActiveMQArtemisReconcilerImpl) generateAcceptorSSLOptionalArguments(acceptor brokerv1beta1.AcceptorType) string { sslOptionalArguments := "" @@ -1222,10 +1294,6 @@ func generateAcceptorSSLOptionalArguments(acceptor brokerv1beta1.AcceptorType) s sslOptionalArguments = sslOptionalArguments + ";" + "keyStoreProvider=" + acceptor.KeyStoreProvider } - if acceptor.TrustStoreType != "" { - sslOptionalArguments = sslOptionalArguments + ";" + "trustStoreType=" + acceptor.TrustStoreType - } - if acceptor.TrustStoreProvider != "" { sslOptionalArguments = sslOptionalArguments + ";" + "trustStoreProvider=" + acceptor.TrustStoreProvider } @@ -1233,7 +1301,41 @@ func generateAcceptorSSLOptionalArguments(acceptor brokerv1beta1.AcceptorType) s return sslOptionalArguments } -func generateConnectorSSLOptionalArguments(connector brokerv1beta1.ConnectorType) string { +func (r *ActiveMQArtemisReconcilerImpl) addPemConfigFileSecret(ss *appsv1.StatefulSet, configs []string) { + resourceName := types.NamespacedName{ + Namespace: ss.Namespace, + Name: certutil.CfgToSecretName(configs[0]), + } + + buf := &bytes.Buffer{} + fmt.Fprintln(buf, "source.key=", configs[1]) + fmt.Fprintln(buf, "source.cert=", configs[2]) + + contents := map[string]string{configs[0]: buf.String()} + + var desired *corev1.Secret + + obj := r.cloneOfDeployed(reflect.TypeOf(corev1.Secret{}), resourceName.Name) + if obj != nil { + desired = obj.(*corev1.Secret) + desired.StringData = contents + } else { + desired = secrets.NewSecret(resourceName, contents, nil) + } + r.trackDesired(desired) + volume := volumes.MakeVolumeForSecret(desired.Name) + + if !common.HasVolumeInStatefulset(ss, volume.Name) { + ss.Spec.Template.Spec.Volumes = append(ss.Spec.Template.Spec.Volumes, volume) + } + + secretVolumeMount := volumes.MakeVolumeMount(volume.Name) + if !common.HasVolumeMount(&ss.Spec.Template.Spec.Containers[0], secretVolumeMount.Name) { + ss.Spec.Template.Spec.Containers[0].VolumeMounts = append(ss.Spec.Template.Spec.Containers[0].VolumeMounts, secretVolumeMount) + } +} + +func (r *ActiveMQArtemisReconcilerImpl) generateConnectorSSLOptionalArguments(connector brokerv1beta1.ConnectorType) string { sslOptionalArguments := "" @@ -1276,12 +1378,12 @@ func generateConnectorSSLOptionalArguments(connector brokerv1beta1.ConnectorType func (reconciler *ActiveMQArtemisReconcilerImpl) CurrentDeployedResources(customResource *brokerv1beta1.ActiveMQArtemis, client rtclient.Client) { reqLogger := reconciler.log.WithValues("ActiveMQArtemis Name", customResource.Name) - reqLogger.V(1).Info("currentDeployedResources") var err error if customResource.Spec.DeploymentPlan.PersistenceEnabled { reconciler.checkExistingPersistentVolumes(customResource, client) } + reconciler.deployed, err = common.GetDeployedResources(customResource, client) if err != nil { reqLogger.Error(err, "error getting deployed resources") @@ -1307,9 +1409,9 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) ProcessResources(customResource reqLogger := reconciler.log.WithValues("ActiveMQArtemis Name", customResource.Name) - for index := range reconciler.requestedResources { - reconciler.requestedResources[index].SetNamespace(customResource.Namespace) - if err = reconciler.applyTemplates(reconciler.requestedResources[index]); err != nil { + for _, requested := range common.ToResourceList(reconciler.requestedResources) { + requested.SetNamespace(customResource.Namespace) + if err = reconciler.applyTemplates(requested); err != nil { return err } } @@ -1320,7 +1422,7 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) ProcessResources(customResource } reqLogger.V(1).Info("Processing resources", "num requested", len(reconciler.requestedResources), "num current", currenCount) - requested := compare.NewMapBuilder().Add(reconciler.requestedResources...).ResourceMap() + requested := compare.NewMapBuilder().Add(common.ToResourceList(reconciler.requestedResources)...).ResourceMap() comparator := compare.NewMapComparator() comparator.Comparator.SetComparator(reflect.TypeOf(appsv1.StatefulSet{}), func(deployed, requested rtclient.Object) (isEqual bool) { @@ -1564,7 +1666,7 @@ func addNewVolumes(existingNames map[string]string, existing *[]corev1.Volume, n } } -func (r *ActiveMQArtemisReconcilerImpl) MakeVolumes(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers) []corev1.Volume { +func (r *ActiveMQArtemisReconcilerImpl) MakeVolumes(customResource *brokerv1beta1.ActiveMQArtemis, namer common.Namers) ([]corev1.Volume, error) { volumeDefinitions := []corev1.Volume{} if customResource.Spec.DeploymentPlan.PersistenceEnabled { @@ -1586,10 +1688,15 @@ func (r *ActiveMQArtemisReconcilerImpl) MakeVolumes(customResource *brokerv1beta continue } secretName := customResource.Name + "-" + acceptor.Name + "-secret" + if acceptor.SSLSecret != "" { secretName = acceptor.SSLSecret } addNewVolumes(secretVolumes, &volumeDefinitions, &secretName) + + if acceptor.TrustSecret != nil { + addNewVolumes(secretVolumes, &volumeDefinitions, acceptor.TrustSecret) + } } // Scan connectors for any with sslEnabled @@ -1602,18 +1709,21 @@ func (r *ActiveMQArtemisReconcilerImpl) MakeVolumes(customResource *brokerv1beta secretName = connector.SSLSecret } addNewVolumes(secretVolumes, &volumeDefinitions, &secretName) + if connector.TrustSecret != nil { + addNewVolumes(secretVolumes, &volumeDefinitions, connector.TrustSecret) + } } if customResource.Spec.Console.SSLEnabled { r.log.V(1).Info("Make volumes for ssl console exposure on k8s") secretName := namer.SecretsConsoleNameBuilder.Name() - if customResource.Spec.Console.SSLSecret != "" { - secretName = customResource.Spec.Console.SSLSecret - } addNewVolumes(secretVolumes, &volumeDefinitions, &secretName) + if customResource.Spec.Console.TrustSecret != nil { + addNewVolumes(secretVolumes, &volumeDefinitions, customResource.Spec.Console.TrustSecret) + } } - return volumeDefinitions + return volumeDefinitions, nil } func addNewVolumeMounts(existingNames map[string]string, existing *[]corev1.VolumeMount, newVolumeMountName *string) { @@ -1678,6 +1788,10 @@ func (r *ActiveMQArtemisReconcilerImpl) MakeVolumeMounts(customResource *brokerv volumeMountName = acceptor.SSLSecret + "-volume" } addNewVolumeMounts(secretVolumeMounts, &volumeMounts, &volumeMountName) + if acceptor.TrustSecret != nil { + volMountName := *acceptor.TrustSecret + "-volume" + addNewVolumeMounts(secretVolumeMounts, &volumeMounts, &volMountName) + } } // Scan connectors for any with sslEnabled @@ -1690,15 +1804,20 @@ func (r *ActiveMQArtemisReconcilerImpl) MakeVolumeMounts(customResource *brokerv volumeMountName = connector.SSLSecret + "-volume" } addNewVolumeMounts(secretVolumeMounts, &volumeMounts, &volumeMountName) + if connector.TrustSecret != nil { + volMountName := *connector.TrustSecret + "-volume" + addNewVolumeMounts(secretVolumeMounts, &volumeMounts, &volMountName) + } } if customResource.Spec.Console.SSLEnabled { r.log.V(1).Info("Make volume mounts for ssl console exposure on k8s") volumeMountName := namer.SecretsConsoleNameBuilder.Name() + "-volume" - if customResource.Spec.Console.SSLSecret != "" { - volumeMountName = customResource.Spec.Console.SSLSecret + "-volume" - } addNewVolumeMounts(secretVolumeMounts, &volumeMounts, &volumeMountName) + if customResource.Spec.Console.TrustSecret != nil { + volMountName := *customResource.Spec.Console.TrustSecret + "-volume" + addNewVolumeMounts(secretVolumeMounts, &volumeMounts, &volMountName) + } } return volumeMounts, nil @@ -1812,7 +1931,10 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) NewPodTemplateSpecForCR(customR newContainersArray := []corev1.Container{} podSpec.Containers = append(newContainersArray, *container) - brokerVolumes := reconciler.MakeVolumes(customResource, namer) + brokerVolumes, err := reconciler.MakeVolumes(customResource, namer) + if err != nil { + return nil, err + } if len(extraVolumes) > 0 { brokerVolumes = append(brokerVolumes, extraVolumes...) } @@ -2295,8 +2417,10 @@ func (reconciler *ActiveMQArtemisReconcilerImpl) addResourceForBrokerProperties( } data := brokerPropertiesData(customResource.Spec.BrokerProperties) + if desired == nil { - secret := secrets.MakeSecret(resourceName, resourceName.Name, data, namer.LabelBuilder.Labels()) + reconciler.log.V(1).Info("desired brokerprop secret nil, create new one", "name", resourceName.Name) + secret := secrets.MakeSecret(resourceName, data, namer.LabelBuilder.Labels()) desired = &secret } else { desired.StringData = data diff --git a/controllers/activemqartemis_reconciler_test.go b/controllers/activemqartemis_reconciler_test.go index c8fee272a..0af7572ff 100644 --- a/controllers/activemqartemis_reconciler_test.go +++ b/controllers/activemqartemis_reconciler_test.go @@ -560,7 +560,7 @@ func TestProcess_TemplateIncludesLabelsServiceAndSecret(t *testing.T) { var ssFound = false var secretFound = false var serviceFound = false - for _, resource := range reconciler.requestedResources { + for _, resource := range common.ToResourceList(reconciler.requestedResources) { if ss, ok := resource.(*appsv1.StatefulSet); ok { newSpec := ss.Spec.Template @@ -637,7 +637,7 @@ func TestProcess_TemplateIncludesLabelsSecretRegexp(t *testing.T) { var secretFound = false var serviceFound = false - for _, resource := range reconciler.requestedResources { + for _, resource := range common.ToResourceList(reconciler.requestedResources) { if secret, ok := resource.(*v1.Secret); ok { assert.True(t, len(secret.Labels) >= 1) assert.Equal(t, secret.Labels["mySecretKey"], "mySecretValue") @@ -690,7 +690,7 @@ func TestProcess_TemplateDuplicateKeyReplacesOk(t *testing.T) { assert.NoError(t, err) var secretFound = false - for _, resource := range reconciler.requestedResources { + for _, resource := range common.ToResourceList(reconciler.requestedResources) { if secret, ok := resource.(*v1.Secret); ok { assert.True(t, len(secret.Labels) >= 1) assert.Equal(t, secret.Labels["mySecretKey"], "mySecretValue") @@ -730,10 +730,9 @@ func TestProcess_TemplateKeyValue(t *testing.T) { }, }, Acceptors: []brokerv1beta1.AcceptorType{{ - Name: "aa", - Port: 563, - Expose: true, - SSLEnabled: true, + Name: "aa", + Port: 563, + Expose: true, }}, }, } @@ -759,7 +758,7 @@ func TestProcess_TemplateKeyValue(t *testing.T) { var secretFound = false var serviceFound = false var ssFound = false - for _, resource := range reconciler.requestedResources { + for _, resource := range common.ToResourceList(reconciler.requestedResources) { if ss, ok := resource.(*appsv1.StatefulSet); ok { v, ok := ss.Labels["myKey"] @@ -785,7 +784,7 @@ func TestProcess_TemplateKeyValue(t *testing.T) { } if ingress, ok := resource.(*netv1.Ingress); ok { - assert.True(t, len(ingress.Annotations) >= 2) + assert.True(t, len(ingress.Annotations) >= 1) assert.Equal(t, ingress.Annotations["myIngressKey-cr"], "myValue-0", resource.GetName()) } @@ -821,7 +820,7 @@ func TestProcess_TemplateCustomAttributeIngress(t *testing.T) { Name: "aa", Port: 563, Expose: true, - SSLEnabled: true, + SSLEnabled: false, }}, }, } @@ -845,10 +844,10 @@ func TestProcess_TemplateCustomAttributeIngress(t *testing.T) { assert.NoError(t, err) var ingressOk = false - for _, resource := range reconciler.requestedResources { + for _, resource := range common.ToResourceList(reconciler.requestedResources) { if ingress, ok := resource.(*netv1.Ingress); ok { - assert.True(t, len(ingress.Annotations) >= 2) + assert.True(t, len(ingress.Annotations) >= 1) assert.Equal(t, ingress.Annotations["myIngressKey-cr"], "myValue-0", resource.GetName()) assert.NotNil(t, ingress.Spec.IngressClassName) assert.Equal(t, *ingress.Spec.IngressClassName, ingressClassVal) @@ -884,7 +883,7 @@ func TestProcess_TemplateCustomAttributeMisSpellingIngress(t *testing.T) { Name: "aa", Port: 563, Expose: true, - SSLEnabled: true, + SSLEnabled: false, }}, }, } @@ -958,7 +957,7 @@ func TestProcess_TemplateCustomAttributeContainerSecurityContext(t *testing.T) { assert.NoError(t, err) var runAsRootOk = false - for _, resource := range reconciler.requestedResources { + for _, resource := range common.ToResourceList(reconciler.requestedResources) { if ss, ok := resource.(*appsv1.StatefulSet); ok { assert.NotNil(t, ss.Spec.Template.Spec.Containers[0].SecurityContext.RunAsNonRoot) @@ -1014,7 +1013,7 @@ func TestProcess_TemplateCustomAttributePriorityClassName(t *testing.T) { assert.NoError(t, err) var priorityClassNameOk = false - for _, resource := range reconciler.requestedResources { + for _, resource := range common.ToResourceList(reconciler.requestedResources) { if ss, ok := resource.(*appsv1.StatefulSet); ok { assert.Equal(t, ss.Spec.Template.Spec.PriorityClassName, "high-priority") diff --git a/controllers/activemqartemissecurity_controller.go b/controllers/activemqartemissecurity_controller.go index f0b7aab4b..8b554e516 100644 --- a/controllers/activemqartemissecurity_controller.go +++ b/controllers/activemqartemissecurity_controller.go @@ -245,7 +245,7 @@ func (r *ActiveMQArtemisSecurityConfigHandler) getPassword(secretName string, ke // Attempt to retrieve the secret stringDataMap := make(map[string]string) - secretDefinition := secrets.NewSecret(namespacedName, secretName, stringDataMap, r.GetDefaultLabels()) + secretDefinition := secrets.NewSecret(namespacedName, stringDataMap, r.GetDefaultLabels()) if err := resources.Retrieve(namespacedName, r.owner.Client, secretDefinition); err != nil { if errors.IsNotFound(err) { diff --git a/controllers/common_util_test.go b/controllers/common_util_test.go index 298728e18..d28f0f6b5 100644 --- a/controllers/common_util_test.go +++ b/controllers/common_util_test.go @@ -29,6 +29,7 @@ import ( "math/big" "math/rand" "os" + "os/exec" "path" "regexp" "strconv" @@ -44,6 +45,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/config" "software.sslmate.com/src/go-pkcs12" + cmv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" + tm "github.com/cert-manager/trust-manager/pkg/apis/trust/v1alpha1" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" appsv1 "k8s.io/api/apps/v1" @@ -66,6 +69,8 @@ var defaultPassword string = "password" var defaultSanDnsNames = []string{"*.apps.artemiscloud.io", "*.tests.artemiscloud.io"} var okDefaultPwd = "okdefaultpassword" +const helmCmd = "helm" + type TestLogWriter struct { unbufferedWriter bytes.Buffer } @@ -523,14 +528,16 @@ func ExecOnPod(podWithOrdinal string, brokerName string, namespace string, comma g.Expect(err).To(BeNil()) var outPutbuffer bytes.Buffer + var errBuffer bytes.Buffer + By("executing " + fmt.Sprintf(" command: %v", command)) err = exec.StreamWithContext(ctx, remotecommand.StreamOptions{ Stdin: os.Stdin, Stdout: &outPutbuffer, - Stderr: os.Stderr, + Stderr: &errBuffer, Tty: false, }) - g.Expect(err).To(BeNil()) + g.Expect(err).To(BeNil(), errBuffer.String()) g.Eventually(func(g Gomega) { By("Checking for output from " + fmt.Sprintf(" command: %v", command)) @@ -803,3 +810,247 @@ func WaitForPod(crName string, iPods ...int32) { } } + +func InstallCertManager() error { + cmd := exec.Command(helmCmd, "repo", "add", "jetstack", "https://charts.jetstack.io", "--force-update") + err := cmd.Run() + if err != nil { + return err + } + // cert manager + cmd = exec.Command(helmCmd, "upgrade", "-i", "-n", "cert-manager", "cert-manager", "jetstack/cert-manager", "--set", "installCRDs=true", "--wait", "--create-namespace") + err = cmd.Run() + if err != nil { + return err + } + // Wait for cert-manager-webhook to be ready, which can take time if cert-manager + // was re-installed after uninstalling on a cluster. + cmd = exec.Command(kubeTool, "wait", "deployment.apps/cert-manager-webhook", + "--for", "condition=Available", + "--namespace", "cert-manager", + "--timeout", "5m") + err = cmd.Run() + if err != nil { + fmt.Printf("error waiting cert-manager %v\n", err) + return err + } + // trust manager + // https://cert-manager.io/docs/trust/trust-manager/installation/ + cmd = exec.Command(helmCmd, "upgrade", "-i", "-n", "cert-manager", "trust-manager", "jetstack/trust-manager", "--set", "secretTargets.enabled=true", "--set", "secretTargets.authorizedSecretsAll=true", "--wait") + err = cmd.Run() + + if err != nil { + fmt.Printf("error waiting cert-manager %v\n", err) + } + + return err +} + +func UninstallCertManager() error { + + //trust manager + cmd := exec.Command(helmCmd, "uninstall", "-n", "cert-manager", "trust-manager") + err := cmd.Run() + if err != nil { + return err + } + //cert manager + cmd = exec.Command(helmCmd, "uninstall", "-n", "cert-manager", "cert-manager") + err = cmd.Run() + if err != nil { + return err + } + //namespace + cmd = exec.Command(kubeTool, "delete", "namespace", "cert-manager") + err = cmd.Run() + return err +} + +func CertManagerInstalled() bool { + cmDeploymentKey := types.NamespacedName{Name: "cert-manager", Namespace: "cert-manager"} + cmDeployment := &appsv1.Deployment{} + err := k8sClient.Get(ctx, cmDeploymentKey, cmDeployment) + return err == nil +} + +func InstallClusteredIssuer(issuerName string, customFunc func(*cmv1.ClusterIssuer)) *cmv1.ClusterIssuer { + issuer := cmv1.ClusterIssuer{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ClusterIssuer", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: issuerName, + }, + Spec: cmv1.IssuerSpec{ + IssuerConfig: cmv1.IssuerConfig{ + SelfSigned: &cmv1.SelfSignedIssuer{}, + }, + }, + } + if customFunc != nil { + customFunc(&issuer) + } + Expect(k8sClient.Create(ctx, &issuer, &client.CreateOptions{})).To(Succeed()) + issKey := types.NamespacedName{Name: issuerName, Namespace: defaultNamespace} + currentIssuer := &cmv1.ClusterIssuer{} + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, issKey, currentIssuer)).Should(Succeed()) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + return currentIssuer +} + +func InstallCert(certName string, namespace string, customFunc func(candidate *cmv1.Certificate)) *cmv1.Certificate { + cmCert := cmv1.Certificate{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Certificate", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: certName, + Namespace: namespace, + }, + Spec: cmv1.CertificateSpec{ + SecretName: certName + "-secret", + DNSNames: defaultSanDnsNames, + Subject: &cmv1.X509Subject{ + Organizations: []string{"www.artemiscloud.io"}, + }, + }, + } + if customFunc != nil { + customFunc(&cmCert) + } + + Expect(k8sClient.Create(ctx, &cmCert, &client.CreateOptions{})).To(Succeed()) + + certKey := types.NamespacedName{Name: certName, Namespace: namespace} + cert := &cmv1.Certificate{} + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, certKey, cert)).Should(Succeed()) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + secretKey := types.NamespacedName{Name: cmCert.Spec.SecretName, Namespace: namespace} + secret := corev1.Secret{} + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, secretKey, &secret)).Should(Succeed()) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + return &cmCert +} + +func InstallCaBundle(name string, sourceSecret string, caFileName string) *tm.Bundle { + bundle := tm.Bundle{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "trust.cert-manager.io/v1alpha1", + Kind: "Bundle", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "cert-manager", + }, + Spec: tm.BundleSpec{ + Sources: []tm.BundleSource{ + { + Secret: &tm.SourceObjectKeySelector{ + Name: sourceSecret, + KeySelector: tm.KeySelector{ + Key: "tls.crt", + }, + }, + }, + }, + Target: tm.BundleTarget{ + Secret: &tm.KeySelector{ + Key: caFileName, + }, + }, + }, + } + + Expect(k8sClient.Create(ctx, &bundle, &client.CreateOptions{})).To(Succeed()) + bundleKey := types.NamespacedName{Name: name, Namespace: "cert-manager"} + newBundle := &tm.Bundle{} + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, bundleKey, newBundle)).Should(Succeed()) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + return newBundle +} + +func UnInstallCaBundle(bundleName string) { + bundleKey := types.NamespacedName{Name: bundleName, Namespace: "cert-manager"} + bundle := &tm.Bundle{} + + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, bundleKey, bundle)).Should(Succeed()) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + CleanResource(bundle, bundleName, bundle.Namespace) +} + +func InstallSecret(secretName string, namespace string, configFunc func(candidate *corev1.Secret)) *corev1.Secret { + secret := corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + }, + StringData: make(map[string]string), + } + if configFunc != nil { + configFunc(&secret) + } + + Expect(k8sClient.Create(ctx, &secret, &client.CreateOptions{})).To(Succeed()) + certKey := types.NamespacedName{Name: secretName, Namespace: namespace} + newSecret := &corev1.Secret{} + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, certKey, newSecret)).Should(Succeed()) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + return &secret +} + +func UninstallCert(certName string, namespace string) { + certKey := types.NamespacedName{Name: certName, Namespace: namespace} + cert := &cmv1.Certificate{} + + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, certKey, cert)).Should(Succeed()) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + CleanResource(cert, certName, namespace) + + certSecret := corev1.Secret{} + secretKey := types.NamespacedName{Name: cert.Spec.SecretName, Namespace: namespace} + Expect(k8sClient.Get(ctx, secretKey, &certSecret)).To(Succeed()) + + CleanResource(&certSecret, certSecret.Name, certSecret.Namespace) +} + +func UninstallSecret(secretName string, namespace string) { + secretKey := types.NamespacedName{Name: secretName, Namespace: namespace} + secret := &corev1.Secret{} + + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, secretKey, secret)).Should(Succeed()) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + CleanResource(secret, secretName, namespace) +} + +func UninstallClusteredIssuer(issuerName string) { + issKey := types.NamespacedName{Name: issuerName, Namespace: defaultNamespace} + currentIssuer := &cmv1.ClusterIssuer{} + + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, issKey, currentIssuer)).Should(Succeed()) + }, existingClusterTimeout, existingClusterInterval).Should(Succeed()) + + CleanResource(currentIssuer, issuerName, defaultNamespace) +} diff --git a/controllers/suite_test.go b/controllers/suite_test.go index f35cb01e2..01a066ad3 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -40,6 +40,7 @@ import ( "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/labels" + cmv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -64,6 +65,7 @@ import ( "github.com/artemiscloud/activemq-artemis-operator/pkg/resources/ingresses" "github.com/artemiscloud/activemq-artemis-operator/pkg/utils/common" + tm "github.com/cert-manager/trust-manager/pkg/apis/trust/v1alpha1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" netv1 "k8s.io/api/networking/v1" @@ -675,6 +677,12 @@ func setUpK8sClient() { err = routev1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) + err = cmv1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + err = tm.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + err = brokerv2alpha5.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) diff --git a/deploy/activemq-artemis-operator.yaml b/deploy/activemq-artemis-operator.yaml index 125880cd8..3c518e4b9 100644 --- a/deploy/activemq-artemis-operator.yaml +++ b/deploy/activemq-artemis-operator.yaml @@ -445,6 +445,9 @@ spec: suppressInternalManagementObjects: description: If prevents advisory addresses/queues to be registered to management service, default false type: boolean + trustSecret: + description: The name of the truststore secret. + type: string trustStoreProvider: description: Provider used for the truststore; "SUN", "SunJCE", etc. Default in broker is null type: string @@ -755,6 +758,9 @@ spec: sslSecret: description: Name of the secret to use for ssl information type: string + trustSecret: + description: The name of the truststore secret. + type: string trustStoreProvider: description: Provider used for the truststore; "SUN", "SunJCE", etc. Default in broker is null type: string @@ -800,6 +806,9 @@ spec: sslSecret: description: Name of the secret to use for ssl information type: string + trustSecret: + description: The name of the truststore secret. + type: string useClientAuth: description: If the embedded server requires client authentication type: boolean diff --git a/deploy/crds/broker_activemqartemis_crd.yaml b/deploy/crds/broker_activemqartemis_crd.yaml index 0965cd7f7..6a93e4454 100644 --- a/deploy/crds/broker_activemqartemis_crd.yaml +++ b/deploy/crds/broker_activemqartemis_crd.yaml @@ -102,6 +102,9 @@ spec: suppressInternalManagementObjects: description: If prevents advisory addresses/queues to be registered to management service, default false type: boolean + trustSecret: + description: The name of the truststore secret. + type: string trustStoreProvider: description: Provider used for the truststore; "SUN", "SunJCE", etc. Default in broker is null type: string @@ -412,6 +415,9 @@ spec: sslSecret: description: Name of the secret to use for ssl information type: string + trustSecret: + description: The name of the truststore secret. + type: string trustStoreProvider: description: Provider used for the truststore; "SUN", "SunJCE", etc. Default in broker is null type: string @@ -457,6 +463,9 @@ spec: sslSecret: description: Name of the secret to use for ssl information type: string + trustSecret: + description: The name of the truststore secret. + type: string useClientAuth: description: If the embedded server requires client authentication type: boolean diff --git a/docs/help/operator.md b/docs/help/operator.md index 2c205c0da..6afac401e 100644 --- a/docs/help/operator.md +++ b/docs/help/operator.md @@ -1164,7 +1164,9 @@ spec: deploymentPlan: size: 1 ``` -And you use init container to configure security to have a username **alice** with password **password1** for jolikia access. To enable operator to use client to have access jolokia, create a secret named **amq-jolokia-secret** in the same namespace, like this: + +And you use init container to configure security to have a username **alice** with password **password1** for jolokia access. To enable operator to use client to have access jolokia, create a secret named **amq-jolokia-secret** in the same namespace, like this: + ```yaml apiVersion: v1 metadata: @@ -1215,7 +1217,7 @@ spec: ``` When deploying the above CR, the PVC volume will be mounted to path **/opt/mydata** in the broker container of both broker pods. The **extraVolumeMounts** is optional. If not specified a default mountPath is given based on the type of the volume, following the pattern: -/amq/extra// +/amq/extra/volumes/ For example if you configure to attach a PersistentVolumeClaim type volume called `mydata`, the default mount path is **/amq/extra/volumes/mydata**. @@ -1252,3 +1254,164 @@ Note for each pod the PVC's name must follow the pattern `-/data**. For complete configruation options please take a look at the api definitions of [broker CRD](../../api/v1beta1/activemqartemis_types.go). + +## Using cert-manager and trust-manager configure brokers + +Note: this feature currently is experimental. Feedback is welcomed. + +[cert-manager](https://cert-manager.io/) adds certificates and certificate issuers as resource types in Kubernetes clusters, and simplifies the process of obtaining, renewing and using those certificates. + +The operator provides options in the custom resource that utilizes cert-manager x509 certificates to configure SSL/TLS transports for brokers. It also works with [trust-manager](https://github.com/cert-manager/trust-manager) to distribute trust CA bundles. + +Before configuring a broker you need to have the certificates and bundles ready. In the following example a self-signed isser is used as a root CA. + +Step 1 - create the root self-signed issuer + +```yaml +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: root-issuer +spec: + selfSigned: {} +``` + +Step 2 - create the root Certificate + +```yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: root-ca + namespace: cert-manager +spec: + isCA: true + commonName: "artemiscloud.io.root" + secretName: root-ca-secret + subject: + organizations: + - "www.artemiscloud.io" + issuerRef: + name: root-issuer + kind: ClusterIssuer +``` + +Step 3 - create a ca issuer that is used to issue broker certificates signed by the root CA + +```yaml +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: broker-cert-issuer +spec: + ca: + secretName: root-ca-secret +``` + +Step 4 - create a broker certificate signed by the root CA + +```yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: server-cert +spec: + isCA: false + commonName: "artemiscloud.io" + dnsNames: + - "artemis-broker-ss-0" + - "artemis-broker-ss-0.artemis-broker-hdls-svc.default.svc.cluster.local" + secretName: server-cert-secret + subject: + organizations: + - "www.artemiscloud.io" + issuerRef: + name: broker-cert-issuer + kind: ClusterIssuer +``` + +Step 5 - create the ca bundle from the root CA using trust-manager + +```yaml +apiVersion: trust.cert-manager.io/v1alpha1 +kind: Bundle +metadata: + name: ca-bundle +spec: + sources: + - useDefaultCAs: false + - secret: + name: "root-ca-secret" + key: "tls.crt" + target: + secret: + key: "trust-bundle.pem" +``` + +### Configuring SSL/TLS for management console + +Once you have the certificate and ca bundle ready you can configure the management console of the broker to used it: + +```yaml +apiVersion: broker.amq.io/v1beta1 +kind: ActiveMQArtemis +metadata: + name: artemis-broker +spec: + console: + expose: true + sslEnabled: true + sslSecret: server-cert-secret + trustSecret: ca-bundle + deploymentPlan: + size: 1 +``` + +The above broker cr configures a broker that has a SSL/TLS secured management console whose keystore and truststore are generated from certificate stored in secret `server-cert-secret`. + +### Configuring SSL/TLS for acceptors and connectors + +With the certificate ready you can configure an acceptor and/or connector of the broker to use it: + +```yaml +apiVersion: broker.amq.io/v1beta1 +kind: ActiveMQArtemis +metadata: + name: artemis-broker +spec: + acceptors: + - name: new-acceptor + protocols: all + port: 62666 + sslEnabled: true + needClientAuth: true + expose: true + sslSecret: server-cert-secret + trustSecret: ca-bundle + deploymentPlan: + size: 1 +``` + +The above broker cr configures a broker that has a SSL/TLS secured acceptor called `new-acceptor` whose keystore and truststore are generated from secret `server-cert-secret` that is from the certificate resource. + +You can configure a connector with ssl parameters from a certificate in like manner, for example the following yaml configures a connector called `new-connector` with the certificated above mentioned: + +```yaml +apiVersion: broker.amq.io/v1beta1 +kind: ActiveMQArtemis +metadata: + name: artemis-broker +spec: + connectors: + - name: new-connector + host: artemis-broker-ss-0 + port: 62666 + sslEnabled: true + expose: true + sslSecret: server-cert-secret + trustSecret: ca-bundle + deploymentPlan: + size: 1 +``` + +For details on how to use cert-manager to manage your certificates please refer to its [documentation](https://cert-manager.io/docs/). diff --git a/go.mod b/go.mod index d45a96d33..adfacc297 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.20 require ( github.com/Azure/go-amqp v0.17.4 github.com/RHsyseng/operator-utils v1.4.10 + github.com/cert-manager/cert-manager v1.12.2 + github.com/cert-manager/trust-manager v0.7.0 github.com/go-logr/logr v1.2.4 github.com/golang/mock v1.6.0 github.com/onsi/ginkgo/v2 v2.13.0 @@ -23,7 +25,7 @@ require ( require ( github.com/blang/semver/v4 v4.0.0 - k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 + k8s.io/utils v0.0.0-20230726121419-3b25d923346b ) require ( @@ -45,9 +47,9 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect + github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect + github.com/imdario/mergo v0.3.16 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -81,6 +83,7 @@ require ( k8s.io/component-base v0.28.3 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect + sigs.k8s.io/gateway-api v0.7.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/go.sum b/go.sum index 763c5ebb7..e7f9c2b8b 100644 --- a/go.sum +++ b/go.sum @@ -9,11 +9,12 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cert-manager/cert-manager v1.12.2 h1:lJ7Xn0VhmBA4uOZb5dlSZzepu38ez73okOqgE24x8YM= +github.com/cert-manager/cert-manager v1.12.2/go.mod h1:ql0msU88JCcQSceN+PFjEY8U+AMe13y06vO2klJk8bs= +github.com/cert-manager/trust-manager v0.7.0 h1:MvbDA83qV1JKB2EoXqkxB46bTcDkRR2bES87/EsvsX4= +github.com/cert-manager/trust-manager v0.7.0/go.mod h1:8dYyuVk4wO24MvfWwaQdh06/hFidV/lyzI3LThjCqcI= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -61,14 +62,13 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3 h1:2XF1Vzq06X+inNqgJ9tRnGuw+ZVCB3FazXODD6JE1R8= +github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -172,7 +172,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -218,7 +217,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -238,10 +236,12 @@ k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/gateway-api v0.7.0 h1:/mG8yyJNBifqvuVLW5gwlI4CQs0NR/5q4BKUlf1bVdY= +sigs.k8s.io/gateway-api v0.7.0/go.mod h1:Xv0+ZMxX0lu1nSSDIIPEfbVztgNZ+3cfiYrJsa2Ooso= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= diff --git a/main.go b/main.go index 165b7b8f5..caf195e4a 100644 --- a/main.go +++ b/main.go @@ -189,7 +189,6 @@ func main() { setupLog.Info("setting up operator to watch local namespace") mgrOptions.Cache.DefaultNamespaces = map[string]cache.Config{ oprNamespace: {}} - } else { if watchList != nil { if len(watchList) == 1 { diff --git a/pkg/draincontroller/controller.go b/pkg/draincontroller/controller.go index 2b9891cd5..b2d4cc5f8 100644 --- a/pkg/draincontroller/controller.go +++ b/pkg/draincontroller/controller.go @@ -750,7 +750,7 @@ func (c *Controller) getClusterCredentials(namespace string, ssNames map[string] stringDataMap["AMQ_CLUSTER_USER"] = "" stringDataMap["AMQ_CLUSTER_PASSWORD"] = "" - secretDefinition := secrets.NewSecret(namespacedName, secretName, stringDataMap, c.ssLabels) + secretDefinition := secrets.NewSecret(namespacedName, stringDataMap, c.ssLabels) c.log.V(2).Info("Try retrieving cluster credentials from secret", "secret", namespacedName) if err := resources.Retrieve(namespacedName, c.client, secretDefinition); err != nil { diff --git a/pkg/resources/containers/container.go b/pkg/resources/containers/container.go index a58b25101..0029fa6aa 100644 --- a/pkg/resources/containers/container.go +++ b/pkg/resources/containers/container.go @@ -26,7 +26,7 @@ func MakeContainer(hostingPodSpec *corev1.PodSpec, customResourceName string, im if container == nil { container = &corev1.Container{ Name: name, - Command: []string{"/bin/bash", "-c", "export STATEFUL_SET_ORDINAL=${HOSTNAME##*-};export JDK_JAVA_OPTIONS=${JDK_JAVA_OPTIONS//\\$\\{STATEFUL_SET_ORDINAL\\}/${HOSTNAME##*-}};exec /opt/amq/bin/launch.sh", "start"}, + Command: []string{"/bin/bash", "-c", "export STATEFUL_SET_ORDINAL=${HOSTNAME##*-}; export JDK_JAVA_OPTIONS=${JDK_JAVA_OPTIONS//\\$\\{STATEFUL_SET_ORDINAL\\}/${HOSTNAME##*-}}; export FQ_HOST_NAME=$(hostname -f); export JAVA_ARGS_APPEND=$( echo ${JAVA_ARGS_APPEND} | sed \"s/FQ_HOST_NAME/${FQ_HOST_NAME}/\"); exec /opt/amq/bin/launch.sh", "start"}, } } diff --git a/pkg/resources/secrets/secret.go b/pkg/resources/secrets/secret.go index a860934f1..825fa86bd 100644 --- a/pkg/resources/secrets/secret.go +++ b/pkg/resources/secrets/secret.go @@ -35,7 +35,7 @@ func MakeStringDataMap(keyName string, valueName string, key string, value strin return stringDataMap } -func MakeSecret(namespacedName types.NamespacedName, secretName string, stringData map[string]string, labels map[string]string) corev1.Secret { +func MakeSecretWithData(namespacedName types.NamespacedName, secretName string, data map[string][]byte, labels map[string]string) *corev1.Secret { secretDefinition := corev1.Secret{ TypeMeta: metav1.TypeMeta{ @@ -47,15 +47,33 @@ func MakeSecret(namespacedName types.NamespacedName, secretName string, stringDa Name: secretName, Namespace: namespacedName.Namespace, }, + Data: data, + } + + return &secretDefinition +} + +func MakeSecret(namespacedName types.NamespacedName, stringData map[string]string, labels map[string]string) corev1.Secret { + + secretDefinition := corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Name: namespacedName.Name, + Namespace: namespacedName.Namespace, + }, StringData: stringData, } return secretDefinition } -func NewSecret(namespacedName types.NamespacedName, secretName string, stringData map[string]string, labels map[string]string) *corev1.Secret { +func NewSecret(namespacedName types.NamespacedName, stringData map[string]string, labels map[string]string) *corev1.Secret { - secretDefinition := MakeSecret(namespacedName, secretName, stringData, labels) + secretDefinition := MakeSecret(namespacedName, stringData, labels) return &secretDefinition } @@ -63,7 +81,7 @@ func NewSecret(namespacedName types.NamespacedName, secretName string, stringDat func CreateOrUpdate(owner metav1.Object, namespacedName types.NamespacedName, stringDataMap map[string]string, labels map[string]string, client client.Client, scheme *runtime.Scheme) error { log := ctrl.Log.WithName("util_secrets") var err error = nil - secretDefinition := NewSecret(namespacedName, namespacedName.Name, stringDataMap, labels) + secretDefinition := NewSecret(namespacedName, stringDataMap, labels) if err = resources.Retrieve(namespacedName, client, secretDefinition); err != nil { if errors.IsNotFound(err) { @@ -76,7 +94,7 @@ func CreateOrUpdate(owner metav1.Object, namespacedName types.NamespacedName, st } } else { //Update - secretDefinition = NewSecret(namespacedName, namespacedName.Name, stringDataMap, labels) + secretDefinition = NewSecret(namespacedName, stringDataMap, labels) if err = resources.Update(client, secretDefinition); err != nil { log.Error(err, "Failed to update secret", "secret", namespacedName.Name) } @@ -89,7 +107,7 @@ func Create(owner metav1.Object, namespacedName types.NamespacedName, stringData log := ctrl.Log.WithName("util_secrets") var err error = nil - secretDefinition := NewSecret(namespacedName, namespacedName.Name, stringDataMap, labels) + secretDefinition := NewSecret(namespacedName, stringDataMap, labels) if err = resources.Retrieve(namespacedName, client, secretDefinition); err != nil { if errors.IsNotFound(err) { @@ -104,13 +122,13 @@ func Create(owner metav1.Object, namespacedName types.NamespacedName, stringData } func Delete(namespacedName types.NamespacedName, stringDataMap map[string]string, labels map[string]string, client client.Client) { - secretDefinition := NewSecret(namespacedName, namespacedName.Name, stringDataMap, labels) + secretDefinition := NewSecret(namespacedName, stringDataMap, labels) resources.Delete(client, secretDefinition) } func RetriveSecret(namespacedName types.NamespacedName, secretName string, labels map[string]string, client client.Client) (*corev1.Secret, error) { stringData := make(map[string]string) - secretDefinition := MakeSecret(namespacedName, secretName, stringData, labels) + secretDefinition := MakeSecret(namespacedName, stringData, labels) if err := resources.Retrieve(namespacedName, client, &secretDefinition); err != nil { return nil, err } @@ -129,7 +147,7 @@ func GetValueFromSecret(namespace string, // Attempt to retrieve the secret stringDataMap := make(map[string]string) - secretDefinition := NewSecret(namespacedName, secretName, stringDataMap, labels) + secretDefinition := NewSecret(namespacedName, stringDataMap, labels) if err := resources.Retrieve(namespacedName, client, secretDefinition); err != nil { if errors.IsNotFound(err) { diff --git a/pkg/resources/volumes/volume.go b/pkg/resources/volumes/volume.go index a5472365b..651d45c20 100644 --- a/pkg/resources/volumes/volume.go +++ b/pkg/resources/volumes/volume.go @@ -21,11 +21,14 @@ func MakeVolumeMount(volumeMountName string) corev1.VolumeMount { func MakeVolume(secretName string) corev1.Volume { volumeName := secretName + "-volume" + // 420(decimal) = 0644(octal) + var defaultMode int32 = 420 volume := corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: secretName, + SecretName: secretName, + DefaultMode: &defaultMode, }, }, } diff --git a/pkg/utils/certutil/certutil.go b/pkg/utils/certutil/certutil.go new file mode 100644 index 000000000..b5f7512a7 --- /dev/null +++ b/pkg/utils/certutil/certutil.go @@ -0,0 +1,271 @@ +/* +Copyright 2021. + +Licensed 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 certutil + +import ( + "fmt" + "strings" + + corev1 "k8s.io/api/core/v1" +) + +const ( + Cert_annotation_key = "cert-manager.io/issuer-name" + Bundle_annotation_key = "trust.cert-manager.io/hash" + Console_web_prefix = "webconfig.bindings.artemis." +) + +var defaultKeyStorePassword = "password" + +type SslArguments struct { + KeyStoreType string + KeyStorePath string + KeyStorePassword *string + TrustStoreType string + TrustStorePath string + TrustStorePassword *string + PemCfgs []string + IsConsole bool +} + +func (s *SslArguments) ToSystemProperties() string { + sslFlags := "" + + if s.KeyStorePath != "" { + if s.KeyStoreType == "PEM" { + ksPath := "/etc/secret-" + CfgToSecretName(s.KeyStorePath) + "/" + s.KeyStorePath + sslFlags = "-D" + Console_web_prefix + "keyStorePath=" + ksPath + } else { + sslFlags = "-D" + Console_web_prefix + "keyStorePath=" + s.KeyStorePath + } + } + + if s.KeyStorePassword != nil { + sslFlags = sslFlags + " -D" + Console_web_prefix + "keyStorePassword=" + *s.KeyStorePassword + } + + if s.KeyStoreType == "PEM" { + sslFlags = sslFlags + " -D" + Console_web_prefix + "keyStoreType=PEMCFG" + } else if s.KeyStoreType != "" { + sslFlags = sslFlags + " -D" + Console_web_prefix + "keyStoreType=" + s.KeyStoreType + } + + if s.TrustStorePath != "" { + sslFlags = sslFlags + " -D" + Console_web_prefix + "trustStorePath=" + s.TrustStorePath + } + if s.TrustStorePassword != nil { + sslFlags = sslFlags + " -D" + Console_web_prefix + "trustStorePassword=" + *s.TrustStorePassword + } + if s.TrustStoreType != "" { + sslFlags = sslFlags + " -D" + Console_web_prefix + "trustStoreType=" + s.TrustStoreType + } + + sslFlags = sslFlags + " -D" + Console_web_prefix + "uri=" + getConsoleUri() + + return sslFlags +} + +func getConsoleUri() string { + return "https://FQ_HOST_NAME:8161" +} + +func (s *SslArguments) ToFlags() string { + sslFlags := "" + + if s.IsConsole { + sslFlags = sslFlags + " " + "--ssl-key" + " " + s.KeyStorePath + if s.KeyStorePassword != nil { + sslFlags = sslFlags + " " + "--ssl-key-password" + " " + *s.KeyStorePassword + } + if s.TrustStorePath != "" { + sslFlags = sslFlags + " " + "--ssl-trust" + " " + s.TrustStorePath + } + if s.TrustStorePassword != nil { + sslFlags = sslFlags + " " + "--ssl-trust-password" + " " + *s.TrustStorePassword + } + return sslFlags + } + + sep := "\\/" + sslFlags = "sslEnabled=true" + if s.KeyStorePath != "" { + if s.KeyStoreType == "PEM" { + ksPath := "/etc/secret-" + CfgToSecretName(s.KeyStorePath) + "/" + s.KeyStorePath + sslFlags = sslFlags + ";" + "keyStorePath=" + strings.ReplaceAll(ksPath, "/", sep) + } else { + sslFlags = sslFlags + ";" + "keyStorePath=" + s.KeyStorePath + } + } + if s.KeyStorePassword != nil { + sslFlags = sslFlags + ";" + "keyStorePassword=" + *s.KeyStorePassword + } + + if s.KeyStoreType == "PEM" { + sslFlags = sslFlags + ";" + "keyStoreType=PEMCFG" + } else if s.KeyStoreType != "" { + sslFlags = sslFlags + ";" + "keyStoreType=" + s.KeyStoreType + } + + if s.TrustStorePath != "" { + sslFlags = sslFlags + ";" + "trustStorePath=" + s.TrustStorePath + } + if s.TrustStorePassword != nil { + sslFlags = sslFlags + ";" + "trustStorePassword=" + *s.TrustStorePassword + } + if s.TrustStoreType != "" { + sslFlags = sslFlags + ";" + "trustStoreType=" + s.TrustStoreType + } + + return sslFlags +} + +func IsSecretFromCert(secret *corev1.Secret) (bool, bool) { + _, exist := secret.Annotations[Cert_annotation_key] + if len(secret.Data) < 2 { + return exist, false + } else if _, ok := secret.Data["tls.crt"]; !ok { + return exist, false + } else if _, ok := secret.Data["tls.key"]; !ok { + return exist, false + } + return exist, true +} + +func isSecretFromBundle(secret *corev1.Secret) bool { + _, exist := secret.Annotations[Bundle_annotation_key] + return exist +} + +func getBundleNameFromSecret(secret *corev1.Secret) string { + //extract the key of the secret's only entry + bundleName := "" + for key := range secret.Data { + bundleName = key + break + } + return bundleName +} + +func GetSslArgumentsFromSecret(sslSecret *corev1.Secret, trustStoreType string, trustSecret *corev1.Secret, isConsole bool) (*SslArguments, error) { + sslArgs := SslArguments{ + IsConsole: isConsole, + } + + isCertSecret, isValid := IsSecretFromCert(sslSecret) + + if isCertSecret && !isValid { + return nil, fmt.Errorf("certificate secret not have correct keys") + } + + if isCertSecret { + //internally we use PEM to represent PEMCFG + sslArgs.KeyStoreType = "PEM" + } + + sep := "/" + if !isConsole { + sep = "\\/" + } + + volumeDir := sep + "etc" + sep + sslSecret.Name + "-volume" + + if sslArgs.KeyStoreType == "PEM" { + uniqueName := sslSecret.Name + ".pemcfg" + sslArgs.KeyStorePath = uniqueName + sslArgs.PemCfgs = []string{ + sslArgs.KeyStorePath, + "/etc/" + sslSecret.Name + "-volume/tls.key", + "/etc/" + sslSecret.Name + "-volume/tls.crt", + } + } else { + // if it is the cert-secret, we throw an error + if isCertSecret { + return nil, fmt.Errorf("certificate only supports PEM keystore type, actual: %v", sslArgs.KeyStoreType) + } + + // old user secret + sslArgs.KeyStorePassword = &defaultKeyStorePassword + sslArgs.KeyStorePath = volumeDir + sep + "broker.ks" + if passwordString := string(sslSecret.Data["keyStorePassword"]); passwordString != "" { + if !isConsole { + passwordString = strings.ReplaceAll(passwordString, "/", sep) + } + sslArgs.KeyStorePassword = &passwordString + } + if keyPathString := string(sslSecret.Data["keyStorePath"]); keyPathString != "" { + if !isConsole { + keyPathString = strings.ReplaceAll(keyPathString, "/", sep) + } + sslArgs.KeyStorePath = keyPathString + } + } + + if trustSecret == nil { + if !isCertSecret { + //old user secret + trustSecret = sslSecret + } else { + //user didn't specify truststore + return &sslArgs, nil + } + } + + isBundleSecret := isSecretFromBundle(trustSecret) + + if isBundleSecret { + if trustStoreType != "" { + if trustStoreType != "PEM" { + return nil, fmt.Errorf("ca bundle secret must have PEM trust store type") + } + } + sslArgs.TrustStoreType = "PEM" + } else { + if trustStoreType != "" { + sslArgs.TrustStoreType = trustStoreType + } + + } + + trustVolumeDir := sep + "etc" + sep + trustSecret.Name + "-volume" + + if isBundleSecret { + bundleName := getBundleNameFromSecret(trustSecret) + sslArgs.TrustStorePath = trustVolumeDir + sep + bundleName + } else { + //old user Secret + sslArgs.TrustStorePassword = &defaultKeyStorePassword + sslArgs.TrustStorePath = trustVolumeDir + sep + "client.ts" + if trustPassword := string(trustSecret.Data["trustStorePassword"]); trustPassword != "" { + if !isConsole { + trustPassword = strings.ReplaceAll(trustPassword, "/", sep) + } + sslArgs.TrustStorePassword = &trustPassword + } + if trustStorePath := string(trustSecret.Data["trustStorePath"]); trustStorePath != "" { + if !isConsole { + trustStorePath = strings.ReplaceAll(trustStorePath, "/", sep) + } + sslArgs.TrustStorePath = trustStorePath + } + } + + return &sslArgs, nil +} + +func CfgToSecretName(cfgFileName string) string { + return strings.ReplaceAll(cfgFileName, ".", "-") +} diff --git a/pkg/utils/common/common.go b/pkg/utils/common/common.go index 77ed56e18..8e42bc378 100644 --- a/pkg/utils/common/common.go +++ b/pkg/utils/common/common.go @@ -657,3 +657,31 @@ func ApplyAnnotations(objectMeta *metav1.ObjectMeta, annotations map[string]stri } } } + +func ToResourceList(resourceMap map[reflect.Type]map[string]rtclient.Object) []rtclient.Object { + var resourceList []rtclient.Object + for _, resMap := range resourceMap { + for _, res := range resMap { + resourceList = append(resourceList, res) + } + } + return resourceList +} + +func HasVolumeInStatefulset(ss *appsv1.StatefulSet, volumeName string) bool { + for _, v := range ss.Spec.Template.Spec.Volumes { + if v.Name == volumeName { + return true + } + } + return false +} + +func HasVolumeMount(container *corev1.Container, mountName string) bool { + for _, vm := range container.VolumeMounts { + if vm.Name == mountName { + return true + } + } + return false +} diff --git a/pkg/utils/jolokia_client/jolokia_client.go b/pkg/utils/jolokia_client/jolokia_client.go index 6832bad08..da12eacc9 100644 --- a/pkg/utils/jolokia_client/jolokia_client.go +++ b/pkg/utils/jolokia_client/jolokia_client.go @@ -188,7 +188,7 @@ func getEnvVarValueFromSecret(envName string, varSource *corev1.EnvVarSource, na stringDataMap := map[string]string{ envName: "", } - theSecret := secrets.NewSecret(namespacedName, secretName, stringDataMap, labels) + theSecret := secrets.NewSecret(namespacedName, stringDataMap, labels) var err error = nil if err = resources.Retrieve(namespacedName, client, theSecret); err != nil { if errors.IsNotFound(err) {