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