github.com/verrazzano/verrazzano@v1.7.0/cluster-operator/controllers/vmc/sync_registration_secret.go (about) 1 // Copyright (c) 2021, 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 vmc 5 6 import ( 7 "context" 8 "fmt" 9 "strings" 10 11 clusterapi "github.com/verrazzano/verrazzano/cluster-operator/apis/clusters/v1alpha1" 12 "github.com/verrazzano/verrazzano/pkg/constants" 13 "github.com/verrazzano/verrazzano/pkg/mcconstants" 14 "github.com/verrazzano/verrazzano/pkg/vzcr" 15 vzapi "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1" 16 corev1 "k8s.io/api/core/v1" 17 k8net "k8s.io/api/networking/v1" 18 "k8s.io/apimachinery/pkg/api/errors" 19 "k8s.io/apimachinery/pkg/types" 20 "sigs.k8s.io/controller-runtime/pkg/client" 21 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 22 ) 23 24 const operatorOSIngress = "opensearch" 25 const defaultSecretName = "verrazzano" 26 27 // Create a registration secret with the managed cluster information. This secret will 28 // be used on the managed cluster to get information about itself, like the cluster name 29 func (r *VerrazzanoManagedClusterReconciler) syncRegistrationSecret(vmc *clusterapi.VerrazzanoManagedCluster) error { 30 secretName := GetRegistrationSecretName(vmc.Name) 31 managedNamespace := vmc.Namespace 32 33 _, err := r.createOrUpdateRegistrationSecret(vmc, secretName, managedNamespace) 34 if err != nil { 35 return err 36 } 37 38 return nil 39 } 40 41 // Create or update the registration secret 42 func (r *VerrazzanoManagedClusterReconciler) createOrUpdateRegistrationSecret(vmc *clusterapi.VerrazzanoManagedCluster, name string, namespace string) (controllerutil.OperationResult, error) { 43 var secret corev1.Secret 44 secret.Namespace = namespace 45 secret.Name = name 46 47 return controllerutil.CreateOrUpdate(context.TODO(), r.Client, &secret, func() error { 48 err := r.mutateRegistrationSecret(&secret, vmc.Name) 49 if err != nil { 50 return err 51 } 52 // This SetControllerReference call will trigger garbage collection i.e. the secret 53 // will automatically get deleted when the VerrazzanoManagedCluster is deleted 54 return controllerutil.SetControllerReference(vmc, &secret, r.Scheme) 55 }) 56 } 57 58 // Mutate the secret, setting the kubeconfig data 59 func (r *VerrazzanoManagedClusterReconciler) mutateRegistrationSecret(secret *corev1.Secret, manageClusterName string) error { 60 secret.Type = corev1.SecretTypeOpaque 61 62 vzList := vzapi.VerrazzanoList{} 63 err := r.List(context.TODO(), &vzList, &client.ListOptions{}) 64 if err != nil { 65 r.log.Errorf("Failed to list Verrazzano CR: %v", err) 66 return err 67 } 68 if len(vzList.Items) < 1 { 69 return fmt.Errorf("can not find Verrazzano CR") 70 } 71 72 // Get the fluentd configuration for ES URL and secret 73 fluentdESURL, fluentdESSecretName, err := r.getVzESURLSecret(&vzList) 74 if err != nil { 75 return err 76 } 77 78 // Decide which ES URL to use. 79 // If the fluentd OPENSEARCH_URL is the default "http://verrazzano-authproxy-opensearch:8775", use VMI ES ingress URL. 80 // If the fluentd OPENSEARCH_URL is not the default, meaning it is a custom ES, use the external ES URL. 81 esURL := fluentdESURL 82 if esURL == constants.DefaultOpensearchURL { 83 esURL, err = r.getESURL(vzList) 84 if err != nil { 85 return err 86 } 87 } 88 89 // Get the CA bundle needed to connect to the admin keycloak 90 adminCaBundle, err := r.getAdminCaBundle() 91 if err != nil { 92 return err 93 } 94 95 // Decide which ES secret to use for username/password and password. 96 // If the fluentd opensearchSecret is the default "verrazzano", use VerrazzanoESInternal secret for username/password, and adminCaBundle for ES CA bundle. 97 // if the fluentd opensearchSecret is not the default, meaning it is a custom secret, use its username/password and CA bundle. 98 var esCaBundle []byte 99 var esUsername []byte 100 var esPassword []byte 101 if fluentdESSecretName != "verrazzano" { 102 esSecret, err := r.getSecret(constants.VerrazzanoSystemNamespace, fluentdESSecretName, true) 103 if err != nil { 104 return err 105 } 106 esCaBundle = esSecret.Data[mcconstants.FluentdESCaBundleKey] 107 esUsername = esSecret.Data[mcconstants.VerrazzanoUsernameKey] 108 esPassword = esSecret.Data[mcconstants.VerrazzanoPasswordKey] 109 } else { 110 esSecret, err := r.getSecret(constants.VerrazzanoSystemNamespace, constants.VerrazzanoESInternal, false) 111 if err != nil && !errors.IsNotFound(err) { 112 return err 113 } 114 115 if len(esSecret.Data) > 0 { 116 esCaBundle = adminCaBundle 117 esUsername = esSecret.Data[mcconstants.VerrazzanoUsernameKey] 118 esPassword = esSecret.Data[mcconstants.VerrazzanoPasswordKey] 119 } 120 121 } 122 123 // Get the keycloak URL 124 keycloakURL, err := r.getIngressURL("keycloak", "keycloak") 125 if err != nil { 126 return err 127 } 128 129 dexURL, err := r.getIngressURL("verrazzano-auth", "dex") 130 if err != nil { 131 return err 132 } 133 134 if keycloakURL == "" && dexURL == "" { 135 return fmt.Errorf("no oidc provider found") 136 } 137 138 oidcProvider := "keycloak" 139 if vzcr.IsDexEnabled(&vzList.Items[0]) { 140 oidcProvider = "dex" 141 } 142 143 // Get the Jaeger OpenSearch related data if it exists 144 jaegerStorage, err := r.getJaegerOpenSearchConfig(&vzList) 145 if err != nil { 146 return err 147 } 148 149 // Build the secret data 150 secret.Data = map[string][]byte{ 151 mcconstants.ManagedClusterNameKey: []byte(manageClusterName), 152 mcconstants.ESURLKey: []byte(esURL), 153 mcconstants.ESCaBundleKey: esCaBundle, 154 mcconstants.RegistrationUsernameKey: esUsername, 155 mcconstants.RegistrationPasswordKey: esPassword, 156 mcconstants.KeycloakURLKey: []byte(keycloakURL), 157 mcconstants.AdminCaBundleKey: adminCaBundle, 158 mcconstants.JaegerOSURLKey: []byte(jaegerStorage.URL), 159 mcconstants.JaegerOSTLSCAKey: jaegerStorage.CA, 160 mcconstants.JaegerOSTLSKey: jaegerStorage.TLSKey, 161 mcconstants.JaegerOSTLSCertKey: jaegerStorage.TLSCert, 162 mcconstants.JaegerOSUsernameKey: jaegerStorage.username, 163 mcconstants.JaegerOSPasswordKey: jaegerStorage.password, 164 mcconstants.DexURLKey: []byte(dexURL), 165 mcconstants.OidcProviderKey: []byte(oidcProvider), 166 } 167 return nil 168 } 169 170 // GetRegistrationSecretName returns the registration secret name 171 func GetRegistrationSecretName(vmcName string) string { 172 const registrationSecretSuffix = "-registration" 173 return generateManagedResourceName(vmcName) + registrationSecretSuffix 174 } 175 176 // getVzESURLSecret returns the opensearchURL and opensearchSecret from Verrazzano CR 177 func (r *VerrazzanoManagedClusterReconciler) getVzESURLSecret(vzList *vzapi.VerrazzanoList) (string, string, error) { 178 url := constants.DefaultOpensearchURL 179 secret := defaultSecretName 180 // what to do when there is more than one Verrazzano CR 181 for _, vz := range vzList.Items { 182 if vz.Spec.Components.Fluentd != nil { 183 if len(vz.Spec.Components.Fluentd.OpenSearchURL) > 0 { 184 url = vz.Spec.Components.Fluentd.OpenSearchURL 185 } 186 if len(vz.Spec.Components.Fluentd.OpenSearchSecret) > 0 { 187 secret = vz.Spec.Components.Fluentd.OpenSearchSecret 188 } 189 } 190 } 191 return url, secret, nil 192 } 193 194 // Get the opensearch URL. 195 func (r *VerrazzanoManagedClusterReconciler) getESURL(vzList vzapi.VerrazzanoList) (URL string, err error) { 196 if len(vzList.Items) == 0 { 197 return "", nil 198 } 199 if !vzcr.IsOpenSearchEnabled(&vzList.Items[0]) { 200 return "", nil 201 } 202 var Ingress k8net.Ingress 203 nsn := types.NamespacedName{ 204 Namespace: constants.VerrazzanoSystemNamespace, 205 Name: operatorOSIngress, 206 } 207 if err := r.Get(context.TODO(), nsn, &Ingress); err != nil { 208 return "", fmt.Errorf("failed to fetch the OpenSearch ingress %s/%s, %v", nsn.Namespace, nsn.Name, err) 209 } 210 if len(Ingress.Spec.Rules) == 0 { 211 return "", fmt.Errorf("OpenSearch ingress %s/%s missing host entry in rule", nsn.Namespace, nsn.Name) 212 } 213 host := Ingress.Spec.Rules[0].Host 214 if len(Ingress.Spec.Rules) == 0 { 215 return "", fmt.Errorf("OpenSearch ingress %s/%s host field is empty", nsn.Namespace, nsn.Name) 216 } 217 return fmt.Sprintf("https://%s:443", host), nil 218 } 219 220 // Get secret from verrazzano-system namespace 221 func (r *VerrazzanoManagedClusterReconciler) getSecret(namespace string, secretName string, required bool) (corev1.Secret, error) { 222 var secret corev1.Secret 223 nsn := types.NamespacedName{ 224 Namespace: namespace, 225 Name: secretName, 226 } 227 err := r.Get(context.TODO(), nsn, &secret) 228 if err != nil { 229 if !required && errors.IsNotFound(err) { 230 return corev1.Secret{}, err 231 } 232 return corev1.Secret{}, fmt.Errorf("failed to fetch the secret %s/%s, %v", nsn.Namespace, nsn.Name, err) 233 } 234 return secret, nil 235 } 236 237 // Get the CA bundle used by verrazzano ingress and the optional rancher-ca-additional secret 238 func (r *VerrazzanoManagedClusterReconciler) getAdminCaBundle() ([]byte, error) { 239 var caBundle []byte 240 241 // Append the CA bundle from verrazzano-tls-ca secret if it exists 242 optSecret, err := r.getSecret(constants.VerrazzanoSystemNamespace, constants.PrivateCABundle, false) 243 if err != nil && !errors.IsNotFound(err) { 244 return nil, err 245 } 246 if err == nil { 247 // Combine the two CA bundles 248 tlsCABundle := optSecret.Data[constants.RancherTLSCAKey] 249 if len(tlsCABundle) > 0 { 250 r.log.Debugf("Adding tls-ca bundle to Admin CA bundle") 251 caBundle = tlsCABundle 252 } 253 } 254 255 // Append the CA bundle from the ingress vzIngressSecret if it exists and is not already present in the bundle 256 vzIngressSecret, err := r.getSecret(constants.VerrazzanoSystemNamespace, constants.VerrazzanoIngressTLSSecret, true) 257 if err != nil && !errors.IsNotFound(err) { 258 return nil, err 259 } 260 tlsIngressCA, found := vzIngressSecret.Data[mcconstants.CaCrtKey] 261 if found { 262 if !strings.Contains(strings.TrimSpace(string(caBundle)), strings.TrimSpace(string(tlsIngressCA))) { 263 r.log.Infof("Adding ingress CA cert bundle to Admin CA bundle") 264 caBundle = append(caBundle, tlsIngressCA...) 265 } else { 266 r.log.Debugf("ingress CA bundle already present in bundle data, skipping") 267 } 268 } 269 270 return caBundle, nil 271 } 272 273 func (r *VerrazzanoManagedClusterReconciler) getIngressURL(namespace string, name string) (string, error) { 274 var ingress = &k8net.Ingress{} 275 err := r.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, ingress) 276 if err != nil && errors.IsNotFound(err) { 277 r.log.Infof("ingress %s/%s does not exist", namespace, name) 278 return "", nil 279 } 280 281 if err != nil { 282 return "", fmt.Errorf("unable to fetch ingress %s/%s, %v", namespace, name, err) 283 } 284 285 r.log.Infof("found ingress %s/%s", namespace, name) 286 return fmt.Sprintf("%s://%s", "https", ingress.Spec.Rules[0].Host), nil 287 }