diff --git a/internal/controllers/certmanager.go b/internal/controllers/certmanager.go index 93bdf044..ae9fa875 100644 --- a/internal/controllers/certmanager.go +++ b/internal/controllers/certmanager.go @@ -122,6 +122,48 @@ func (r *Reconciler) setupTLS(ctx context.Context, cr *model.CryostatInstance) ( return nil, err } + secret, err := r.GetCertificateSecret(ctx, caCert) + if err != nil { + return nil, err + } + // Copy Cryostat CA secret in each target namespace + for _, ns := range cr.TargetNamespaces { + if ns != cr.InstallNamespace { + namespaceSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: caCert.Spec.SecretName, + Namespace: ns, + }, + Type: corev1.SecretTypeOpaque, + } + err = r.createOrUpdateSecret(ctx, namespaceSecret, cr.Object, func() error { + if namespaceSecret.Data == nil { + namespaceSecret.Data = map[string][]byte{} + } + namespaceSecret.Data[corev1.TLSCertKey] = secret.Data[corev1.TLSCertKey] + return nil + }) + if err != nil { + return nil, err + } + } + } + // Delete any Cryostat CA secrets in target namespaces that are no longer requested + for _, ns := range toDelete(cr) { + if ns != cr.InstallNamespace { + namespaceSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: caCert.Spec.SecretName, + Namespace: ns, + }, + } + err = r.deleteSecret(ctx, namespaceSecret) + if err != nil { + return nil, err + } + } + } + // Get the Cryostat CA certificate bytes from certificate secret caBytes, err := r.getCertficateBytes(ctx, caCert) if err != nil { @@ -131,6 +173,25 @@ func (r *Reconciler) setupTLS(ctx context.Context, cr *model.CryostatInstance) ( return tlsConfig, nil } +func (r *Reconciler) finalizeTLS(ctx context.Context, cr *model.CryostatInstance) error { + caCert := resources.NewCryostatCACert(cr) + for _, ns := range cr.TargetNamespaces { + if ns != cr.InstallNamespace { + namespaceSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: caCert.Spec.SecretName, + Namespace: ns, + }, + } + err := r.deleteSecret(ctx, namespaceSecret) + if err != nil { + return err + } + } + } + return nil +} + func (r *Reconciler) setCertSecretOwner(ctx context.Context, owner metav1.Object, certs ...*certv1.Certificate) error { // Make Cryostat CR controller of secrets created by cert-manager for _, cert := range certs { diff --git a/internal/controllers/clustercryostat_controller_test.go b/internal/controllers/clustercryostat_controller_test.go index 4c93df77..a5227600 100644 --- a/internal/controllers/clustercryostat_controller_test.go +++ b/internal/controllers/clustercryostat_controller_test.go @@ -20,6 +20,7 @@ import ( "github.com/cryostatio/cryostat-operator/internal/controllers" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" ) @@ -62,6 +63,10 @@ var _ = Describe("ClusterCryostatController", func() { t.expectMainDeployment() }) + It("should create CA Cert secret in each namespace", func() { + t.expectCertificates() + }) + It("should create RBAC in each namespace", func() { t.expectRBAC() }) @@ -80,7 +85,9 @@ var _ = Describe("ClusterCryostatController", func() { *cr.TargetNamespaceStatus = targetNamespaces t.objs = append(t.objs, cr.Object, t.NewRoleBinding(targetNamespaces[0]), - t.NewRoleBinding(targetNamespaces[1])) + t.NewRoleBinding(targetNamespaces[1]), + t.NewCACertSecret(targetNamespaces[0]), + t.NewCACertSecret(targetNamespaces[1])) }) It("should create the expected main deployment", func() { t.expectMainDeployment() @@ -94,6 +101,16 @@ var _ = Describe("ClusterCryostatController", func() { Expect(err).ToNot(BeNil()) Expect(errors.IsNotFound(err)).To(BeTrue()) }) + It("leave CA Cert secret for the first namespace", func() { + t.expectCertificates() + }) + It("should remove CA Cert secret from the second namespace", func() { + caCert := t.NewCACert() + secret := &corev1.Secret{} + err := t.Client.Get(context.Background(), types.NamespacedName{Name: caCert.Name, Namespace: targetNamespaces[1]}, secret) + Expect(err).ToNot(BeNil()) + Expect(errors.IsNotFound(err)).To(BeTrue()) + }) It("should update the target namespaces in Status", func() { t.expectTargetNamespaces() }) diff --git a/internal/controllers/common/finalizer_utils.go b/internal/controllers/common/finalizer_utils.go index dc36890f..02bd1d67 100644 --- a/internal/controllers/common/finalizer_utils.go +++ b/internal/controllers/common/finalizer_utils.go @@ -22,7 +22,7 @@ import ( ) // AddFinalizer adds the provided finalizer to the object and updates it in the cluster -func AddFinalizer(ctx context.Context, client client.Client, obj controllerutil.Object, finalizer string) error { +func AddFinalizer(ctx context.Context, client client.Client, obj client.Object, finalizer string) error { log.Info("adding finalizer to object", "namespace", obj.GetNamespace(), "name", obj.GetName()) controllerutil.AddFinalizer(obj, finalizer) err := client.Update(ctx, obj) @@ -35,7 +35,7 @@ func AddFinalizer(ctx context.Context, client client.Client, obj controllerutil. } // RemoveFinalizer removes the provided finalizer from the object and updates it in the cluster -func RemoveFinalizer(ctx context.Context, client client.Client, obj controllerutil.Object, finalizer string) error { +func RemoveFinalizer(ctx context.Context, client client.Client, obj client.Object, finalizer string) error { log.Info("removing finalizer from object", "namespace", obj.GetNamespace(), "name", obj.GetName()) controllerutil.RemoveFinalizer(obj, finalizer) err := client.Update(ctx, obj) diff --git a/internal/controllers/reconciler.go b/internal/controllers/reconciler.go index 30b8f4b3..be5d4605 100644 --- a/internal/controllers/reconciler.go +++ b/internal/controllers/reconciler.go @@ -161,6 +161,12 @@ func (r *Reconciler) reconcile(ctx context.Context, cr *model.CryostatInstance) return reconcile.Result{}, err } + // Finalizer for CA Cert secrets + err = r.finalizeTLS(ctx, cr) + if err != nil { + return reconcile.Result{}, err + } + err = common.RemoveFinalizer(ctx, r.Client, cr.Object, cryostatFinalizer) if err != nil { return reconcile.Result{}, err @@ -196,8 +202,15 @@ func (r *Reconciler) reconcile(ctx context.Context, cr *model.CryostatInstance) return reconcile.Result{}, err } + // Reconcile RBAC resources for Cryostat + err = r.reconcileRBAC(ctx, cr) + if err != nil { + return reconcile.Result{}, err + } + // Set up TLS using cert-manager, if available var tlsConfig *resources.TLSConfig + if r.IsCertManagerEnabled(cr) { tlsConfig, err = r.setupTLS(ctx, cr) if err != nil { @@ -230,12 +243,6 @@ func (r *Reconciler) reconcile(ctx context.Context, cr *model.CryostatInstance) } } - // Reconcile RBAC resources for Cryostat - err = r.reconcileRBAC(ctx, cr) - if err != nil { - return reconcile.Result{}, err - } - serviceSpecs := &resources.ServiceSpecs{ InsightsURL: r.InsightsProxy, } diff --git a/internal/controllers/reconciler_test.go b/internal/controllers/reconciler_test.go index b02b8d3d..156209b2 100644 --- a/internal/controllers/reconciler_test.go +++ b/internal/controllers/reconciler_test.go @@ -2336,7 +2336,21 @@ func (t *cryostatTestInput) expectCertificates() { err := t.Client.Get(context.Background(), types.NamespacedName{Name: expectedSecret.Name, Namespace: expectedSecret.Namespace}, secret) Expect(err).ToNot(HaveOccurred()) t.checkMetadata(secret, expectedSecret) - Expect(secret.StringData).To(Equal(secret.StringData)) + Expect(secret.StringData).To(Equal(expectedSecret.StringData)) + + // Check CA Cert secrets in each target namespace + Expect(t.TargetNamespaces).ToNot(BeEmpty()) + for _, ns := range t.TargetNamespaces { + if ns != t.Namespace { + namespaceSecret := t.NewCACertSecret(ns) + secret := &corev1.Secret{} + err := t.Client.Get(context.Background(), types.NamespacedName{Name: namespaceSecret.Name, Namespace: ns}, secret) + Expect(err).ToNot(HaveOccurred()) + t.checkMetadata(secret, namespaceSecret) + Expect(secret.Data).To(Equal(namespaceSecret.Data)) + Expect(secret.Type).To(Equal(namespaceSecret.Type)) + } + } } func (t *cryostatTestInput) expectRBAC() { diff --git a/internal/test/clients.go b/internal/test/clients.go index 221a414f..05632c1a 100644 --- a/internal/test/clients.go +++ b/internal/test/clients.go @@ -92,7 +92,8 @@ func (c *testClient) createCertSecret(ctx context.Context, cert *certv1.Certific Namespace: cert.Namespace, }, Data: map[string][]byte{ - corev1.TLSCertKey: []byte(cert.Name + "-bytes"), + corev1.TLSCertKey: []byte(cert.Name + "-bytes"), + corev1.TLSPrivateKeyKey: []byte(cert.Name + "-key"), }, } err := c.Create(ctx, secret) diff --git a/internal/test/resources.go b/internal/test/resources.go index 79c95726..c9dd11e5 100644 --- a/internal/test/resources.go +++ b/internal/test/resources.go @@ -880,6 +880,19 @@ func (r *TestResources) NewTestService() *corev1.Service { } } +func (r *TestResources) NewCACertSecret(ns string) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: r.Name + "-ca", + Namespace: ns, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + corev1.TLSCertKey: []byte(r.Name + "-ca-bytes"), + }, + } +} + func (r *TestResources) NewGrafanaSecret() *corev1.Secret { return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{