From 66cb587511b2252af860111479db9d70c4b2556e Mon Sep 17 00:00:00 2001 From: Cesar Celis Hernandez Date: Wed, 7 Feb 2024 16:14:26 -0500 Subject: [PATCH] Renew external certificates via the added multi-tenancy support (#1973) Renew external certs --- pkg/controller/monitoring.go | 5 ++ pkg/controller/tenants.go | 99 ++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/pkg/controller/monitoring.go b/pkg/controller/monitoring.go index 0e5b2e2df6d..10ffe114797 100644 --- a/pkg/controller/monitoring.go +++ b/pkg/controller/monitoring.go @@ -120,6 +120,11 @@ func (c *Controller) updateHealthStatusForTenant(tenant *miniov2.Tenant) error { if err != nil { // show the error and continue klog.Infof("'%s/%s' Failed to get cluster health: %v", tenant.Namespace, tenant.Name, err) + err = c.renewExternalCerts(context.Background(), tenant, err) + if err != nil { + klog.Errorf("There was an error on certificate renewal %s", err) + return err + } return nil } diff --git a/pkg/controller/tenants.go b/pkg/controller/tenants.go index 5f7d553e282..d869a167bdc 100644 --- a/pkg/controller/tenants.go +++ b/pkg/controller/tenants.go @@ -19,6 +19,10 @@ package controller import ( "context" "errors" + "strings" + + corev1 "k8s.io/api/core/v1" + "k8s.io/klog/v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -45,6 +49,101 @@ func (c *Controller) getTenantConfiguration(ctx context.Context, tenant *miniov2 return tenantConfiguration, nil } +// renewCert will renew one certificate at a time +func (c *Controller) renewCert(secret corev1.Secret, index int, tenant *miniov2.Tenant) error { + // Check if secret starts with "operator-ca-tls-" + secretName := OperatorCATLSSecretName + "-" + // If the secret does not start with "operator-ca-tls-" then no need to continue + if !strings.HasPrefix(secret.Name, secretName) { + klog.Info("No secret found for multi-tenancy architecture of external certificates") + return nil + } + klog.Infof("%d external secret found: %s", index, secret.Name) + klog.Info("We are going to renew the external certificate for the tenant...") + // Get the new certificate generated by cert-manager + tenantSecretName := tenant.Spec.ExternalCertSecret[0].Name + data, err := c.kubeClientSet.CoreV1().Secrets(tenant.Namespace).Get(context.Background(), tenantSecretName, metav1.GetOptions{}) + if err != nil { + klog.Errorf("Couldn't get the certificate due to error %s", err) + return err + } + if data == nil || len(data.Data) <= 0 { + klog.Errorf("certificate's data can't be empty: %s", data) + return errors.New("empty cert data") + } + CACertificate := data.Data["ca.crt"] + if CACertificate == nil || len(CACertificate) <= 0 { + klog.Errorf("ca.crt certificate data can't be empty: %s", CACertificate) + return errors.New("empty cert ca data") + } + klog.Info("certificate data is not empty, proceed with renewal") + // Delete the secret that starts with operator-ca-tls- because it is expired + err = c.kubeClientSet.CoreV1().Secrets(miniov2.GetNSFromFile()).Delete(context.Background(), secret.Name, metav1.DeleteOptions{}) + if err != nil { + klog.Infof("There was an error when deleting the secret: %s", err) + return err + } + // Create the new secret that contains the new certificate + newSecret := &corev1.Secret{ + Type: "Opaque", + ObjectMeta: metav1.ObjectMeta{ + Name: secret.Name, + Namespace: miniov2.GetNSFromFile(), + }, + Data: map[string][]byte{ + "ca.crt": CACertificate, + }, + } + _, err = c.kubeClientSet.CoreV1().Secrets(miniov2.GetNSFromFile()).Create(context.Background(), newSecret, metav1.CreateOptions{}) + if err != nil { + klog.Errorf("Secret not created %s", err) + return err + } + // Append it + c.fetchTransportCACertificates() + // Reload CA certificates + c.createTransport() + // Rollout the Operator Deployment to use new certificate and trust the tenant. + operatorDeployment, err := c.kubeClientSet.AppsV1().Deployments(miniov2.GetNSFromFile()).Get(context.Background(), miniov2.GetNSFromFile(), metav1.GetOptions{}) + if err != nil || operatorDeployment == nil { + klog.Errorf("Couldn't retrieve the deployment %s", err) + return err + } + operatorDeployment.Spec.Template.ObjectMeta.Name = miniov2.GetNSFromFile() + operatorDeployment, err = c.kubeClientSet.AppsV1().Deployments(miniov2.GetNSFromFile()).Update(context.Background(), operatorDeployment, metav1.UpdateOptions{}) + if err != nil { + klog.Errorf("There was an error on deployment update %s", err) + return err + } + klog.Info("external certificate successfully renewed for the tenant") + return nil +} + +// renewExternalCerts renews external certificates when they expire, ensuring that the Operator trusts its tenants. +func (c *Controller) renewExternalCerts(ctx context.Context, tenant *miniov2.Tenant, err error) error { + if strings.Contains(err.Error(), "failed to verify certificate") { + externalCertSecret := tenant.Spec.ExternalCertSecret + klog.Info("Let's check if there is an external cert for the tenant...") + if externalCertSecret != nil { + // Check that there is a secret that starts with "operator-ca-tls-" to proceed with the renewal + secretsAvailableAtOperatorNS, err := c.kubeClientSet.CoreV1().Secrets(miniov2.GetNSFromFile()).List(context.Background(), metav1.ListOptions{}) + if err != nil { + klog.Info("No external certificates are found under the multi-tenancy architecture to handle.") + return nil + } + klog.Info("there are secret(s) for the operator") + for index, secret := range secretsAvailableAtOperatorNS.Items { + err = c.renewCert(secret, index, tenant) + if err != nil { + klog.Errorf("There was an error while renewing the cert: %s", err) + return err + } + } + } + } + return nil +} + // getTenantCredentials returns a combination of env, credsSecret and Configuration tenant credentials func (c *Controller) getTenantCredentials(ctx context.Context, tenant *miniov2.Tenant) (map[string][]byte, error) { // Configuration for tenant can be passed using 2 different sources, tenant.spec.env and config.env secret