github.com/verrazzano/verrazzano@v1.7.0/application-operator/mcagent/mcagent_cluster_secrets.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 mcagent
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  
    10  	"github.com/verrazzano/verrazzano/application-operator/constants"
    11  	clustersapi "github.com/verrazzano/verrazzano/cluster-operator/apis/clusters/v1alpha1"
    12  	"github.com/verrazzano/verrazzano/pkg/certs"
    13  	"github.com/verrazzano/verrazzano/pkg/mcconstants"
    14  	corev1 "k8s.io/api/core/v1"
    15  	"sigs.k8s.io/controller-runtime/pkg/client"
    16  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    17  )
    18  
    19  const (
    20  	keyCaCrtNoDot = "cacrt"
    21  )
    22  
    23  // Synchronize Secret objects to the local cluster
    24  func (s *Syncer) syncClusterCAs() (controllerutil.OperationResult, error) {
    25  	managedClusterResult, err := s.syncRegistrationFromAdminCluster()
    26  	if err != nil {
    27  		s.Log.Errorf("Error syncing Admin Cluster CA: %v", err)
    28  	}
    29  	err = s.syncLocalClusterCA()
    30  	if err != nil {
    31  		s.Log.Errorf("Error syncing Local Cluster CA: %v", err)
    32  	}
    33  	return managedClusterResult, nil
    34  }
    35  
    36  // syncAgentSecretFromAdminCluster - synchronize the agent secret from admin cluster including
    37  // kubeconfig, cluster name -- update local agent secret if any of those change
    38  func (s *Syncer) syncAgentSecretFromAdminCluster() (controllerutil.OperationResult, error) {
    39  	opResult := controllerutil.OperationResultNone
    40  
    41  	// Get the managed cluster registration secret for THIS managed cluster, from the admin cluster.
    42  	// This will be used to sync registration information here on the managed cluster.
    43  	adminAgentSecret := corev1.Secret{}
    44  	err := s.AdminClient.Get(s.Context, client.ObjectKey{
    45  		Namespace: constants.VerrazzanoMultiClusterNamespace,
    46  		Name:      getAgentSecretName(s.ManagedClusterName),
    47  	}, &adminAgentSecret)
    48  	if err != nil {
    49  		return opResult, err
    50  	}
    51  
    52  	agentSecret := corev1.Secret{}
    53  	agentSecret.Name = constants.MCAgentSecret
    54  	agentSecret.Namespace = constants.VerrazzanoSystemNamespace
    55  	return controllerutil.CreateOrUpdate(s.Context, s.LocalClient, &agentSecret, func() error {
    56  		// Set info from admin agent secret
    57  		agentSecret.Data[mcconstants.KubeconfigKey] = adminAgentSecret.Data[mcconstants.KubeconfigKey]
    58  		agentSecret.Data[mcconstants.ManagedClusterNameKey] = adminAgentSecret.Data[mcconstants.ManagedClusterNameKey]
    59  		return nil
    60  	})
    61  }
    62  
    63  // syncRegistrationFromAdminCluster - synchronize the admin cluster registration info including
    64  // CA cert, URLs and credentials -- update local registration if any of those change
    65  func (s *Syncer) syncRegistrationFromAdminCluster() (controllerutil.OperationResult, error) {
    66  
    67  	opResult := controllerutil.OperationResultNone
    68  	// Get the cluster CA secret from the admin cluster - for the CA secret, this is considered
    69  	// the source of truth
    70  	adminCASecret := corev1.Secret{}
    71  	err := s.AdminClient.Get(s.Context, client.ObjectKey{
    72  		Namespace: constants.VerrazzanoMultiClusterNamespace,
    73  		Name:      constants.VerrazzanoLocalCABundleSecret,
    74  	}, &adminCASecret)
    75  	if err != nil {
    76  		return opResult, err
    77  	}
    78  
    79  	// Get the managed cluster registration secret for THIS managed cluster, from the admin cluster.
    80  	// This will be used to sync registration information here on the managed cluster.
    81  	adminRegistrationSecret := corev1.Secret{}
    82  	err = s.AdminClient.Get(s.Context, client.ObjectKey{
    83  		Namespace: constants.VerrazzanoMultiClusterNamespace,
    84  		Name:      getRegistrationSecretName(s.ManagedClusterName),
    85  	}, &adminRegistrationSecret)
    86  	if err != nil {
    87  		return opResult, err
    88  	}
    89  
    90  	// Get the local cluster registration secret
    91  	registrationSecret := corev1.Secret{}
    92  	err = s.LocalClient.Get(s.Context, client.ObjectKey{
    93  		Namespace: constants.VerrazzanoSystemNamespace,
    94  		Name:      constants.MCRegistrationSecret,
    95  	}, &registrationSecret)
    96  	if err != nil {
    97  		return opResult, err
    98  	}
    99  
   100  	// Update the local cluster registration secret if the admin CA certs are different, or if
   101  	// any of the registration info on admin cluster is different
   102  	if !byteSlicesEqualTrimmedWhitespace(registrationSecret.Data[mcconstants.AdminCaBundleKey], adminCASecret.Data[mcconstants.AdminCaBundleKey]) ||
   103  		!registrationInfoEqual(registrationSecret, adminRegistrationSecret) {
   104  		opResult, err = controllerutil.CreateOrUpdate(s.Context, s.LocalClient, &registrationSecret, func() error {
   105  			// Get CA info from admin CA secret
   106  			registrationSecret.Data[mcconstants.AdminCaBundleKey] = adminCASecret.Data[mcconstants.AdminCaBundleKey]
   107  
   108  			// Get other registration info from admin registration secret for this managed cluster
   109  			registrationSecret.Data[mcconstants.ESURLKey] = adminRegistrationSecret.Data[mcconstants.ESURLKey]
   110  			registrationSecret.Data[mcconstants.RegistrationUsernameKey] = adminRegistrationSecret.Data[mcconstants.RegistrationUsernameKey]
   111  			registrationSecret.Data[mcconstants.RegistrationPasswordKey] = adminRegistrationSecret.Data[mcconstants.RegistrationPasswordKey]
   112  			registrationSecret.Data[mcconstants.KeycloakURLKey] = adminRegistrationSecret.Data[mcconstants.KeycloakURLKey]
   113  			registrationSecret.Data[mcconstants.ESCaBundleKey] = adminRegistrationSecret.Data[mcconstants.ESCaBundleKey]
   114  			registrationSecret.Data[mcconstants.JaegerOSURLKey] = adminRegistrationSecret.Data[mcconstants.JaegerOSURLKey]
   115  			registrationSecret.Data[mcconstants.JaegerOSUsernameKey] = adminRegistrationSecret.Data[mcconstants.JaegerOSUsernameKey]
   116  			registrationSecret.Data[mcconstants.JaegerOSPasswordKey] = adminRegistrationSecret.Data[mcconstants.JaegerOSPasswordKey]
   117  			registrationSecret.Data[mcconstants.JaegerOSTLSCAKey] = adminRegistrationSecret.Data[mcconstants.JaegerOSTLSCAKey]
   118  			registrationSecret.Data[mcconstants.JaegerOSTLSCertKey] = adminRegistrationSecret.Data[mcconstants.JaegerOSTLSCertKey]
   119  			registrationSecret.Data[mcconstants.JaegerOSTLSKey] = adminRegistrationSecret.Data[mcconstants.JaegerOSTLSKey]
   120  			registrationSecret.Data[mcconstants.DexURLKey] = adminRegistrationSecret.Data[mcconstants.DexURLKey]
   121  			registrationSecret.Data[mcconstants.OidcProviderKey] = adminRegistrationSecret.Data[mcconstants.OidcProviderKey]
   122  			return nil
   123  		})
   124  		if err != nil {
   125  			s.Log.Errorw(fmt.Sprintf("Failed syncing admin CA certificate: %v", err),
   126  				"Secret", registrationSecret.Name)
   127  		} else {
   128  			s.Log.Infof("Updated local cluster registration secret, result was: %v", opResult)
   129  		}
   130  	}
   131  
   132  	return opResult, nil
   133  }
   134  
   135  func registrationInfoEqual(regSecret1 corev1.Secret, regSecret2 corev1.Secret) bool {
   136  	return byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.ESURLKey],
   137  		regSecret2.Data[mcconstants.ESURLKey]) &&
   138  		byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.KeycloakURLKey],
   139  			regSecret2.Data[mcconstants.KeycloakURLKey]) &&
   140  		byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.RegistrationUsernameKey],
   141  			regSecret2.Data[mcconstants.RegistrationUsernameKey]) &&
   142  		byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.RegistrationPasswordKey],
   143  			regSecret2.Data[mcconstants.RegistrationPasswordKey]) &&
   144  		byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.ESCaBundleKey],
   145  			regSecret2.Data[mcconstants.ESCaBundleKey]) &&
   146  		byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.JaegerOSURLKey],
   147  			regSecret2.Data[mcconstants.JaegerOSURLKey]) &&
   148  		byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.JaegerOSUsernameKey],
   149  			regSecret2.Data[mcconstants.JaegerOSUsernameKey]) &&
   150  		byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.JaegerOSPasswordKey],
   151  			regSecret2.Data[mcconstants.JaegerOSPasswordKey]) &&
   152  		byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.JaegerOSTLSCAKey],
   153  			regSecret2.Data[mcconstants.JaegerOSTLSCAKey]) &&
   154  		byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.JaegerOSTLSCertKey],
   155  			regSecret2.Data[mcconstants.JaegerOSTLSCertKey]) &&
   156  		byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.JaegerOSTLSKey],
   157  			regSecret2.Data[mcconstants.JaegerOSTLSKey]) &&
   158  		byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.DexURLKey],
   159  			regSecret2.Data[mcconstants.DexURLKey]) &&
   160  		byteSlicesEqualTrimmedWhitespace(regSecret1.Data[mcconstants.OidcProviderKey],
   161  			regSecret2.Data[mcconstants.OidcProviderKey])
   162  }
   163  
   164  // syncLocalClusterCA - synchronize the local cluster CA cert -- update admin copy if local CA changes
   165  func (s *Syncer) syncLocalClusterCA() error {
   166  	localCASecretData, err := certs.GetLocalClusterCABundleData(s.Log, s.LocalClient, s.Context)
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	// Get the managed cluster CA secret from the admin cluster
   172  	vmc := clustersapi.VerrazzanoManagedCluster{}
   173  	err = s.AdminClient.Get(s.Context, client.ObjectKey{
   174  		Name:      s.ManagedClusterName,
   175  		Namespace: constants.VerrazzanoMultiClusterNamespace,
   176  	}, &vmc)
   177  	if err != nil {
   178  		return err
   179  	}
   180  
   181  	// No CA secret for this managed cluster - nothing to sync.
   182  	if vmc.Spec.CASecret == "" {
   183  		return nil
   184  	}
   185  
   186  	vmcCASecret := corev1.Secret{}
   187  	err = s.AdminClient.Get(s.Context, client.ObjectKey{
   188  		Namespace: constants.VerrazzanoMultiClusterNamespace,
   189  		Name:      vmc.Spec.CASecret,
   190  	}, &vmcCASecret)
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	// Update the VMC cluster CA secret if the local CA is different
   196  	if !byteSlicesEqualTrimmedWhitespace(vmcCASecret.Data[keyCaCrtNoDot], localCASecretData) {
   197  		result, err := controllerutil.CreateOrUpdate(s.Context, s.AdminClient, &vmcCASecret, func() error {
   198  			vmcCASecret.Data[keyCaCrtNoDot] = localCASecretData
   199  			return nil
   200  		})
   201  		if err != nil {
   202  			s.Log.Errorw(fmt.Sprintf("Failed syncing local CA certificate: %v", err),
   203  				"Secret", vmcCASecret.Name)
   204  		} else {
   205  			s.Log.Infof("Updated VMC cluster CA secret on admin cluster, result was: %v", result)
   206  		}
   207  	}
   208  
   209  	return nil
   210  }
   211  
   212  func byteSlicesEqualTrimmedWhitespace(byteSlice1, byteSlice2 []byte) bool {
   213  	a := bytes.Trim(byteSlice1, " \t\n\r")
   214  	b := bytes.Trim(byteSlice2, " \t\n\r")
   215  	return bytes.Equal(a, b)
   216  }
   217  
   218  // Generate the common name used by all resources specific to a given managed cluster
   219  func generateManagedResourceName(clusterName string) string {
   220  	return fmt.Sprintf("verrazzano-cluster-%s", clusterName)
   221  }
   222  
   223  // getRegistrationSecretName returns the registration secret name for a managed cluster on the admin
   224  // cluster
   225  func getRegistrationSecretName(clusterName string) string {
   226  	const registrationSecretSuffix = "-registration"
   227  	return generateManagedResourceName(clusterName) + registrationSecretSuffix
   228  }
   229  
   230  // getAgentSecretName returns the agent secret name for a managed cluster on the admin
   231  // cluster
   232  func getAgentSecretName(clusterName string) string {
   233  	const suffix = "-agent"
   234  	return generateManagedResourceName(clusterName) + suffix
   235  }