Skip to content
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

Merged
merged 2 commits into from
Dec 15, 2016
Merged

Conversation

mitake
Copy link
Contributor

@mitake mitake commented Nov 28, 2016

This PR add a mechanism for protecting the membership change RPCs with auth.

Fixes #6899

@@ -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
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

@heyitsanthony
Copy link
Contributor

this seems too invasive; I don't think raftpb should know anything about auth revisions. Maybe reuse Context?

@mitake
Copy link
Contributor Author

mitake commented Nov 29, 2016

@heyitsanthony Which Context do you mean? I agree that changing raftpb is a large change, but the information must be propagated via network. So at least one message struct must be modified. Or is the http header of rafthttp more suitable for the auth revision?

@heyitsanthony
Copy link
Contributor

@mitake there's a Context field in raftpb.ConfChange that's being filled out with a json structure in etcdserver. If the auth rev is going to be passed through raft, it probably belongs inside of that (there's already a comment saying it needs to be changed to protobuf). The only drawback is etcd will have to support both the json and protobuf message for a while.

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.

@mitake
Copy link
Contributor Author

mitake commented Nov 30, 2016

@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.

@mitake
Copy link
Contributor Author

mitake commented Dec 6, 2016

@heyitsanthony updated for your comments.

  • now authStore.isAuthEnabled() is private again
  • the version number validation of confchange was removed (instead of it I added a comment of the potential problem)

Could you take a look?

Copy link
Contributor

@heyitsanthony heyitsanthony left a 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)
Copy link
Contributor

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

Copy link
Contributor Author

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 {
Copy link
Contributor

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)

Copy link
Contributor Author

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)
Copy link
Contributor

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)

Copy link
Contributor Author

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?

Copy link
Contributor

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 {
Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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.

@mitake
Copy link
Contributor Author

mitake commented Dec 12, 2016

@heyitsanthony updated based on your comments, PTAL.

  • added a nil check to EtcdServer.checkMembershipOperationPermission() and reduced the dummy auth store
  • unified membership management functions for cleaning e2e

@heyitsanthony
Copy link
Contributor

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 {
Copy link
Contributor

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.

Copy link
Contributor Author

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 {
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@xiang90
Copy link
Contributor

xiang90 commented Dec 14, 2016

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
@mitake
Copy link
Contributor Author

mitake commented Dec 15, 2016

@xiang90 updated for the comments, PTAL.

@xiang90
Copy link
Contributor

xiang90 commented Dec 15, 2016

lgtm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants