-
Notifications
You must be signed in to change notification settings - Fork 9.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
protect membership change RPCs with auth #6903
Conversation
@@ -150,6 +150,9 @@ type AuthStore interface { | |||
|
|||
// CheckPassword checks a given pair of username and password is correct | |||
CheckPassword(username, password string) (uint64, error) | |||
|
|||
// IsAuthEnabled checks authentication is enabled or not | |||
IsAuthEnabled() bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IsEnabled
stuff leads to a lot of split paths. Ideally it shouldn't be necesary; if auth is disabled either the authstore would be niled out (still has path splitting, though) or the authstore would use a dummy auth that passes everything through.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I'll try to eliminate IsEnabled()
in the next update.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So long as it's unexported it's OK. I'd like to get rid of the in-package usage, but what I have in mind would be another patchset since it'll need some changes to the etcdserver apply stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, my writing was bad. Of course I didn't intend to remove the function, just only make it local again. I'll add conditions based on authStore is nil or not to etcdserver side.
this seems too invasive; I don't think raftpb should know anything about auth revisions. Maybe reuse |
@heyitsanthony Which |
@mitake there's a I don't think raft/ or rafthttp/ should know anything about auth if possible since those are lower level packages than etcdserver and auth. Discussed the raft bits with @xiang90 yesterday; he's OK with doing a check at the api layer (instead of putting it through raft) and risking ignoring a permission revoke between that check and the apply. I assume you want stronger consistency guarantees, though. |
@heyitsanthony sorry, I missed the field... anyway, both of permission revoke and membership change require the root role, so the risk would be able to be avoided by admins' careful operations. I'll remove the apply side checking and retry in the API layer in the next update. |
@heyitsanthony updated for your comments.
Could you take a look? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
approach looks OK in general
|
||
// ordinal user cannot add a new member | ||
cx.user, cx.pass = "test-user", "pass" | ||
peerURL := fmt.Sprintf("http://localhost:%d", etcdProcessBasePort+11) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these should probably be made full-fledged functions in ctl_v3_member_test.go
like func ctlV3MemberAdd(cx ctlCtx) error
, etc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I'll add new fledged functions and modify callers in both of ctl_v3_auth_test.go
and ctl_v3_member_test.go
.
// ordinal user cannot remove a member | ||
cx.user, cx.pass = "test-user", "pass" | ||
cmdArgs := append(cx.PrefixArgs(), "member", "remove", memIDToRemove) | ||
if err := spawnWithExpect(cmdArgs, "Error: etcdserver: permission denied"); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
err := ctlV3MemberRemove(cx, resp.Header.MemberId, resp.Header.ClusterId)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll use the function, thanks.
return err | ||
} | ||
|
||
err = s.AuthStore().IsAdminPermitted(authInfo) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return s.AuthStore().IsAdminPermitted(authInfo)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. But if we allow EtcdServer.authStore == nil
, the return value of s.AuthStore()
cannot be used directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah it would need if s.AuthStore() == nil { return nil }
at the start of the function...
@@ -1002,6 +1002,26 @@ func (s *EtcdServer) LeaderStats() []byte { | |||
|
|||
func (s *EtcdServer) StoreStats() []byte { return s.store.JsonStats() } | |||
|
|||
func (s *EtcdServer) checkMembershipOperationPermission(ctx context.Context) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this return nil if authStore is nil instead of needing the dummy stuff?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Allowing EtcdServer.authStore == nil
is ok for me but it requires many changes because the callers of EtcdServer.AuthStore()
must be changed and there are more than 20 callers. Is it ok?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the tests only needed to be updated with non-nil because it started hitting this path? Or is it breaking somewhere else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, we'll need changes in auth related functions of applierV3Backend
? The changes won't be costly but will make this PR a little bit bloated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh? Why would all that be necessary? It was working fine allowing authStore == nil
in the tests. The server_test.go
membership tests now need != nil
plus the dummy auth code because this function does not do a nil check. If there's a nil check in this function, all that new code can be removed, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, I misunderstood your intention. You wanted to add the nil check mainly for test code, but I thought that you were talking about the idea of detecting auth is enabled or not with EtcdServer.authStore
is nil or not instead of calling EtcdServer.isAuthEnabled()
(yes, if this understanding is correct, two ways of detection coexist...). Of course I agree with your idea of reducing the dummy code and add nil check to test code. Sorry for that.
@heyitsanthony updated based on your comments, PTAL.
|
lgtm /cc @xiang90 |
@@ -1015,6 +1036,10 @@ func (s *EtcdServer) AddMember(ctx context.Context, memb membership.Member) erro | |||
} | |||
} | |||
|
|||
if err := s.checkMembershipOperationPermission(ctx); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we probably should move before line 1027.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems reasonable, I'll fix it soon.
@@ -1034,6 +1059,10 @@ func (s *EtcdServer) RemoveMember(ctx context.Context, id uint64) error { | |||
return err | |||
} | |||
|
|||
if err := s.checkMembershipOperationPermission(ctx); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move this before line 1057?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
Minor issues. LGTM after fixing them. Thanks a lot! |
This commit protects membership change operations with auth. Only users that have root role can issue the operations. Implements etcd-io#6899
@xiang90 updated for the comments, PTAL. |
lgtm |
This PR add a mechanism for protecting the membership change RPCs with auth.
Fixes #6899