github.com/verrazzano/verrazzano@v1.7.0/platform-operator/controllers/secrets/verrazzano_tls_secret.go (about)

     1  // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package secrets
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"time"
    10  
    11  	vzconst "github.com/verrazzano/verrazzano/pkg/constants"
    12  	"github.com/verrazzano/verrazzano/pkg/log"
    13  	"github.com/verrazzano/verrazzano/platform-operator/constants"
    14  	"go.uber.org/zap"
    15  	appsv1 "k8s.io/api/apps/v1"
    16  	corev1 "k8s.io/api/core/v1"
    17  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    18  	"k8s.io/apimachinery/pkg/types"
    19  	ctrl "sigs.k8s.io/controller-runtime"
    20  	"sigs.k8s.io/controller-runtime/pkg/client"
    21  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    22  )
    23  
    24  const rancherDeploymentName = "rancher"
    25  const mcCABundleKey = "ca-bundle"
    26  
    27  var fetchSecretFailureTemplate = "Failed to fetch secret %s/%s: %v"
    28  
    29  // reconcileVerrazzanoTLS - Update the Verrazzano private CA bundle
    30  func (r *VerrazzanoSecretsReconciler) reconcileVerrazzanoTLS(ctx context.Context, secret types.NamespacedName, caKey string) (ctrl.Result, error) {
    31  
    32  	// Get the secret
    33  	caSecret := corev1.Secret{}
    34  	if err := r.Get(ctx, secret, &caSecret); err != nil {
    35  		if apierrors.IsNotFound(err) {
    36  			// Secret may have been deleted, skip reconcile
    37  			zap.S().Infof("Secret %s does not exist, skipping reconcile", secret)
    38  			return ctrl.Result{}, nil
    39  		}
    40  		// Secret should never be not found, unless we're running while installation is still underway
    41  		zap.S().Errorf(fetchSecretFailureTemplate,
    42  			secret.Namespace, secret.Name, err)
    43  		return newRequeueWithDelay(), nil
    44  	}
    45  	zap.S().Debugf("Fetched secret %s/%s ", secret.Namespace, secret.Name)
    46  
    47  	// Get the resource logger needed to log message using 'progress' and 'once' methods
    48  	if result, err := r.initLogger(secret, &caSecret); err != nil {
    49  		return result, err
    50  	}
    51  
    52  	// Update the Verrazzano private CA bundle; the source of truth from a VZ perspective
    53  	_, err := r.updateSecret(vzconst.VerrazzanoSystemNamespace, vzconst.PrivateCABundle,
    54  		vzconst.CABundleKey, caKey, &caSecret, false)
    55  	if err != nil {
    56  		return newRequeueWithDelay(), nil
    57  	}
    58  	return ctrl.Result{}, nil
    59  }
    60  
    61  // reconcileVerrazzanoCABundleCopies - The Verrazzano private CA secret has changed. Propagate that change into the following:
    62  //   - The Rancher TLS CA secret and restart Rancher deployment
    63  //   - The multi-cluster verrazzano-local-ca-bundle secret which maintains a copy of the local CA bundle to sync with remote clusters in the multi-cluster case
    64  //
    65  // Certs issued from ACME issuers do not populate the "ca.crt" field in leaf cert secrets.  In those scenarios those copies are set up
    66  // once during VZ resource reconciliation until if/when the VZ issuer configuration is changed.
    67  //
    68  // These copies are only maintained when private CA configurations are involved; self-signed, custom CA, and Let's Encrypt staging configurations
    69  func (r *VerrazzanoSecretsReconciler) reconcileVerrazzanoCABundleCopies() (ctrl.Result, error) {
    70  	// Use private bundle secret to update copies
    71  	privateBundleSecret := &corev1.Secret{}
    72  	err := r.Get(context.TODO(), types.NamespacedName{Name: vzconst.PrivateCABundle, Namespace: vzconst.VerrazzanoSystemNamespace}, privateBundleSecret)
    73  	if otherErr := client.IgnoreNotFound(err); otherErr != nil {
    74  		return newRequeueWithDelay(), otherErr
    75  	}
    76  
    77  	// Get the resource logger needed to log message using 'progress' and 'once' methods
    78  	if result, err := r.initLogger(client.ObjectKeyFromObject(privateBundleSecret), privateBundleSecret); err != nil {
    79  		return result, err
    80  	}
    81  
    82  	// Update the Rancher TLS CA secret
    83  	result, err := r.updateSecret(vzconst.RancherSystemNamespace, vzconst.RancherTLSCA,
    84  		vzconst.RancherTLSCAKey, vzconst.CABundleKey, privateBundleSecret, false)
    85  	if err != nil {
    86  		return newRequeueWithDelay(), nil
    87  	}
    88  
    89  	// Restart Rancher pod to have the updated CA secret value reflected in the pod
    90  	if result == controllerutil.OperationResultUpdated {
    91  		if err := r.restartRancherPod(); err != nil {
    92  			return newRequeueWithDelay(), err
    93  		}
    94  	}
    95  
    96  	if !r.multiclusterNamespaceExists() {
    97  		// Multicluster namespace doesn't exist yet, nothing to do so requeue
    98  		return newRequeueWithDelay(), nil
    99  	}
   100  
   101  	// Update the verrazzano-local-ca-bundle secret
   102  	if _, err := r.updateSecret(constants.VerrazzanoMultiClusterNamespace, constants.VerrazzanoLocalCABundleSecret,
   103  		mcCABundleKey, vzconst.CABundleKey, privateBundleSecret, true); err != nil {
   104  		return newRequeueWithDelay(), nil
   105  	}
   106  	return ctrl.Result{}, nil
   107  }
   108  
   109  func (r *VerrazzanoSecretsReconciler) updateSecret(namespace string, name string, destCAKey string,
   110  	sourceCAKey string, sourceSecret *corev1.Secret, isCreateAllowed bool) (controllerutil.OperationResult, error) {
   111  	// Get the secret
   112  	secret := corev1.Secret{}
   113  	err := r.Get(context.TODO(), client.ObjectKey{
   114  		Namespace: namespace,
   115  		Name:      name,
   116  	}, &secret)
   117  	if err != nil {
   118  		if !apierrors.IsNotFound(err) {
   119  			r.log.Errorf(fetchSecretFailureTemplate, namespace, name, err)
   120  			return controllerutil.OperationResultNone, err
   121  		}
   122  		if !isCreateAllowed {
   123  			r.log.Debugf("Secret %s/%s not found, nothing to do", namespace, name)
   124  			return controllerutil.OperationResultNone, nil
   125  		}
   126  		// Secret was not found, make a new one
   127  		secret = corev1.Secret{}
   128  		secret.Name = name
   129  		secret.Namespace = namespace
   130  	}
   131  
   132  	result, err := controllerutil.CreateOrUpdate(context.TODO(), r.Client, &secret, func() error {
   133  		// We only want to update the target secret IFF the secret/key in the source secret/key exist;
   134  		// we are keeping private CA bundles in sync on rotation only, the modules manage the lifecycle
   135  		// of the target secrets on reconcile of the VZ CR
   136  		sourceBundle, exists := sourceSecret.Data[sourceCAKey]
   137  		if !exists && !isCreateAllowed {
   138  			zap.S().Debugf("Source key %s does not exist in secret %s/%s, nothing to do ", sourceCAKey,
   139  				sourceSecret.Namespace, sourceSecret.Name)
   140  			return nil
   141  		}
   142  		zap.S().Debugf("Updating CA secret with data from %s key of %s/%s secret ", sourceCAKey,
   143  			sourceSecret.Namespace, sourceSecret.Name)
   144  		if secret.Data == nil {
   145  			secret.Data = make(map[string][]byte)
   146  		}
   147  		secret.Data[destCAKey] = sourceBundle
   148  		return nil
   149  	})
   150  
   151  	if err != nil {
   152  		r.log.ErrorfThrottled("Failed to create or update secret %s/%s: %s", name, namespace, err.Error())
   153  		return controllerutil.OperationResultNone, err
   154  	}
   155  
   156  	r.log.Debugf("Created or updated secret %s/%s (result: %v)", name, namespace, result)
   157  	return result, nil
   158  }
   159  
   160  // restartRancherPod adds an annotation to the Rancher deployment template to restart the Rancher pods
   161  func (r *VerrazzanoSecretsReconciler) restartRancherPod() error {
   162  	deployment := appsv1.Deployment{}
   163  	if err := r.Get(context.TODO(), types.NamespacedName{Namespace: vzconst.RancherSystemNamespace,
   164  		Name: rancherDeploymentName}, &deployment); err != nil {
   165  		if apierrors.IsNotFound(err) {
   166  			r.log.Debugf("Rancher deployment %s/%s not found, nothing to do",
   167  				vzconst.RancherSystemNamespace, rancherDeploymentName)
   168  			return nil
   169  		}
   170  		r.log.ErrorfThrottled("Failed getting Rancher deployment %s/%s to restart pod: %v",
   171  			vzconst.RancherSystemNamespace, rancherDeploymentName, err)
   172  		return err
   173  	}
   174  
   175  	// annotate the deployment to do a restart of the pod
   176  	if deployment.Spec.Template.ObjectMeta.Annotations == nil {
   177  		deployment.Spec.Template.ObjectMeta.Annotations = make(map[string]string)
   178  	}
   179  	deployment.Spec.Template.ObjectMeta.Annotations[vzconst.VerrazzanoRestartAnnotation] = time.Now().String()
   180  
   181  	if err := r.Update(context.TODO(), &deployment); err != nil {
   182  		return log.ConflictWithLog(fmt.Sprintf("Failed updating deployment %s/%s", deployment.Namespace, deployment.Name), err, zap.S())
   183  	}
   184  	r.log.Infof("Updated Rancher deployment %s/%s with restart annotation to force a pod restart",
   185  		deployment.Namespace, deployment.Name)
   186  	return nil
   187  }