github.com/verrazzano/verrazzano-monitoring-operator@v0.0.30/pkg/vmo/secret.go (about) 1 // Copyright (C) 2020, 2022, 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 vmo 5 6 import ( 7 "context" 8 "errors" 9 "golang.org/x/crypto/bcrypt" 10 apierrors "k8s.io/apimachinery/pkg/api/errors" 11 12 "reflect" 13 "regexp" 14 "strings" 15 16 vmcontrollerv1 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/apis/vmcontroller/v1" 17 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/constants" 18 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/resources/secrets" 19 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 "k8s.io/apimachinery/pkg/labels" 21 ) 22 23 const ( 24 // PasswordSeparator separates passwords from hashes 25 PasswordSeparator = ":" 26 // LineSeparator separates password records 27 LineSeparator = "\n" 28 ) 29 30 // HashedPasswords name => hash 31 type HashedPasswords map[string]string 32 33 func hashBcrypt(password string) (string, error) { 34 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) 35 if err != nil { 36 return "", err 37 } 38 39 return string(hashedPassword), nil 40 } 41 42 // Bytes bytes representation 43 func (hp HashedPasswords) Bytes() (passwordBytes []byte) { 44 passwordBytes = []byte{} 45 for name, hash := range hp { 46 passwordBytes = append(passwordBytes, []byte(name+PasswordSeparator+hash+LineSeparator)...) 47 } 48 return passwordBytes 49 } 50 51 // SetPassword set a password for a user with a hashing algo 52 func (hp HashedPasswords) SetPassword(name, password string) (err error) { 53 hashValue, err := hashBcrypt(password) 54 if err != nil { 55 return err 56 } 57 hp[name] = hashValue 58 return nil 59 } 60 61 // GetAuthSecrets returns username and password 62 func GetAuthSecrets(controller *Controller, vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) (string, string, error) { 63 // setup username/passwords and secrets 64 65 username, err := controller.loadSecretData(vmo.Namespace, 66 vmo.Spec.SecretsName, constants.VMOSecretUsernameField) 67 if err != nil { 68 return "", "", controller.log.ErrorfNewErr("Failed getting username from secret %s/%s: %v", vmo.Namespace, vmo.Spec.SecretsName, err) 69 } 70 71 password, err := controller.loadSecretData(vmo.Namespace, 72 vmo.Spec.SecretsName, constants.VMOSecretPasswordField) 73 if err != nil { 74 return "", "", controller.log.ErrorfNewErr("Failed getting password from secret %s/%s: %v", vmo.Namespace, vmo.Spec.SecretsName, err) 75 } 76 return string(username), string(password), nil 77 } 78 79 func GetClusterNameFromSecret(controller *Controller, namespace string) (string, error) { 80 clusterName, err := controller.loadSecretData(namespace, constants.MCRegistrationSecret, constants.ClusterNameData) 81 if err == nil { 82 return string(clusterName), nil 83 } 84 if apierrors.IsNotFound(err) { 85 clusterName, err = controller.loadSecretData(namespace, constants.MCLocalRegistrationSecret, constants.ClusterNameData) 86 if err != nil { 87 return "", controller.log.ErrorfNewErr("Failed to load secret %s data %s: %v", constants.MCLocalRegistrationSecret, constants.ClusterNameData, err) 88 } 89 return string(clusterName), err 90 } 91 return "", err 92 } 93 94 // CreateOrUpdateAuthSecrets create/updates auth secrets 95 func CreateOrUpdateAuthSecrets(controller *Controller, vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, credsMap map[string]string) error { 96 97 passwords := HashedPasswords(map[string]string{}) 98 for k, v := range credsMap { 99 if err := passwords.SetPassword(k, v); err != nil { 100 return err 101 } 102 } 103 auth := string(passwords.Bytes()) 104 105 secretData := make(map[string][]byte) 106 secretData["auth"] = []byte(auth) 107 secret, err := controller.secretLister.Secrets(vmo.Namespace).Get(vmo.Spec.SecretName) 108 //When secret exists, k8s api returns a nil err obj. 109 if err == nil { 110 isEqual := reflect.DeepEqual(secretData, secret.Data) 111 if !isEqual { 112 secret.Data = secretData 113 _, err = controller.kubeclientset.CoreV1().Secrets(vmo.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}) 114 if err != nil { 115 return controller.log.ErrorfNewErr("Failed to update a basic auth secret %s:%s: %v", vmo.Namespace, vmo.Spec.SecretName, err) 116 } 117 } 118 return nil 119 } 120 // set a name for our VMO secret 121 // create the secret based on the Username/Password passed in the spec 122 secret, err = secrets.New(vmo, vmo.Spec.SecretName, []byte(auth)) 123 if err != nil { 124 return controller.log.ErrorfNewErr("Failed creating a password hash, err: %v", err) 125 } 126 secretOut, err := controller.kubeclientset.CoreV1().Secrets(vmo.Namespace).Create(context.TODO(), secret, metav1.CreateOptions{}) 127 if err != nil { 128 return controller.log.ErrorfNewErr("Failed creating secret %s/%s: %v", vmo.Namespace, vmo.Spec.SecretName, err) 129 } 130 controller.log.Debugf("Created secret: %s", secretOut.Name) 131 132 // Delete Auth secrets if it is not supposed to exists 133 134 secretsNames := []string{secret.Name, vmo.Name + "-tls"} 135 selector := labels.SelectorFromSet(map[string]string{constants.VMOLabel: vmo.Name}) 136 secretList, err := controller.secretLister.Secrets(vmo.Namespace).List(selector) 137 if err != nil { 138 return controller.log.ErrorfNewErr("Failed listing secrets in namespace %s: %v", vmo.Namespace, err) 139 } 140 for _, existedSecret := range secretList { 141 if !contains(secretsNames, existedSecret.Name) { 142 controller.log.Debugf("Deleting secret %s", existedSecret.Name) 143 err := controller.kubeclientset.CoreV1().Secrets(vmo.Namespace).Delete(context.TODO(), existedSecret.Name, metav1.DeleteOptions{}) 144 if err != nil { 145 return controller.log.ErrorfNewErr("Failed to delete secret %s/%s: %v", vmo.Namespace, existedSecret.Name, err) 146 } 147 } 148 } 149 150 return nil 151 } 152 153 // CreateOrUpdateTLSSecrets create/updates TLS secrets 154 func CreateOrUpdateTLSSecrets(controller *Controller, vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) error { 155 156 if vmo.Spec.AutoSecret { 157 controller.log.Debug("Not explicitly creating TLS secret, we expect it to be auto-generated") 158 // by setting the AutoSecret to true we ask that a certificate be made for us 159 // currently the mechanism relies on ingressShim part of cert-manager to notice the 160 // annotation we set on the ingress rule which is set off by AutoSecret being true 161 return nil 162 } 163 //get tlsCrt from vmoSecrets 164 tlsCrt, err := controller.loadSecretData(vmo.Namespace, 165 vmo.Spec.SecretsName, constants.TLSCRTName) 166 if err != nil { 167 return controller.log.ErrorfNewErr("Failed getting tls.crt data using name %s, from secret %s/%s: %v", 168 constants.TLSCRTName, vmo.Namespace, vmo.Spec.SecretsName, err) 169 } 170 //get tlsKey from vmoSecrets 171 tlsKey, err := controller.loadSecretData(vmo.Namespace, 172 vmo.Spec.SecretsName, constants.TLSKeyName) 173 if err != nil { 174 return controller.log.ErrorfNewErr("Failed getting tls.key data using name %s, from secret %s/%s: %v", 175 constants.TLSKeyName, vmo.Namespace, vmo.Spec.SecretsName, err) 176 } 177 178 if len(tlsCrt) != 0 && len(tlsKey) != 0 { 179 secretData := make(map[string][]byte) 180 secret, err := controller.secretLister.Secrets(vmo.Namespace).Get(vmo.Name + "-tls") 181 //When secret exists, k8s api returns a nil err obj. 182 if err == nil { 183 secretData["tls.crt"] = tlsCrt 184 secretData["tls.key"] = tlsKey 185 isSecretDataEqual := reflect.DeepEqual(secretData, secret.Data) 186 if !isSecretDataEqual { 187 secret.Data = secretData 188 _, err = controller.kubeclientset.CoreV1().Secrets(vmo.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}) 189 if err != nil { 190 return controller.log.ErrorfNewErr("Failed to updated basic auth secret %s/%s: err: %v", vmo.Namespace, vmo.Name+"-tls", err) 191 } 192 } 193 return nil 194 } 195 secret, err = secrets.NewTLS(vmo, vmo.Name+"-tls", secretData) 196 if err != nil { 197 return controller.log.ErrorfNewErr("Failed trying to create a password hash: %v", err) 198 } 199 secretOut, err := controller.kubeclientset.CoreV1().Secrets(vmo.Namespace).Create(context.TODO(), secret, metav1.CreateOptions{}) 200 if err != nil { 201 return controller.log.ErrorfNewErr("Failed to create secret %s/%s: %v", vmo.Namespace, vmo.Name+"-tls", err) 202 } 203 controller.log.Debugf("Create TLS secret: %s", secretOut.Name) 204 } 205 return nil 206 } 207 208 func (c *Controller) loadSecretData(ns, secretName, secretField string) ([]byte, error) { 209 secret, err := c.secretLister.Secrets(ns).Get(secretName) 210 if err != nil { 211 return nil, err 212 } 213 214 if data, ok := secret.Data[secretField]; ok { 215 return data, nil 216 } 217 return nil, nil 218 } 219 220 func (c *Controller) loadAllAuthSecretData(ns, secretName string) (map[string]string, error) { 221 secret, err := c.secretLister.Secrets(ns).Get(secretName) 222 if err != nil { 223 return nil, err 224 } 225 226 dataMap := secret.Data 227 228 _, ok := dataMap["username"] 229 if !ok { 230 return nil, errors.New("Failed: The default username is not defined in VMO secrets") 231 } 232 233 m := make(map[string]string) 234 re := regexp.MustCompile("[0-9]+") 235 var pwd []byte 236 for key, value := range dataMap { 237 if !strings.Contains(strings.ToUpper(key), strings.ToUpper("username")) { 238 continue 239 } 240 //Below Regular Expression returns any number present in the string after username 241 userIndex := re.FindAllString(key, -1) 242 if len(userIndex) == 0 { 243 //Default User does not have any number appended 244 pwd, ok = dataMap["password"] 245 if !ok { 246 return nil, errors.New("Failed: The default password is not defined in VMO secrets") 247 } 248 m[string(value)] = string(pwd) 249 } else if len(userIndex) == 1 { 250 //Other users in the format username1,username2 etc, Have a integer appended at the end 251 pwd, ok = dataMap["password"+userIndex[0]] 252 if !ok { 253 return nil, errors.New("error: The password is in the wrong format in VMO secrets, should be i.e. password:p1, password2:u2, password3:u3 etc") 254 } 255 m[string(value)] = string(pwd) 256 } else { 257 // We should never reach here if the usernames are defined correctly in the secret file 258 return nil, errors.New("Failed: The username is in the wrong format in VMO secrets, More than 1 number in map key") 259 } 260 } 261 262 return m, nil 263 }