github.com/percona/percona-xtradb-cluster-operator@v1.14.0/pkg/controller/pxc/secrets.go (about) 1 package pxc 2 3 import ( 4 "context" 5 "crypto/rand" 6 "fmt" 7 "math/big" 8 mrand "math/rand" 9 "strings" 10 "time" 11 12 "github.com/pkg/errors" 13 corev1 "k8s.io/api/core/v1" 14 k8serror "k8s.io/apimachinery/pkg/api/errors" 15 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 "k8s.io/apimachinery/pkg/types" 17 logf "sigs.k8s.io/controller-runtime/pkg/log" 18 19 api "github.com/percona/percona-xtradb-cluster-operator/pkg/apis/pxc/v1" 20 "github.com/percona/percona-xtradb-cluster-operator/pkg/pxc/users" 21 ) 22 23 const internalSecretsPrefix = "internal-" 24 25 func (r *ReconcilePerconaXtraDBCluster) reconcileUsersSecret(ctx context.Context, cr *api.PerconaXtraDBCluster) error { 26 log := logf.FromContext(ctx) 27 28 secretObj := new(corev1.Secret) 29 err := r.client.Get(context.TODO(), 30 types.NamespacedName{ 31 Namespace: cr.Namespace, 32 Name: cr.Spec.SecretsName, 33 }, 34 secretObj, 35 ) 36 if err == nil { 37 if err := validatePasswords(secretObj); err != nil { 38 return errors.Wrap(err, "validate passwords") 39 } 40 isChanged, err := setUserSecretDefaults(secretObj) 41 if err != nil { 42 return errors.Wrap(err, "set user secret defaults") 43 } 44 if isChanged { 45 err := r.client.Update(context.TODO(), secretObj) 46 if err == nil { 47 log.Info("User secrets updated", "secrets", cr.Spec.SecretsName) 48 } 49 return err 50 } 51 return nil 52 } else if !k8serror.IsNotFound(err) { 53 return errors.Wrap(err, "get secret") 54 } 55 56 secretObj = &corev1.Secret{ 57 ObjectMeta: metav1.ObjectMeta{ 58 Name: cr.Spec.SecretsName, 59 Namespace: cr.Namespace, 60 }, 61 Type: corev1.SecretTypeOpaque, 62 } 63 64 if _, err = setUserSecretDefaults(secretObj); err != nil { 65 return errors.Wrap(err, "set user secret defaults") 66 } 67 68 err = r.client.Create(context.TODO(), secretObj) 69 if err != nil { 70 return fmt.Errorf("create Users secret: %v", err) 71 } 72 73 log.Info("Created user secrets", "secrets", cr.Spec.SecretsName) 74 return nil 75 } 76 77 func setUserSecretDefaults(secret *corev1.Secret) (isChanged bool, err error) { 78 if secret.Data == nil { 79 secret.Data = make(map[string][]byte) 80 } 81 users := []string{users.Root, users.Xtrabackup, users.Monitor, users.ProxyAdmin, users.Operator, users.Replication} 82 for _, user := range users { 83 if pass, ok := secret.Data[user]; !ok || len(pass) == 0 { 84 secret.Data[user], err = generatePass() 85 if err != nil { 86 return false, errors.Wrapf(err, "create %s users password", user) 87 } 88 89 isChanged = true 90 } 91 } 92 return 93 } 94 95 const ( 96 passwordMaxLen = 20 97 passwordMinLen = 16 98 passSymbols = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + 99 "abcdefghijklmnopqrstuvwxyz" + 100 "0123456789" + 101 "!#$%&()*+,-.<=>?@[]^_{}~" 102 ) 103 104 // generatePass generates a random password 105 func generatePass() ([]byte, error) { 106 mrand.Seed(time.Now().UnixNano()) 107 ln := mrand.Intn(passwordMaxLen-passwordMinLen) + passwordMinLen 108 b := make([]byte, ln) 109 for i := 0; i < ln; i++ { 110 randInt, err := rand.Int(rand.Reader, big.NewInt(int64(len(passSymbols)))) 111 if err != nil { 112 return nil, errors.Wrap(err, "get rand int") 113 } 114 b[i] = passSymbols[randInt.Int64()] 115 } 116 117 return b, nil 118 } 119 120 func validatePasswords(secret *corev1.Secret) error { 121 for user, pass := range secret.Data { 122 switch user { 123 case users.ProxyAdmin: 124 if strings.ContainsAny(string(pass), ";:") { 125 return errors.New("invalid proxyadmin password, don't use ';' or ':'") 126 } 127 default: 128 continue 129 } 130 } 131 132 return nil 133 }