blob: 2877bb8cdaade790efda70fb265e8be208ac1981 [file] [log] [blame]
khenaidoo26721882021-08-11 17:42:52 -04001// 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}