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