go.etcd.io/etcd@v3.3.27+incompatible/etcdserver/auth/auth.go (about) 1 // Copyright 2015 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package auth implements etcd authentication. 16 package auth 17 18 import ( 19 "context" 20 "encoding/json" 21 "fmt" 22 "net/http" 23 "path" 24 "reflect" 25 "sort" 26 "strings" 27 "time" 28 29 etcderr "github.com/coreos/etcd/error" 30 "github.com/coreos/etcd/etcdserver" 31 "github.com/coreos/etcd/etcdserver/etcdserverpb" 32 "github.com/coreos/etcd/pkg/types" 33 "github.com/coreos/pkg/capnslog" 34 35 "golang.org/x/crypto/bcrypt" 36 ) 37 38 const ( 39 // StorePermsPrefix is the internal prefix of the storage layer dedicated to storing user data. 40 StorePermsPrefix = "/2" 41 42 // RootRoleName is the name of the ROOT role, with privileges to manage the cluster. 43 RootRoleName = "root" 44 45 // GuestRoleName is the name of the role that defines the privileges of an unauthenticated user. 46 GuestRoleName = "guest" 47 ) 48 49 var ( 50 plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdserver/auth") 51 ) 52 53 var rootRole = Role{ 54 Role: RootRoleName, 55 Permissions: Permissions{ 56 KV: RWPermission{ 57 Read: []string{"/*"}, 58 Write: []string{"/*"}, 59 }, 60 }, 61 } 62 63 var guestRole = Role{ 64 Role: GuestRoleName, 65 Permissions: Permissions{ 66 KV: RWPermission{ 67 Read: []string{"/*"}, 68 Write: []string{"/*"}, 69 }, 70 }, 71 } 72 73 type doer interface { 74 Do(context.Context, etcdserverpb.Request) (etcdserver.Response, error) 75 } 76 77 type Store interface { 78 AllUsers() ([]string, error) 79 GetUser(name string) (User, error) 80 CreateOrUpdateUser(user User) (out User, created bool, err error) 81 CreateUser(user User) (User, error) 82 DeleteUser(name string) error 83 UpdateUser(user User) (User, error) 84 AllRoles() ([]string, error) 85 GetRole(name string) (Role, error) 86 CreateRole(role Role) error 87 DeleteRole(name string) error 88 UpdateRole(role Role) (Role, error) 89 AuthEnabled() bool 90 EnableAuth() error 91 DisableAuth() error 92 PasswordStore 93 } 94 95 type PasswordStore interface { 96 CheckPassword(user User, password string) bool 97 HashPassword(password string) (string, error) 98 } 99 100 type store struct { 101 server doer 102 timeout time.Duration 103 ensuredOnce bool 104 105 PasswordStore 106 } 107 108 type User struct { 109 User string `json:"user"` 110 Password string `json:"password,omitempty"` 111 Roles []string `json:"roles"` 112 Grant []string `json:"grant,omitempty"` 113 Revoke []string `json:"revoke,omitempty"` 114 } 115 116 type Role struct { 117 Role string `json:"role"` 118 Permissions Permissions `json:"permissions"` 119 Grant *Permissions `json:"grant,omitempty"` 120 Revoke *Permissions `json:"revoke,omitempty"` 121 } 122 123 type Permissions struct { 124 KV RWPermission `json:"kv"` 125 } 126 127 func (p *Permissions) IsEmpty() bool { 128 return p == nil || (len(p.KV.Read) == 0 && len(p.KV.Write) == 0) 129 } 130 131 type RWPermission struct { 132 Read []string `json:"read"` 133 Write []string `json:"write"` 134 } 135 136 type Error struct { 137 Status int 138 Errmsg string 139 } 140 141 func (ae Error) Error() string { return ae.Errmsg } 142 func (ae Error) HTTPStatus() int { return ae.Status } 143 144 func authErr(hs int, s string, v ...interface{}) Error { 145 return Error{Status: hs, Errmsg: fmt.Sprintf("auth: "+s, v...)} 146 } 147 148 func NewStore(server doer, timeout time.Duration) Store { 149 s := &store{ 150 server: server, 151 timeout: timeout, 152 PasswordStore: passwordStore{}, 153 } 154 return s 155 } 156 157 // passwordStore implements PasswordStore using bcrypt to hash user passwords 158 type passwordStore struct{} 159 160 func (_ passwordStore) CheckPassword(user User, password string) bool { 161 err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) 162 return err == nil 163 } 164 165 func (_ passwordStore) HashPassword(password string) (string, error) { 166 hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) 167 return string(hash), err 168 } 169 170 func (s *store) AllUsers() ([]string, error) { 171 resp, err := s.requestResource("/users/", false, false) 172 if err != nil { 173 if e, ok := err.(*etcderr.Error); ok { 174 if e.ErrorCode == etcderr.EcodeKeyNotFound { 175 return []string{}, nil 176 } 177 } 178 return nil, err 179 } 180 var nodes []string 181 for _, n := range resp.Event.Node.Nodes { 182 _, user := path.Split(n.Key) 183 nodes = append(nodes, user) 184 } 185 sort.Strings(nodes) 186 return nodes, nil 187 } 188 189 func (s *store) GetUser(name string) (User, error) { return s.getUser(name, false) } 190 191 // CreateOrUpdateUser should be only used for creating the new user or when you are not 192 // sure if it is a create or update. (When only password is passed in, we are not sure 193 // if it is a update or create) 194 func (s *store) CreateOrUpdateUser(user User) (out User, created bool, err error) { 195 _, err = s.getUser(user.User, true) 196 if err == nil { 197 out, err = s.UpdateUser(user) 198 return out, false, err 199 } 200 u, err := s.CreateUser(user) 201 return u, true, err 202 } 203 204 func (s *store) CreateUser(user User) (User, error) { 205 // Attach root role to root user. 206 if user.User == "root" { 207 user = attachRootRole(user) 208 } 209 u, err := s.createUserInternal(user) 210 if err == nil { 211 plog.Noticef("created user %s", user.User) 212 } 213 return u, err 214 } 215 216 func (s *store) createUserInternal(user User) (User, error) { 217 if user.Password == "" { 218 return user, authErr(http.StatusBadRequest, "Cannot create user %s with an empty password", user.User) 219 } 220 hash, err := s.HashPassword(user.Password) 221 if err != nil { 222 return user, err 223 } 224 user.Password = hash 225 226 _, err = s.createResource("/users/"+user.User, user) 227 if err != nil { 228 if e, ok := err.(*etcderr.Error); ok { 229 if e.ErrorCode == etcderr.EcodeNodeExist { 230 return user, authErr(http.StatusConflict, "User %s already exists.", user.User) 231 } 232 } 233 } 234 return user, err 235 } 236 237 func (s *store) DeleteUser(name string) error { 238 if s.AuthEnabled() && name == "root" { 239 return authErr(http.StatusForbidden, "Cannot delete root user while auth is enabled.") 240 } 241 _, err := s.deleteResource("/users/" + name) 242 if err != nil { 243 if e, ok := err.(*etcderr.Error); ok { 244 if e.ErrorCode == etcderr.EcodeKeyNotFound { 245 return authErr(http.StatusNotFound, "User %s does not exist", name) 246 } 247 } 248 return err 249 } 250 plog.Noticef("deleted user %s", name) 251 return nil 252 } 253 254 func (s *store) UpdateUser(user User) (User, error) { 255 old, err := s.getUser(user.User, true) 256 if err != nil { 257 if e, ok := err.(*etcderr.Error); ok { 258 if e.ErrorCode == etcderr.EcodeKeyNotFound { 259 return user, authErr(http.StatusNotFound, "User %s doesn't exist.", user.User) 260 } 261 } 262 return old, err 263 } 264 265 newUser, err := old.merge(user, s.PasswordStore) 266 if err != nil { 267 return old, err 268 } 269 if reflect.DeepEqual(old, newUser) { 270 return old, authErr(http.StatusBadRequest, "User not updated. Use grant/revoke/password to update the user.") 271 } 272 _, err = s.updateResource("/users/"+user.User, newUser) 273 if err == nil { 274 plog.Noticef("updated user %s", user.User) 275 } 276 return newUser, err 277 } 278 279 func (s *store) AllRoles() ([]string, error) { 280 nodes := []string{RootRoleName} 281 resp, err := s.requestResource("/roles/", false, false) 282 if err != nil { 283 if e, ok := err.(*etcderr.Error); ok { 284 if e.ErrorCode == etcderr.EcodeKeyNotFound { 285 return nodes, nil 286 } 287 } 288 return nil, err 289 } 290 for _, n := range resp.Event.Node.Nodes { 291 _, role := path.Split(n.Key) 292 nodes = append(nodes, role) 293 } 294 sort.Strings(nodes) 295 return nodes, nil 296 } 297 298 func (s *store) GetRole(name string) (Role, error) { return s.getRole(name, false) } 299 300 func (s *store) CreateRole(role Role) error { 301 if role.Role == RootRoleName { 302 return authErr(http.StatusForbidden, "Cannot modify role %s: is root role.", role.Role) 303 } 304 _, err := s.createResource("/roles/"+role.Role, role) 305 if err != nil { 306 if e, ok := err.(*etcderr.Error); ok { 307 if e.ErrorCode == etcderr.EcodeNodeExist { 308 return authErr(http.StatusConflict, "Role %s already exists.", role.Role) 309 } 310 } 311 } 312 if err == nil { 313 plog.Noticef("created new role %s", role.Role) 314 } 315 return err 316 } 317 318 func (s *store) DeleteRole(name string) error { 319 if name == RootRoleName { 320 return authErr(http.StatusForbidden, "Cannot modify role %s: is root role.", name) 321 } 322 _, err := s.deleteResource("/roles/" + name) 323 if err != nil { 324 if e, ok := err.(*etcderr.Error); ok { 325 if e.ErrorCode == etcderr.EcodeKeyNotFound { 326 return authErr(http.StatusNotFound, "Role %s doesn't exist.", name) 327 } 328 } 329 } 330 if err == nil { 331 plog.Noticef("deleted role %s", name) 332 } 333 return err 334 } 335 336 func (s *store) UpdateRole(role Role) (Role, error) { 337 if role.Role == RootRoleName { 338 return Role{}, authErr(http.StatusForbidden, "Cannot modify role %s: is root role.", role.Role) 339 } 340 old, err := s.getRole(role.Role, true) 341 if err != nil { 342 if e, ok := err.(*etcderr.Error); ok { 343 if e.ErrorCode == etcderr.EcodeKeyNotFound { 344 return role, authErr(http.StatusNotFound, "Role %s doesn't exist.", role.Role) 345 } 346 } 347 return old, err 348 } 349 newRole, err := old.merge(role) 350 if err != nil { 351 return old, err 352 } 353 if reflect.DeepEqual(old, newRole) { 354 return old, authErr(http.StatusBadRequest, "Role not updated. Use grant/revoke to update the role.") 355 } 356 _, err = s.updateResource("/roles/"+role.Role, newRole) 357 if err == nil { 358 plog.Noticef("updated role %s", role.Role) 359 } 360 return newRole, err 361 } 362 363 func (s *store) AuthEnabled() bool { 364 return s.detectAuth() 365 } 366 367 func (s *store) EnableAuth() error { 368 if s.AuthEnabled() { 369 return authErr(http.StatusConflict, "already enabled") 370 } 371 372 if _, err := s.getUser("root", true); err != nil { 373 return authErr(http.StatusConflict, "No root user available, please create one") 374 } 375 if _, err := s.getRole(GuestRoleName, true); err != nil { 376 plog.Printf("no guest role access found, creating default") 377 if err := s.CreateRole(guestRole); err != nil { 378 plog.Errorf("error creating guest role. aborting auth enable.") 379 return err 380 } 381 } 382 383 if err := s.enableAuth(); err != nil { 384 plog.Errorf("error enabling auth (%v)", err) 385 return err 386 } 387 388 plog.Noticef("auth: enabled auth") 389 return nil 390 } 391 392 func (s *store) DisableAuth() error { 393 if !s.AuthEnabled() { 394 return authErr(http.StatusConflict, "already disabled") 395 } 396 397 err := s.disableAuth() 398 if err == nil { 399 plog.Noticef("auth: disabled auth") 400 } else { 401 plog.Errorf("error disabling auth (%v)", err) 402 } 403 return err 404 } 405 406 // merge applies the properties of the passed-in User to the User on which it 407 // is called and returns a new User with these modifications applied. Think of 408 // all Users as immutable sets of data. Merge allows you to perform the set 409 // operations (desired grants and revokes) atomically 410 func (ou User) merge(nu User, s PasswordStore) (User, error) { 411 var out User 412 if ou.User != nu.User { 413 return out, authErr(http.StatusConflict, "Merging user data with conflicting usernames: %s %s", ou.User, nu.User) 414 } 415 out.User = ou.User 416 if nu.Password != "" { 417 hash, err := s.HashPassword(nu.Password) 418 if err != nil { 419 return ou, err 420 } 421 out.Password = hash 422 } else { 423 out.Password = ou.Password 424 } 425 currentRoles := types.NewUnsafeSet(ou.Roles...) 426 for _, g := range nu.Grant { 427 if currentRoles.Contains(g) { 428 plog.Noticef("granting duplicate role %s for user %s", g, nu.User) 429 return User{}, authErr(http.StatusConflict, fmt.Sprintf("Granting duplicate role %s for user %s", g, nu.User)) 430 } 431 currentRoles.Add(g) 432 } 433 for _, r := range nu.Revoke { 434 if !currentRoles.Contains(r) { 435 plog.Noticef("revoking ungranted role %s for user %s", r, nu.User) 436 return User{}, authErr(http.StatusConflict, fmt.Sprintf("Revoking ungranted role %s for user %s", r, nu.User)) 437 } 438 currentRoles.Remove(r) 439 } 440 out.Roles = currentRoles.Values() 441 sort.Strings(out.Roles) 442 return out, nil 443 } 444 445 // merge for a role works the same as User above -- atomic Role application to 446 // each of the substructures. 447 func (r Role) merge(n Role) (Role, error) { 448 var out Role 449 var err error 450 if r.Role != n.Role { 451 return out, authErr(http.StatusConflict, "Merging role with conflicting names: %s %s", r.Role, n.Role) 452 } 453 out.Role = r.Role 454 out.Permissions, err = r.Permissions.Grant(n.Grant) 455 if err != nil { 456 return out, err 457 } 458 out.Permissions, err = out.Permissions.Revoke(n.Revoke) 459 return out, err 460 } 461 462 func (r Role) HasKeyAccess(key string, write bool) bool { 463 if r.Role == RootRoleName { 464 return true 465 } 466 return r.Permissions.KV.HasAccess(key, write) 467 } 468 469 func (r Role) HasRecursiveAccess(key string, write bool) bool { 470 if r.Role == RootRoleName { 471 return true 472 } 473 return r.Permissions.KV.HasRecursiveAccess(key, write) 474 } 475 476 // Grant adds a set of permissions to the permission object on which it is called, 477 // returning a new permission object. 478 func (p Permissions) Grant(n *Permissions) (Permissions, error) { 479 var out Permissions 480 var err error 481 if n == nil { 482 return p, nil 483 } 484 out.KV, err = p.KV.Grant(n.KV) 485 return out, err 486 } 487 488 // Revoke removes a set of permissions to the permission object on which it is called, 489 // returning a new permission object. 490 func (p Permissions) Revoke(n *Permissions) (Permissions, error) { 491 var out Permissions 492 var err error 493 if n == nil { 494 return p, nil 495 } 496 out.KV, err = p.KV.Revoke(n.KV) 497 return out, err 498 } 499 500 // Grant adds a set of permissions to the permission object on which it is called, 501 // returning a new permission object. 502 func (rw RWPermission) Grant(n RWPermission) (RWPermission, error) { 503 var out RWPermission 504 currentRead := types.NewUnsafeSet(rw.Read...) 505 for _, r := range n.Read { 506 if currentRead.Contains(r) { 507 return out, authErr(http.StatusConflict, "Granting duplicate read permission %s", r) 508 } 509 currentRead.Add(r) 510 } 511 currentWrite := types.NewUnsafeSet(rw.Write...) 512 for _, w := range n.Write { 513 if currentWrite.Contains(w) { 514 return out, authErr(http.StatusConflict, "Granting duplicate write permission %s", w) 515 } 516 currentWrite.Add(w) 517 } 518 out.Read = currentRead.Values() 519 out.Write = currentWrite.Values() 520 sort.Strings(out.Read) 521 sort.Strings(out.Write) 522 return out, nil 523 } 524 525 // Revoke removes a set of permissions to the permission object on which it is called, 526 // returning a new permission object. 527 func (rw RWPermission) Revoke(n RWPermission) (RWPermission, error) { 528 var out RWPermission 529 currentRead := types.NewUnsafeSet(rw.Read...) 530 for _, r := range n.Read { 531 if !currentRead.Contains(r) { 532 plog.Noticef("revoking ungranted read permission %s", r) 533 continue 534 } 535 currentRead.Remove(r) 536 } 537 currentWrite := types.NewUnsafeSet(rw.Write...) 538 for _, w := range n.Write { 539 if !currentWrite.Contains(w) { 540 plog.Noticef("revoking ungranted write permission %s", w) 541 continue 542 } 543 currentWrite.Remove(w) 544 } 545 out.Read = currentRead.Values() 546 out.Write = currentWrite.Values() 547 sort.Strings(out.Read) 548 sort.Strings(out.Write) 549 return out, nil 550 } 551 552 func (rw RWPermission) HasAccess(key string, write bool) bool { 553 var list []string 554 if write { 555 list = rw.Write 556 } else { 557 list = rw.Read 558 } 559 for _, pat := range list { 560 match, err := simpleMatch(pat, key) 561 if err == nil && match { 562 return true 563 } 564 } 565 return false 566 } 567 568 func (rw RWPermission) HasRecursiveAccess(key string, write bool) bool { 569 list := rw.Read 570 if write { 571 list = rw.Write 572 } 573 for _, pat := range list { 574 match, err := prefixMatch(pat, key) 575 if err == nil && match { 576 return true 577 } 578 } 579 return false 580 } 581 582 func simpleMatch(pattern string, key string) (match bool, err error) { 583 if pattern[len(pattern)-1] == '*' { 584 return strings.HasPrefix(key, pattern[:len(pattern)-1]), nil 585 } 586 return key == pattern, nil 587 } 588 589 func prefixMatch(pattern string, key string) (match bool, err error) { 590 if pattern[len(pattern)-1] != '*' { 591 return false, nil 592 } 593 return strings.HasPrefix(key, pattern[:len(pattern)-1]), nil 594 } 595 596 func attachRootRole(u User) User { 597 inRoles := false 598 for _, r := range u.Roles { 599 if r == RootRoleName { 600 inRoles = true 601 break 602 } 603 } 604 if !inRoles { 605 u.Roles = append(u.Roles, RootRoleName) 606 } 607 return u 608 } 609 610 func (s *store) getUser(name string, quorum bool) (User, error) { 611 resp, err := s.requestResource("/users/"+name, false, quorum) 612 if err != nil { 613 if e, ok := err.(*etcderr.Error); ok { 614 if e.ErrorCode == etcderr.EcodeKeyNotFound { 615 return User{}, authErr(http.StatusNotFound, "User %s does not exist.", name) 616 } 617 } 618 return User{}, err 619 } 620 var u User 621 err = json.Unmarshal([]byte(*resp.Event.Node.Value), &u) 622 if err != nil { 623 return u, err 624 } 625 // Attach root role to root user. 626 if u.User == "root" { 627 u = attachRootRole(u) 628 } 629 return u, nil 630 } 631 632 func (s *store) getRole(name string, quorum bool) (Role, error) { 633 if name == RootRoleName { 634 return rootRole, nil 635 } 636 resp, err := s.requestResource("/roles/"+name, false, quorum) 637 if err != nil { 638 if e, ok := err.(*etcderr.Error); ok { 639 if e.ErrorCode == etcderr.EcodeKeyNotFound { 640 return Role{}, authErr(http.StatusNotFound, "Role %s does not exist.", name) 641 } 642 } 643 return Role{}, err 644 } 645 var r Role 646 err = json.Unmarshal([]byte(*resp.Event.Node.Value), &r) 647 return r, err 648 }