1// Copyright 2016 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 15package auth 16 17import ( 18 "bytes" 19 "context" 20 "encoding/binary" 21 "errors" 22 "sort" 23 "strings" 24 "sync" 25 "sync/atomic" 26 "time" 27 28 "github.com/coreos/etcd/auth/authpb" 29 pb "github.com/coreos/etcd/etcdserver/etcdserverpb" 30 "github.com/coreos/etcd/mvcc/backend" 31 32 "github.com/coreos/pkg/capnslog" 33 "golang.org/x/crypto/bcrypt" 34 "google.golang.org/grpc/credentials" 35 "google.golang.org/grpc/metadata" 36 "google.golang.org/grpc/peer" 37) 38 39var ( 40 enableFlagKey = []byte("authEnabled") 41 authEnabled = []byte{1} 42 authDisabled = []byte{0} 43 44 revisionKey = []byte("authRevision") 45 46 authBucketName = []byte("auth") 47 authUsersBucketName = []byte("authUsers") 48 authRolesBucketName = []byte("authRoles") 49 50 plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "auth") 51 52 ErrRootUserNotExist = errors.New("auth: root user does not exist") 53 ErrRootRoleNotExist = errors.New("auth: root user does not have root role") 54 ErrUserAlreadyExist = errors.New("auth: user already exists") 55 ErrUserEmpty = errors.New("auth: user name is empty") 56 ErrUserNotFound = errors.New("auth: user not found") 57 ErrRoleAlreadyExist = errors.New("auth: role already exists") 58 ErrRoleNotFound = errors.New("auth: role not found") 59 ErrAuthFailed = errors.New("auth: authentication failed, invalid user ID or password") 60 ErrPermissionDenied = errors.New("auth: permission denied") 61 ErrRoleNotGranted = errors.New("auth: role is not granted to the user") 62 ErrPermissionNotGranted = errors.New("auth: permission is not granted to the role") 63 ErrAuthNotEnabled = errors.New("auth: authentication is not enabled") 64 ErrAuthOldRevision = errors.New("auth: revision in header is old") 65 ErrInvalidAuthToken = errors.New("auth: invalid auth token") 66 ErrInvalidAuthOpts = errors.New("auth: invalid auth options") 67 ErrInvalidAuthMgmt = errors.New("auth: invalid auth management") 68 69 // BcryptCost is the algorithm cost / strength for hashing auth passwords 70 BcryptCost = bcrypt.DefaultCost 71) 72 73const ( 74 rootUser = "root" 75 rootRole = "root" 76 77 tokenTypeSimple = "simple" 78 tokenTypeJWT = "jwt" 79 80 revBytesLen = 8 81) 82 83type AuthInfo struct { 84 Username string 85 Revision uint64 86} 87 88// AuthenticateParamIndex is used for a key of context in the parameters of Authenticate() 89type AuthenticateParamIndex struct{} 90 91// AuthenticateParamSimpleTokenPrefix is used for a key of context in the parameters of Authenticate() 92type AuthenticateParamSimpleTokenPrefix struct{} 93 94// saveConsistentIndexFunc is used to sync consistentIndex to backend, now reusing store.saveIndex 95type saveConsistentIndexFunc func(tx backend.BatchTx) 96 97// AuthStore defines auth storage interface. 98type AuthStore interface { 99 // AuthEnable turns on the authentication feature 100 AuthEnable() error 101 102 // AuthDisable turns off the authentication feature 103 AuthDisable() 104 105 // Authenticate does authentication based on given user name and password 106 Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error) 107 108 // Recover recovers the state of auth store from the given backend 109 Recover(b backend.Backend) 110 111 // UserAdd adds a new user 112 UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error) 113 114 // UserDelete deletes a user 115 UserDelete(r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error) 116 117 // UserChangePassword changes a password of a user 118 UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error) 119 120 // UserGrantRole grants a role to the user 121 UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error) 122 123 // UserGet gets the detailed information of a users 124 UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error) 125 126 // UserRevokeRole revokes a role of a user 127 UserRevokeRole(r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error) 128 129 // RoleAdd adds a new role 130 RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) 131 132 // RoleGrantPermission grants a permission to a role 133 RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error) 134 135 // RoleGet gets the detailed information of a role 136 RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error) 137 138 // RoleRevokePermission gets the detailed information of a role 139 RoleRevokePermission(r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error) 140 141 // RoleDelete gets the detailed information of a role 142 RoleDelete(r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error) 143 144 // UserList gets a list of all users 145 UserList(r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error) 146 147 // RoleList gets a list of all roles 148 RoleList(r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error) 149 150 // IsPutPermitted checks put permission of the user 151 IsPutPermitted(authInfo *AuthInfo, key []byte) error 152 153 // IsRangePermitted checks range permission of the user 154 IsRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error 155 156 // IsDeleteRangePermitted checks delete-range permission of the user 157 IsDeleteRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error 158 159 // IsAdminPermitted checks admin permission of the user 160 IsAdminPermitted(authInfo *AuthInfo) error 161 162 // GenTokenPrefix produces a random string in a case of simple token 163 // in a case of JWT, it produces an empty string 164 GenTokenPrefix() (string, error) 165 166 // Revision gets current revision of authStore 167 Revision() uint64 168 169 // CheckPassword checks a given pair of username and password is correct 170 CheckPassword(username, password string) (uint64, error) 171 172 // Close does cleanup of AuthStore 173 Close() error 174 175 // AuthInfoFromCtx gets AuthInfo from gRPC's context 176 AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error) 177 178 // AuthInfoFromTLS gets AuthInfo from TLS info of gRPC's context 179 AuthInfoFromTLS(ctx context.Context) *AuthInfo 180 181 // WithRoot generates and installs a token that can be used as a root credential 182 WithRoot(ctx context.Context) context.Context 183 184 // HasRole checks that user has role 185 HasRole(user, role string) bool 186 187 // SetConsistentIndexSyncer sets consistentIndex syncer 188 SetConsistentIndexSyncer(syncer saveConsistentIndexFunc) 189} 190 191type TokenProvider interface { 192 info(ctx context.Context, token string, revision uint64) (*AuthInfo, bool) 193 assign(ctx context.Context, username string, revision uint64) (string, error) 194 enable() 195 disable() 196 197 invalidateUser(string) 198 genTokenPrefix() (string, error) 199} 200 201type authStore struct { 202 // atomic operations; need 64-bit align, or 32-bit tests will crash 203 revision uint64 204 205 be backend.Backend 206 enabled bool 207 enabledMu sync.RWMutex 208 209 rangePermCache map[string]*unifiedRangePermissions // username -> unifiedRangePermissions 210 211 tokenProvider TokenProvider 212 syncConsistentIndex saveConsistentIndexFunc 213} 214 215func (as *authStore) SetConsistentIndexSyncer(syncer saveConsistentIndexFunc) { 216 as.syncConsistentIndex = syncer 217} 218func (as *authStore) AuthEnable() error { 219 as.enabledMu.Lock() 220 defer as.enabledMu.Unlock() 221 if as.enabled { 222 plog.Noticef("Authentication already enabled") 223 return nil 224 } 225 b := as.be 226 tx := b.BatchTx() 227 tx.Lock() 228 defer func() { 229 tx.Unlock() 230 b.ForceCommit() 231 }() 232 233 u := getUser(tx, rootUser) 234 if u == nil { 235 return ErrRootUserNotExist 236 } 237 238 if !hasRootRole(u) { 239 return ErrRootRoleNotExist 240 } 241 242 tx.UnsafePut(authBucketName, enableFlagKey, authEnabled) 243 244 as.enabled = true 245 as.tokenProvider.enable() 246 247 as.rangePermCache = make(map[string]*unifiedRangePermissions) 248 249 as.setRevision(getRevision(tx)) 250 251 plog.Noticef("Authentication enabled") 252 253 return nil 254} 255 256func (as *authStore) AuthDisable() { 257 as.enabledMu.Lock() 258 defer as.enabledMu.Unlock() 259 if !as.enabled { 260 return 261 } 262 b := as.be 263 tx := b.BatchTx() 264 tx.Lock() 265 tx.UnsafePut(authBucketName, enableFlagKey, authDisabled) 266 as.commitRevision(tx) 267 as.saveConsistentIndex(tx) 268 tx.Unlock() 269 b.ForceCommit() 270 271 as.enabled = false 272 as.tokenProvider.disable() 273 274 plog.Noticef("Authentication disabled") 275} 276 277func (as *authStore) Close() error { 278 as.enabledMu.Lock() 279 defer as.enabledMu.Unlock() 280 if !as.enabled { 281 return nil 282 } 283 as.tokenProvider.disable() 284 return nil 285} 286 287func (as *authStore) Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error) { 288 if !as.isAuthEnabled() { 289 return nil, ErrAuthNotEnabled 290 } 291 292 tx := as.be.BatchTx() 293 tx.Lock() 294 defer tx.Unlock() 295 296 user := getUser(tx, username) 297 if user == nil { 298 return nil, ErrAuthFailed 299 } 300 301 // Password checking is already performed in the API layer, so we don't need to check for now. 302 // Staleness of password can be detected with OCC in the API layer, too. 303 304 token, err := as.tokenProvider.assign(ctx, username, as.Revision()) 305 if err != nil { 306 return nil, err 307 } 308 309 plog.Debugf("authorized %s, token is %s", username, token) 310 return &pb.AuthenticateResponse{Token: token}, nil 311} 312 313func (as *authStore) CheckPassword(username, password string) (uint64, error) { 314 if !as.isAuthEnabled() { 315 return 0, ErrAuthNotEnabled 316 } 317 318 tx := as.be.BatchTx() 319 tx.Lock() 320 defer tx.Unlock() 321 322 user := getUser(tx, username) 323 if user == nil { 324 return 0, ErrAuthFailed 325 } 326 327 if bcrypt.CompareHashAndPassword(user.Password, []byte(password)) != nil { 328 plog.Noticef("authentication failed, invalid password for user %s", username) 329 return 0, ErrAuthFailed 330 } 331 332 return getRevision(tx), nil 333} 334 335func (as *authStore) Recover(be backend.Backend) { 336 enabled := false 337 as.be = be 338 tx := be.BatchTx() 339 tx.Lock() 340 _, vs := tx.UnsafeRange(authBucketName, enableFlagKey, nil, 0) 341 if len(vs) == 1 { 342 if bytes.Equal(vs[0], authEnabled) { 343 enabled = true 344 } 345 } 346 347 as.setRevision(getRevision(tx)) 348 349 tx.Unlock() 350 351 as.enabledMu.Lock() 352 as.enabled = enabled 353 as.enabledMu.Unlock() 354} 355 356func (as *authStore) UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error) { 357 if len(r.Name) == 0 { 358 return nil, ErrUserEmpty 359 } 360 361 hashed, err := bcrypt.GenerateFromPassword([]byte(r.Password), BcryptCost) 362 if err != nil { 363 plog.Errorf("failed to hash password: %s", err) 364 return nil, err 365 } 366 367 tx := as.be.BatchTx() 368 tx.Lock() 369 defer tx.Unlock() 370 371 user := getUser(tx, r.Name) 372 if user != nil { 373 return nil, ErrUserAlreadyExist 374 } 375 376 newUser := &authpb.User{ 377 Name: []byte(r.Name), 378 Password: hashed, 379 } 380 381 putUser(tx, newUser) 382 383 as.commitRevision(tx) 384 as.saveConsistentIndex(tx) 385 386 plog.Noticef("added a new user: %s", r.Name) 387 388 return &pb.AuthUserAddResponse{}, nil 389} 390 391func (as *authStore) UserDelete(r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error) { 392 if as.enabled && strings.Compare(r.Name, rootUser) == 0 { 393 plog.Errorf("the user root must not be deleted") 394 return nil, ErrInvalidAuthMgmt 395 } 396 397 tx := as.be.BatchTx() 398 tx.Lock() 399 defer tx.Unlock() 400 401 user := getUser(tx, r.Name) 402 if user == nil { 403 return nil, ErrUserNotFound 404 } 405 406 delUser(tx, r.Name) 407 408 as.commitRevision(tx) 409 as.saveConsistentIndex(tx) 410 411 as.invalidateCachedPerm(r.Name) 412 as.tokenProvider.invalidateUser(r.Name) 413 414 plog.Noticef("deleted a user: %s", r.Name) 415 416 return &pb.AuthUserDeleteResponse{}, nil 417} 418 419func (as *authStore) UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error) { 420 // TODO(mitake): measure the cost of bcrypt.GenerateFromPassword() 421 // If the cost is too high, we should move the encryption to outside of the raft 422 hashed, err := bcrypt.GenerateFromPassword([]byte(r.Password), BcryptCost) 423 if err != nil { 424 plog.Errorf("failed to hash password: %s", err) 425 return nil, err 426 } 427 428 tx := as.be.BatchTx() 429 tx.Lock() 430 defer tx.Unlock() 431 432 user := getUser(tx, r.Name) 433 if user == nil { 434 return nil, ErrUserNotFound 435 } 436 437 updatedUser := &authpb.User{ 438 Name: []byte(r.Name), 439 Roles: user.Roles, 440 Password: hashed, 441 } 442 443 putUser(tx, updatedUser) 444 445 as.commitRevision(tx) 446 as.saveConsistentIndex(tx) 447 448 as.invalidateCachedPerm(r.Name) 449 as.tokenProvider.invalidateUser(r.Name) 450 451 plog.Noticef("changed a password of a user: %s", r.Name) 452 453 return &pb.AuthUserChangePasswordResponse{}, nil 454} 455 456func (as *authStore) UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error) { 457 tx := as.be.BatchTx() 458 tx.Lock() 459 defer tx.Unlock() 460 461 user := getUser(tx, r.User) 462 if user == nil { 463 return nil, ErrUserNotFound 464 } 465 466 if r.Role != rootRole { 467 role := getRole(tx, r.Role) 468 if role == nil { 469 return nil, ErrRoleNotFound 470 } 471 } 472 473 idx := sort.SearchStrings(user.Roles, r.Role) 474 if idx < len(user.Roles) && strings.Compare(user.Roles[idx], r.Role) == 0 { 475 plog.Warningf("user %s is already granted role %s", r.User, r.Role) 476 return &pb.AuthUserGrantRoleResponse{}, nil 477 } 478 479 user.Roles = append(user.Roles, r.Role) 480 sort.Strings(user.Roles) 481 482 putUser(tx, user) 483 484 as.invalidateCachedPerm(r.User) 485 486 as.commitRevision(tx) 487 as.saveConsistentIndex(tx) 488 489 plog.Noticef("granted role %s to user %s", r.Role, r.User) 490 return &pb.AuthUserGrantRoleResponse{}, nil 491} 492 493func (as *authStore) UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error) { 494 tx := as.be.BatchTx() 495 tx.Lock() 496 user := getUser(tx, r.Name) 497 tx.Unlock() 498 499 if user == nil { 500 return nil, ErrUserNotFound 501 } 502 503 var resp pb.AuthUserGetResponse 504 resp.Roles = append(resp.Roles, user.Roles...) 505 return &resp, nil 506} 507 508func (as *authStore) UserList(r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error) { 509 tx := as.be.BatchTx() 510 tx.Lock() 511 users := getAllUsers(tx) 512 tx.Unlock() 513 514 resp := &pb.AuthUserListResponse{Users: make([]string, len(users))} 515 for i := range users { 516 resp.Users[i] = string(users[i].Name) 517 } 518 return resp, nil 519} 520 521func (as *authStore) UserRevokeRole(r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error) { 522 if as.enabled && strings.Compare(r.Name, rootUser) == 0 && strings.Compare(r.Role, rootRole) == 0 { 523 plog.Errorf("the role root must not be revoked from the user root") 524 return nil, ErrInvalidAuthMgmt 525 } 526 527 tx := as.be.BatchTx() 528 tx.Lock() 529 defer tx.Unlock() 530 531 user := getUser(tx, r.Name) 532 if user == nil { 533 return nil, ErrUserNotFound 534 } 535 536 updatedUser := &authpb.User{ 537 Name: user.Name, 538 Password: user.Password, 539 } 540 541 for _, role := range user.Roles { 542 if strings.Compare(role, r.Role) != 0 { 543 updatedUser.Roles = append(updatedUser.Roles, role) 544 } 545 } 546 547 if len(updatedUser.Roles) == len(user.Roles) { 548 return nil, ErrRoleNotGranted 549 } 550 551 putUser(tx, updatedUser) 552 553 as.invalidateCachedPerm(r.Name) 554 555 as.commitRevision(tx) 556 as.saveConsistentIndex(tx) 557 558 plog.Noticef("revoked role %s from user %s", r.Role, r.Name) 559 return &pb.AuthUserRevokeRoleResponse{}, nil 560} 561 562func (as *authStore) RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error) { 563 tx := as.be.BatchTx() 564 tx.Lock() 565 defer tx.Unlock() 566 567 var resp pb.AuthRoleGetResponse 568 569 role := getRole(tx, r.Role) 570 if role == nil { 571 return nil, ErrRoleNotFound 572 } 573 resp.Perm = append(resp.Perm, role.KeyPermission...) 574 return &resp, nil 575} 576 577func (as *authStore) RoleList(r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error) { 578 tx := as.be.BatchTx() 579 tx.Lock() 580 roles := getAllRoles(tx) 581 tx.Unlock() 582 583 resp := &pb.AuthRoleListResponse{Roles: make([]string, len(roles))} 584 for i := range roles { 585 resp.Roles[i] = string(roles[i].Name) 586 } 587 return resp, nil 588} 589 590func (as *authStore) RoleRevokePermission(r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error) { 591 tx := as.be.BatchTx() 592 tx.Lock() 593 defer tx.Unlock() 594 595 role := getRole(tx, r.Role) 596 if role == nil { 597 return nil, ErrRoleNotFound 598 } 599 600 updatedRole := &authpb.Role{ 601 Name: role.Name, 602 } 603 604 for _, perm := range role.KeyPermission { 605 if !bytes.Equal(perm.Key, []byte(r.Key)) || !bytes.Equal(perm.RangeEnd, []byte(r.RangeEnd)) { 606 updatedRole.KeyPermission = append(updatedRole.KeyPermission, perm) 607 } 608 } 609 610 if len(role.KeyPermission) == len(updatedRole.KeyPermission) { 611 return nil, ErrPermissionNotGranted 612 } 613 614 putRole(tx, updatedRole) 615 616 // TODO(mitake): currently single role update invalidates every cache 617 // It should be optimized. 618 as.clearCachedPerm() 619 620 as.commitRevision(tx) 621 as.saveConsistentIndex(tx) 622 623 plog.Noticef("revoked key %s from role %s", r.Key, r.Role) 624 return &pb.AuthRoleRevokePermissionResponse{}, nil 625} 626 627func (as *authStore) RoleDelete(r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error) { 628 if as.enabled && strings.Compare(r.Role, rootRole) == 0 { 629 plog.Errorf("the role root must not be deleted") 630 return nil, ErrInvalidAuthMgmt 631 } 632 633 tx := as.be.BatchTx() 634 tx.Lock() 635 defer tx.Unlock() 636 637 role := getRole(tx, r.Role) 638 if role == nil { 639 return nil, ErrRoleNotFound 640 } 641 642 delRole(tx, r.Role) 643 644 users := getAllUsers(tx) 645 for _, user := range users { 646 updatedUser := &authpb.User{ 647 Name: user.Name, 648 Password: user.Password, 649 } 650 651 for _, role := range user.Roles { 652 if strings.Compare(role, r.Role) != 0 { 653 updatedUser.Roles = append(updatedUser.Roles, role) 654 } 655 } 656 657 if len(updatedUser.Roles) == len(user.Roles) { 658 continue 659 } 660 661 putUser(tx, updatedUser) 662 663 as.invalidateCachedPerm(string(user.Name)) 664 } 665 666 as.commitRevision(tx) 667 as.saveConsistentIndex(tx) 668 669 plog.Noticef("deleted role %s", r.Role) 670 return &pb.AuthRoleDeleteResponse{}, nil 671} 672 673func (as *authStore) RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) { 674 tx := as.be.BatchTx() 675 tx.Lock() 676 defer tx.Unlock() 677 678 role := getRole(tx, r.Name) 679 if role != nil { 680 return nil, ErrRoleAlreadyExist 681 } 682 683 newRole := &authpb.Role{ 684 Name: []byte(r.Name), 685 } 686 687 putRole(tx, newRole) 688 689 as.commitRevision(tx) 690 as.saveConsistentIndex(tx) 691 692 plog.Noticef("Role %s is created", r.Name) 693 694 return &pb.AuthRoleAddResponse{}, nil 695} 696 697func (as *authStore) authInfoFromToken(ctx context.Context, token string) (*AuthInfo, bool) { 698 return as.tokenProvider.info(ctx, token, as.Revision()) 699} 700 701type permSlice []*authpb.Permission 702 703func (perms permSlice) Len() int { 704 return len(perms) 705} 706 707func (perms permSlice) Less(i, j int) bool { 708 return bytes.Compare(perms[i].Key, perms[j].Key) < 0 709} 710 711func (perms permSlice) Swap(i, j int) { 712 perms[i], perms[j] = perms[j], perms[i] 713} 714 715func (as *authStore) RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error) { 716 tx := as.be.BatchTx() 717 tx.Lock() 718 defer tx.Unlock() 719 720 role := getRole(tx, r.Name) 721 if role == nil { 722 return nil, ErrRoleNotFound 723 } 724 725 idx := sort.Search(len(role.KeyPermission), func(i int) bool { 726 return bytes.Compare(role.KeyPermission[i].Key, []byte(r.Perm.Key)) >= 0 727 }) 728 729 if idx < len(role.KeyPermission) && bytes.Equal(role.KeyPermission[idx].Key, r.Perm.Key) && bytes.Equal(role.KeyPermission[idx].RangeEnd, r.Perm.RangeEnd) { 730 // update existing permission 731 role.KeyPermission[idx].PermType = r.Perm.PermType 732 } else { 733 // append new permission to the role 734 newPerm := &authpb.Permission{ 735 Key: []byte(r.Perm.Key), 736 RangeEnd: []byte(r.Perm.RangeEnd), 737 PermType: r.Perm.PermType, 738 } 739 740 role.KeyPermission = append(role.KeyPermission, newPerm) 741 sort.Sort(permSlice(role.KeyPermission)) 742 } 743 744 putRole(tx, role) 745 746 // TODO(mitake): currently single role update invalidates every cache 747 // It should be optimized. 748 as.clearCachedPerm() 749 750 as.commitRevision(tx) 751 as.saveConsistentIndex(tx) 752 753 plog.Noticef("role %s's permission of key %s is updated as %s", r.Name, r.Perm.Key, authpb.Permission_Type_name[int32(r.Perm.PermType)]) 754 755 return &pb.AuthRoleGrantPermissionResponse{}, nil 756} 757 758func (as *authStore) isOpPermitted(userName string, revision uint64, key, rangeEnd []byte, permTyp authpb.Permission_Type) error { 759 // TODO(mitake): this function would be costly so we need a caching mechanism 760 if !as.isAuthEnabled() { 761 return nil 762 } 763 764 // only gets rev == 0 when passed AuthInfo{}; no user given 765 if revision == 0 { 766 return ErrUserEmpty 767 } 768 rev := as.Revision() 769 if revision < rev { 770 plog.Warningf("request auth revision is less than current node auth revision,"+ 771 "current node auth revision is %d,"+ 772 "request auth revision is %d,"+ 773 "request key is %s, "+ 774 "err is %v", rev, revision, key, ErrAuthOldRevision) 775 return ErrAuthOldRevision 776 } 777 778 tx := as.be.BatchTx() 779 tx.Lock() 780 defer tx.Unlock() 781 782 user := getUser(tx, userName) 783 if user == nil { 784 plog.Errorf("invalid user name %s for permission checking", userName) 785 return ErrPermissionDenied 786 } 787 788 // root role should have permission on all ranges 789 if hasRootRole(user) { 790 return nil 791 } 792 793 if as.isRangeOpPermitted(tx, userName, key, rangeEnd, permTyp) { 794 return nil 795 } 796 797 return ErrPermissionDenied 798} 799 800func (as *authStore) IsPutPermitted(authInfo *AuthInfo, key []byte) error { 801 return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, nil, authpb.WRITE) 802} 803 804func (as *authStore) IsRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error { 805 return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, rangeEnd, authpb.READ) 806} 807 808func (as *authStore) IsDeleteRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error { 809 return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, rangeEnd, authpb.WRITE) 810} 811 812func (as *authStore) IsAdminPermitted(authInfo *AuthInfo) error { 813 if !as.isAuthEnabled() { 814 return nil 815 } 816 if authInfo == nil || authInfo.Username == "" { 817 return ErrUserEmpty 818 } 819 820 tx := as.be.BatchTx() 821 tx.Lock() 822 u := getUser(tx, authInfo.Username) 823 tx.Unlock() 824 825 if u == nil { 826 return ErrUserNotFound 827 } 828 829 if !hasRootRole(u) { 830 return ErrPermissionDenied 831 } 832 833 return nil 834} 835 836func getUser(tx backend.BatchTx, username string) *authpb.User { 837 _, vs := tx.UnsafeRange(authUsersBucketName, []byte(username), nil, 0) 838 if len(vs) == 0 { 839 return nil 840 } 841 842 user := &authpb.User{} 843 err := user.Unmarshal(vs[0]) 844 if err != nil { 845 plog.Panicf("failed to unmarshal user struct (name: %s): %s", username, err) 846 } 847 return user 848} 849 850func getAllUsers(tx backend.BatchTx) []*authpb.User { 851 _, vs := tx.UnsafeRange(authUsersBucketName, []byte{0}, []byte{0xff}, -1) 852 if len(vs) == 0 { 853 return nil 854 } 855 856 users := make([]*authpb.User, len(vs)) 857 for i := range vs { 858 user := &authpb.User{} 859 err := user.Unmarshal(vs[i]) 860 if err != nil { 861 plog.Panicf("failed to unmarshal user struct: %s", err) 862 } 863 users[i] = user 864 } 865 return users 866} 867 868func putUser(tx backend.BatchTx, user *authpb.User) { 869 b, err := user.Marshal() 870 if err != nil { 871 plog.Panicf("failed to marshal user struct (name: %s): %s", user.Name, err) 872 } 873 tx.UnsafePut(authUsersBucketName, user.Name, b) 874} 875 876func delUser(tx backend.BatchTx, username string) { 877 tx.UnsafeDelete(authUsersBucketName, []byte(username)) 878} 879 880func getRole(tx backend.BatchTx, rolename string) *authpb.Role { 881 _, vs := tx.UnsafeRange(authRolesBucketName, []byte(rolename), nil, 0) 882 if len(vs) == 0 { 883 return nil 884 } 885 886 role := &authpb.Role{} 887 err := role.Unmarshal(vs[0]) 888 if err != nil { 889 plog.Panicf("failed to unmarshal role struct (name: %s): %s", rolename, err) 890 } 891 return role 892} 893 894func getAllRoles(tx backend.BatchTx) []*authpb.Role { 895 _, vs := tx.UnsafeRange(authRolesBucketName, []byte{0}, []byte{0xff}, -1) 896 if len(vs) == 0 { 897 return nil 898 } 899 900 roles := make([]*authpb.Role, len(vs)) 901 for i := range vs { 902 role := &authpb.Role{} 903 err := role.Unmarshal(vs[i]) 904 if err != nil { 905 plog.Panicf("failed to unmarshal role struct: %s", err) 906 } 907 roles[i] = role 908 } 909 return roles 910} 911 912func putRole(tx backend.BatchTx, role *authpb.Role) { 913 b, err := role.Marshal() 914 if err != nil { 915 plog.Panicf("failed to marshal role struct (name: %s): %s", role.Name, err) 916 } 917 918 tx.UnsafePut(authRolesBucketName, []byte(role.Name), b) 919} 920 921func delRole(tx backend.BatchTx, rolename string) { 922 tx.UnsafeDelete(authRolesBucketName, []byte(rolename)) 923} 924 925func (as *authStore) isAuthEnabled() bool { 926 as.enabledMu.RLock() 927 defer as.enabledMu.RUnlock() 928 return as.enabled 929} 930 931func NewAuthStore(be backend.Backend, tp TokenProvider) *authStore { 932 tx := be.BatchTx() 933 tx.Lock() 934 935 tx.UnsafeCreateBucket(authBucketName) 936 tx.UnsafeCreateBucket(authUsersBucketName) 937 tx.UnsafeCreateBucket(authRolesBucketName) 938 939 enabled := false 940 _, vs := tx.UnsafeRange(authBucketName, enableFlagKey, nil, 0) 941 if len(vs) == 1 { 942 if bytes.Equal(vs[0], authEnabled) { 943 enabled = true 944 } 945 } 946 947 as := &authStore{ 948 be: be, 949 revision: getRevision(tx), 950 enabled: enabled, 951 rangePermCache: make(map[string]*unifiedRangePermissions), 952 tokenProvider: tp, 953 } 954 955 if enabled { 956 as.tokenProvider.enable() 957 } 958 959 if as.Revision() == 0 { 960 as.commitRevision(tx) 961 } 962 963 as.setupMetricsReporter() 964 965 tx.Unlock() 966 be.ForceCommit() 967 968 return as 969} 970 971func hasRootRole(u *authpb.User) bool { 972 // u.Roles is sorted in UserGrantRole(), so we can use binary search. 973 idx := sort.SearchStrings(u.Roles, rootRole) 974 return idx != len(u.Roles) && u.Roles[idx] == rootRole 975} 976 977func (as *authStore) commitRevision(tx backend.BatchTx) { 978 atomic.AddUint64(&as.revision, 1) 979 revBytes := make([]byte, revBytesLen) 980 binary.BigEndian.PutUint64(revBytes, as.Revision()) 981 tx.UnsafePut(authBucketName, revisionKey, revBytes) 982} 983 984func getRevision(tx backend.BatchTx) uint64 { 985 _, vs := tx.UnsafeRange(authBucketName, []byte(revisionKey), nil, 0) 986 if len(vs) != 1 { 987 // this can happen in the initialization phase 988 return 0 989 } 990 991 return binary.BigEndian.Uint64(vs[0]) 992} 993 994func (as *authStore) setRevision(rev uint64) { 995 atomic.StoreUint64(&as.revision, rev) 996} 997 998func (as *authStore) Revision() uint64 { 999 return atomic.LoadUint64(&as.revision) 1000} 1001 1002func (as *authStore) AuthInfoFromTLS(ctx context.Context) *AuthInfo { 1003 peer, ok := peer.FromContext(ctx) 1004 if !ok || peer == nil || peer.AuthInfo == nil { 1005 return nil 1006 } 1007 1008 tlsInfo := peer.AuthInfo.(credentials.TLSInfo) 1009 for _, chains := range tlsInfo.State.VerifiedChains { 1010 for _, chain := range chains { 1011 cn := chain.Subject.CommonName 1012 plog.Debugf("found common name %s", cn) 1013 1014 ai := &AuthInfo{ 1015 Username: cn, 1016 Revision: as.Revision(), 1017 } 1018 md, ok := metadata.FromIncomingContext(ctx) 1019 if !ok { 1020 return nil 1021 } 1022 1023 // gRPC-gateway proxy request to etcd server includes Grpcgateway-Accept 1024 // header. The proxy uses etcd client server certificate. If the certificate 1025 // has a CommonName we should never use this for authentication. 1026 if gw := md["grpcgateway-accept"]; len(gw) > 0 { 1027 plog.Warningf("ignoring common name in gRPC-gateway proxy request %s", ai.Username) 1028 return nil 1029 } 1030 return ai 1031 } 1032 } 1033 1034 return nil 1035} 1036 1037func (as *authStore) AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error) { 1038 md, ok := metadata.FromIncomingContext(ctx) 1039 if !ok { 1040 return nil, nil 1041 } 1042 1043 //TODO(mitake|hexfusion) review unifying key names 1044 ts, ok := md["token"] 1045 if !ok { 1046 ts, ok = md["authorization"] 1047 } 1048 if !ok { 1049 return nil, nil 1050 } 1051 1052 token := ts[0] 1053 authInfo, uok := as.authInfoFromToken(ctx, token) 1054 if !uok { 1055 plog.Warningf("invalid auth token: %s", token) 1056 return nil, ErrInvalidAuthToken 1057 } 1058 1059 return authInfo, nil 1060} 1061 1062func (as *authStore) GenTokenPrefix() (string, error) { 1063 return as.tokenProvider.genTokenPrefix() 1064} 1065 1066func decomposeOpts(optstr string) (string, map[string]string, error) { 1067 opts := strings.Split(optstr, ",") 1068 tokenType := opts[0] 1069 1070 typeSpecificOpts := make(map[string]string) 1071 for i := 1; i < len(opts); i++ { 1072 pair := strings.Split(opts[i], "=") 1073 1074 if len(pair) != 2 { 1075 plog.Errorf("invalid token specific option: %s", optstr) 1076 return "", nil, ErrInvalidAuthOpts 1077 } 1078 1079 if _, ok := typeSpecificOpts[pair[0]]; ok { 1080 plog.Errorf("invalid token specific option, duplicated parameters (%s): %s", pair[0], optstr) 1081 return "", nil, ErrInvalidAuthOpts 1082 } 1083 1084 typeSpecificOpts[pair[0]] = pair[1] 1085 } 1086 1087 return tokenType, typeSpecificOpts, nil 1088 1089} 1090 1091// NewTokenProvider creates a new token provider. 1092func NewTokenProvider( 1093 tokenOpts string, 1094 indexWaiter func(uint64) <-chan struct{}, 1095 TokenTTL time.Duration) (TokenProvider, error) { 1096 tokenType, typeSpecificOpts, err := decomposeOpts(tokenOpts) 1097 if err != nil { 1098 return nil, ErrInvalidAuthOpts 1099 } 1100 1101 switch tokenType { 1102 case tokenTypeSimple: 1103 plog.Warningf("simple token is not cryptographically signed") 1104 return newTokenProviderSimple(indexWaiter, TokenTTL), nil 1105 1106 case tokenTypeJWT: 1107 return newTokenProviderJWT(typeSpecificOpts) 1108 1109 case "": 1110 return newTokenProviderNop() 1111 default: 1112 plog.Errorf("unknown token type: %s", tokenType) 1113 return nil, ErrInvalidAuthOpts 1114 } 1115} 1116 1117func (as *authStore) WithRoot(ctx context.Context) context.Context { 1118 if !as.isAuthEnabled() { 1119 return ctx 1120 } 1121 1122 var ctxForAssign context.Context 1123 if ts, ok := as.tokenProvider.(*tokenSimple); ok && ts != nil { 1124 ctx1 := context.WithValue(ctx, AuthenticateParamIndex{}, uint64(0)) 1125 prefix, err := ts.genTokenPrefix() 1126 if err != nil { 1127 plog.Errorf("failed to generate prefix of internally used token") 1128 return ctx 1129 } 1130 ctxForAssign = context.WithValue(ctx1, AuthenticateParamSimpleTokenPrefix{}, prefix) 1131 } else { 1132 ctxForAssign = ctx 1133 } 1134 1135 token, err := as.tokenProvider.assign(ctxForAssign, "root", as.Revision()) 1136 if err != nil { 1137 // this must not happen 1138 plog.Errorf("failed to assign token for lease revoking: %s", err) 1139 return ctx 1140 } 1141 1142 mdMap := map[string]string{ 1143 "token": token, 1144 } 1145 tokenMD := metadata.New(mdMap) 1146 1147 // use "mdIncomingKey{}" since it's called from local etcdserver 1148 return metadata.NewIncomingContext(ctx, tokenMD) 1149} 1150 1151func (as *authStore) HasRole(user, role string) bool { 1152 tx := as.be.BatchTx() 1153 tx.Lock() 1154 u := getUser(tx, user) 1155 tx.Unlock() 1156 1157 if u == nil { 1158 plog.Warningf("tried to check user %s has role %s, but user %s doesn't exist", user, role, user) 1159 return false 1160 } 1161 1162 for _, r := range u.Roles { 1163 if role == r { 1164 return true 1165 } 1166 } 1167 1168 return false 1169} 1170 1171func (as *authStore) saveConsistentIndex(tx backend.BatchTx) { 1172 if as.syncConsistentIndex != nil { 1173 as.syncConsistentIndex(tx) 1174 } else { 1175 plog.Errorf("failed to save consistentIndex,syncConsistentIndex is nil") 1176 } 1177} 1178 1179func (as *authStore) setupMetricsReporter() { 1180 reportCurrentAuthRevMu.Lock() 1181 reportCurrentAuthRev = func() float64 { 1182 return float64(as.Revision()) 1183 } 1184 reportCurrentAuthRevMu.Unlock() 1185} 1186