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 }