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