github.com/percona/percona-xtradb-cluster-operator@v1.14.0/pkg/pxc/users/users.go (about) 1 package users 2 3 import ( 4 "database/sql" 5 "fmt" 6 7 "github.com/go-sql-driver/mysql" 8 "github.com/pkg/errors" 9 ) 10 11 const ( 12 Root = "root" 13 Operator = "operator" 14 Monitor = "monitor" 15 Xtrabackup = "xtrabackup" 16 Replication = "replication" 17 ProxyAdmin = "proxyadmin" 18 PMMServer = "pmmserver" 19 PMMServerKey = "pmmserverkey" 20 ) 21 22 var UserNames = []string{Root, Operator, Monitor, Xtrabackup, 23 Replication, ProxyAdmin, PMMServer, PMMServerKey} 24 25 type Manager struct { 26 db *sql.DB 27 } 28 29 type SysUser struct { 30 Name string `yaml:"username"` 31 Pass string `yaml:"password"` 32 Hosts []string `yaml:"hosts"` 33 } 34 35 func NewManager(addr string, user, pass string, timeout int32) (Manager, error) { 36 var um Manager 37 38 timeoutStr := fmt.Sprintf("%ds", timeout) 39 config := mysql.NewConfig() 40 config.User = user 41 config.Passwd = pass 42 config.Net = "tcp" 43 config.Addr = addr 44 config.DBName = "mysql" 45 config.Params = map[string]string{ 46 "interpolateParams": "true", 47 "timeout": timeoutStr, 48 "readTimeout": timeoutStr, 49 "writeTimeout": timeoutStr, 50 "tls": "preferred", 51 } 52 53 mysqlDB, err := sql.Open("mysql", config.FormatDSN()) 54 if err != nil { 55 return um, errors.Wrap(err, "cannot connect to any host") 56 } 57 58 um.db = mysqlDB 59 60 return um, nil 61 } 62 63 func (u *Manager) Close() error { 64 return u.db.Close() 65 } 66 67 func (u *Manager) CreateOperatorUser(pass string) error { 68 _, err := u.db.Exec("CREATE USER IF NOT EXISTS 'operator'@'%' IDENTIFIED BY ?", pass) 69 if err != nil { 70 return errors.Wrap(err, "create operator user") 71 } 72 73 _, err = u.db.Exec("GRANT ALL ON *.* TO 'operator'@'%' WITH GRANT OPTION") 74 if err != nil { 75 return errors.Wrap(err, "grant operator user") 76 } 77 78 return nil 79 } 80 81 // UpdateUserPassWithoutDP updates user pass without Dual Password 82 // feature introduced in MsSQL 8 83 func (u *Manager) UpdateUserPassWithoutDP(user *SysUser) error { 84 if user == nil { 85 return nil 86 } 87 88 for _, host := range user.Hosts { 89 _, err := u.db.Exec("ALTER USER ?@? IDENTIFIED BY ?", user.Name, host, user.Pass) 90 if err != nil { 91 return errors.Wrap(err, "update password") 92 } 93 } 94 95 return nil 96 } 97 98 // UpdateUserPass updates user passwords but retains the current password 99 // using Dual Password feature of MySQL 8. 100 func (m *Manager) UpdateUserPass(user *SysUser) error { 101 if user == nil { 102 return nil 103 } 104 105 tx, err := m.db.Begin() 106 if err != nil { 107 return errors.Wrap(err, "begin transaction") 108 } 109 110 for _, host := range user.Hosts { 111 _, err = tx.Exec("ALTER USER ?@? IDENTIFIED BY ? RETAIN CURRENT PASSWORD", user.Name, host, user.Pass) 112 if err != nil { 113 err = errors.Wrap(err, "alter user") 114 115 if errT := tx.Rollback(); errT != nil { 116 return errors.Wrap(errors.Wrap(errT, "rollback"), err.Error()) 117 } 118 119 return err 120 } 121 } 122 123 if err := tx.Commit(); err != nil { 124 return errors.Wrap(err, "commit transaction") 125 } 126 127 return nil 128 } 129 130 // DiscardOldPassword discards old passwords of given users 131 func (m *Manager) DiscardOldPassword(user *SysUser) error { 132 if user == nil { 133 return nil 134 } 135 136 tx, err := m.db.Begin() 137 if err != nil { 138 return errors.Wrap(err, "begin transaction") 139 } 140 141 for _, host := range user.Hosts { 142 _, err = tx.Exec("ALTER USER ?@? DISCARD OLD PASSWORD", user.Name, host) 143 if err != nil { 144 err = errors.Wrap(err, "alter user") 145 146 if errT := tx.Rollback(); errT != nil { 147 return errors.Wrap(errors.Wrap(errT, "rollback"), err.Error()) 148 } 149 150 return err 151 } 152 } 153 154 if err := tx.Commit(); err != nil { 155 return errors.Wrap(err, "commit transaction") 156 } 157 158 return nil 159 } 160 161 // DiscardOldPassword discards old passwords of given users 162 func (m *Manager) IsOldPassDiscarded(user *SysUser) (bool, error) { 163 var attributes sql.NullString 164 r := m.db.QueryRow("SELECT User_attributes FROM mysql.user WHERE user=?", user.Name) 165 166 err := r.Scan(&attributes) 167 if err != nil { 168 if err == sql.ErrNoRows { 169 return true, nil 170 } 171 return false, errors.Wrap(err, "select User_attributes field") 172 } 173 174 if attributes.Valid { 175 return false, nil 176 } 177 178 return true, nil 179 } 180 181 func (u *Manager) UpdateProxyUser(user *SysUser) error { 182 switch user.Name { 183 case ProxyAdmin: 184 _, err := u.db.Exec("UPDATE global_variables SET variable_value=? WHERE variable_name='admin-admin_credentials'", "proxyadmin:"+user.Pass) 185 if err != nil { 186 return errors.Wrap(err, "update proxy admin password") 187 } 188 _, err = u.db.Exec("UPDATE global_variables SET variable_value=? WHERE variable_name='admin-cluster_password'", user.Pass) 189 if err != nil { 190 return errors.Wrap(err, "update proxy admin password") 191 } 192 _, err = u.db.Exec("LOAD ADMIN VARIABLES TO RUNTIME") 193 if err != nil { 194 return errors.Wrap(err, "load to runtime") 195 } 196 197 _, err = u.db.Exec("SAVE ADMIN VARIABLES TO DISK") 198 if err != nil { 199 return errors.Wrap(err, "save to disk") 200 } 201 case Monitor: 202 _, err := u.db.Exec("UPDATE global_variables SET variable_value=? WHERE variable_name='mysql-monitor_password'", user.Pass) 203 if err != nil { 204 return errors.Wrap(err, "update proxy monitor password") 205 } 206 _, err = u.db.Exec("LOAD MYSQL VARIABLES TO RUNTIME") 207 if err != nil { 208 return errors.Wrap(err, "load to runtime") 209 } 210 211 _, err = u.db.Exec("SAVE MYSQL VARIABLES TO DISK") 212 if err != nil { 213 return errors.Wrap(err, "save to disk") 214 } 215 } 216 217 return nil 218 } 219 220 // Update160MonitorUserGrant grants SERVICE_CONNECTION_ADMIN rights to the monitor user 221 // if pxc version is 8 or more and sets the MAX_USER_CONNECTIONS parameter to 100 (empirically determined) 222 func (u *Manager) Update160MonitorUserGrant(pass string) (err error) { 223 224 _, err = u.db.Exec("CREATE USER IF NOT EXISTS 'monitor'@'%' IDENTIFIED BY ?", pass) 225 if err != nil { 226 return errors.Wrap(err, "create operator user") 227 } 228 229 _, err = u.db.Exec("/*!80015 GRANT SERVICE_CONNECTION_ADMIN ON *.* TO 'monitor'@'%' */") 230 if err != nil { 231 return errors.Wrapf(err, "grant service_connection to user monitor") 232 } 233 234 _, err = u.db.Exec("ALTER USER 'monitor'@'%' WITH MAX_USER_CONNECTIONS 100") 235 if err != nil { 236 return errors.Wrapf(err, "set max connections to user monitor") 237 } 238 239 return nil 240 } 241 242 // Update170XtrabackupUser grants all needed rights to the xtrabackup user 243 func (u *Manager) Update170XtrabackupUser(pass string) (err error) { 244 245 _, err = u.db.Exec("CREATE USER IF NOT EXISTS 'xtrabackup'@'%' IDENTIFIED BY ?", pass) 246 if err != nil { 247 return errors.Wrap(err, "create operator user") 248 } 249 250 _, err = u.db.Exec("GRANT ALL ON *.* TO 'xtrabackup'@'%'") 251 if err != nil { 252 return errors.Wrapf(err, "grant privileges to user xtrabackup") 253 } 254 255 return nil 256 } 257 258 // Update1100MonitorUserPrivilege grants system_user privilege for monitor 259 func (u *Manager) Update1100MonitorUserPrivilege() (err error) { 260 if _, err := u.db.Exec("GRANT SYSTEM_USER ON *.* TO 'monitor'@'%'"); err != nil { 261 return errors.Wrap(err, "monitor user") 262 } 263 264 return nil 265 } 266 267 func (u *Manager) CreateReplicationUser(password string) error { 268 269 _, err := u.db.Exec("CREATE USER IF NOT EXISTS 'replication'@'%' IDENTIFIED BY ?", password) 270 if err != nil { 271 return errors.Wrap(err, "create replication user") 272 } 273 274 _, err = u.db.Exec("GRANT REPLICATION SLAVE ON *.* to 'replication'@'%'") 275 if err != nil { 276 return errors.Wrap(err, "grant replication user") 277 } 278 279 return nil 280 } 281 282 // UpdatePassExpirationPolicy sets user password expiration policy to never 283 func (u *Manager) UpdatePassExpirationPolicy(user *SysUser) error { 284 if user == nil { 285 return nil 286 } 287 288 for _, host := range user.Hosts { 289 _, err := u.db.Exec("ALTER USER ?@? PASSWORD EXPIRE NEVER", user.Name, host) 290 if err != nil { 291 return err 292 } 293 } 294 return nil 295 }