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

auth, etcdserver: separate auth checking apply from core apply #5672

Merged
merged 1 commit into from
Jun 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions auth/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ type AuthStore interface {
UsernameFromToken(token string) (string, bool)

// IsPutPermitted checks put permission of the user
IsPutPermitted(header *pb.RequestHeader, key []byte) bool
IsPutPermitted(username string, key []byte) bool

// IsRangePermitted checks range permission of the user
IsRangePermitted(header *pb.RequestHeader, key, rangeEnd []byte) bool
IsRangePermitted(username string, key, rangeEnd []byte) bool

// IsDeleteRangePermitted checks delete-range permission of the user
IsDeleteRangePermitted(username string, key, rangeEnd []byte) bool
Expand Down Expand Up @@ -570,12 +570,12 @@ func (as *authStore) isOpPermitted(userName string, key, rangeEnd []byte, permTy
return false
}

func (as *authStore) IsPutPermitted(header *pb.RequestHeader, key []byte) bool {
return as.isOpPermitted(header.Username, key, nil, authpb.WRITE)
func (as *authStore) IsPutPermitted(username string, key []byte) bool {
return as.isOpPermitted(username, key, nil, authpb.WRITE)
}

func (as *authStore) IsRangePermitted(header *pb.RequestHeader, key, rangeEnd []byte) bool {
return as.isOpPermitted(header.Username, key, rangeEnd, authpb.READ)
func (as *authStore) IsRangePermitted(username string, key, rangeEnd []byte) bool {
return as.isOpPermitted(username, key, rangeEnd, authpb.READ)
}

func (as *authStore) IsDeleteRangePermitted(username string, key, rangeEnd []byte) bool {
Expand Down
123 changes: 41 additions & 82 deletions etcdserver/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"sort"
"time"

"github.com/coreos/etcd/auth"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/lease"
"github.com/coreos/etcd/mvcc"
Expand Down Expand Up @@ -50,6 +49,8 @@ type applyResult struct {

// applierV3 is the interface for processing V3 raft messages
type applierV3 interface {
Apply(r *pb.InternalRaftRequest) *applyResult

Put(txnID int64, p *pb.PutRequest) (*pb.PutResponse, error)
Range(txnID int64, r *pb.RangeRequest) (*pb.RangeResponse, error)
DeleteRange(txnID int64, dr *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error)
Expand All @@ -61,9 +62,11 @@ type applierV3 interface {

Alarm(*pb.AlarmRequest) (*pb.AlarmResponse, error)

Authenticate(r *pb.InternalAuthenticateRequest) (*pb.AuthenticateResponse, error)

AuthEnable() (*pb.AuthEnableResponse, error)
AuthDisable() (*pb.AuthDisableResponse, error)
Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error)

UserAdd(ua *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error)
UserDelete(ua *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error)
UserChangePassword(ua *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error)
Expand All @@ -81,74 +84,62 @@ type applierV3backend struct {
s *EtcdServer
}

func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) *applyResult {
ar := &applyResult{}
username := r.Header.Username
func (s *EtcdServer) newApplierV3() applierV3 {
return newAuthApplierV3(
s.AuthStore(),
newQuotaApplierV3(s, &applierV3backend{s}),
)
}

if needAdminPermission(r) && !s.AuthStore().IsAdminPermitted(username) {
ar.err = auth.ErrPermissionDenied
return ar
}
func (a *applierV3backend) Apply(r *pb.InternalRaftRequest) *applyResult {
ar := &applyResult{}

// call into a.s.applyV3.F instead of a.F so upper appliers can check individual calls
switch {
case r.Range != nil:
if s.AuthStore().IsRangePermitted(r.Header, r.Range.Key, r.Range.RangeEnd) {
ar.resp, ar.err = s.applyV3.Range(noTxn, r.Range)
} else {
ar.err = auth.ErrPermissionDenied
}
ar.resp, ar.err = a.s.applyV3.Range(noTxn, r.Range)
case r.Put != nil:
if s.AuthStore().IsPutPermitted(r.Header, r.Put.Key) {
ar.resp, ar.err = s.applyV3.Put(noTxn, r.Put)
} else {
ar.err = auth.ErrPermissionDenied
}
ar.resp, ar.err = a.s.applyV3.Put(noTxn, r.Put)
case r.DeleteRange != nil:
if s.AuthStore().IsDeleteRangePermitted(r.Header.Username, r.DeleteRange.Key, r.DeleteRange.RangeEnd) {
ar.resp, ar.err = s.applyV3.DeleteRange(noTxn, r.DeleteRange)
} else {
ar.err = auth.ErrPermissionDenied
}
ar.resp, ar.err = a.s.applyV3.DeleteRange(noTxn, r.DeleteRange)
case r.Txn != nil:
ar.resp, ar.err = s.applyV3.Txn(r.Txn)
ar.resp, ar.err = a.s.applyV3.Txn(r.Txn)
case r.Compaction != nil:
ar.resp, ar.physc, ar.err = s.applyV3.Compaction(r.Compaction)
ar.resp, ar.physc, ar.err = a.s.applyV3.Compaction(r.Compaction)
case r.LeaseGrant != nil:
ar.resp, ar.err = s.applyV3.LeaseGrant(r.LeaseGrant)
ar.resp, ar.err = a.s.applyV3.LeaseGrant(r.LeaseGrant)
case r.LeaseRevoke != nil:
ar.resp, ar.err = s.applyV3.LeaseRevoke(r.LeaseRevoke)
ar.resp, ar.err = a.s.applyV3.LeaseRevoke(r.LeaseRevoke)
case r.Alarm != nil:
ar.resp, ar.err = s.applyV3.Alarm(r.Alarm)

ar.resp, ar.err = a.s.applyV3.Alarm(r.Alarm)
case r.Authenticate != nil:
ar.resp, ar.err = a.s.applyV3.Authenticate(r.Authenticate)
case r.AuthEnable != nil:
ar.resp, ar.err = s.applyV3.AuthEnable()
ar.resp, ar.err = a.s.applyV3.AuthEnable()
case r.AuthDisable != nil:
ar.resp, ar.err = s.applyV3.AuthDisable()
case r.Authenticate != nil:
ctx := context.WithValue(context.WithValue(context.TODO(), "index", s.consistIndex.ConsistentIndex()), "simpleToken", r.Authenticate.SimpleToken)
ar.resp, ar.err = s.applyV3.Authenticate(ctx, r.Authenticate.Name, r.Authenticate.Password)
ar.resp, ar.err = a.s.applyV3.AuthDisable()
case r.AuthUserAdd != nil:
ar.resp, ar.err = s.applyV3.UserAdd(r.AuthUserAdd)
ar.resp, ar.err = a.s.applyV3.UserAdd(r.AuthUserAdd)
case r.AuthUserDelete != nil:
ar.resp, ar.err = s.applyV3.UserDelete(r.AuthUserDelete)
ar.resp, ar.err = a.s.applyV3.UserDelete(r.AuthUserDelete)
case r.AuthUserChangePassword != nil:
ar.resp, ar.err = s.applyV3.UserChangePassword(r.AuthUserChangePassword)
ar.resp, ar.err = a.s.applyV3.UserChangePassword(r.AuthUserChangePassword)
case r.AuthUserGrantRole != nil:
ar.resp, ar.err = s.applyV3.UserGrantRole(r.AuthUserGrantRole)
ar.resp, ar.err = a.s.applyV3.UserGrantRole(r.AuthUserGrantRole)
case r.AuthUserGet != nil:
ar.resp, ar.err = s.applyV3.UserGet(r.AuthUserGet)
ar.resp, ar.err = a.s.applyV3.UserGet(r.AuthUserGet)
case r.AuthUserRevokeRole != nil:
ar.resp, ar.err = s.applyV3.UserRevokeRole(r.AuthUserRevokeRole)
ar.resp, ar.err = a.s.applyV3.UserRevokeRole(r.AuthUserRevokeRole)
case r.AuthRoleAdd != nil:
ar.resp, ar.err = s.applyV3.RoleAdd(r.AuthRoleAdd)
ar.resp, ar.err = a.s.applyV3.RoleAdd(r.AuthRoleAdd)
case r.AuthRoleGrantPermission != nil:
ar.resp, ar.err = s.applyV3.RoleGrantPermission(r.AuthRoleGrantPermission)
ar.resp, ar.err = a.s.applyV3.RoleGrantPermission(r.AuthRoleGrantPermission)
case r.AuthRoleGet != nil:
ar.resp, ar.err = s.applyV3.RoleGet(r.AuthRoleGet)
ar.resp, ar.err = a.s.applyV3.RoleGet(r.AuthRoleGet)
case r.AuthRoleRevokePermission != nil:
ar.resp, ar.err = s.applyV3.RoleRevokePermission(r.AuthRoleRevokePermission)
ar.resp, ar.err = a.s.applyV3.RoleRevokePermission(r.AuthRoleRevokePermission)
case r.AuthRoleDelete != nil:
ar.resp, ar.err = s.applyV3.RoleDelete(r.AuthRoleDelete)
ar.resp, ar.err = a.s.applyV3.RoleDelete(r.AuthRoleDelete)
default:
panic("not implemented")
}
Expand Down Expand Up @@ -503,7 +494,7 @@ func (a *applierV3backend) Alarm(ar *pb.AlarmRequest) (*pb.AlarmResponse, error)
switch m.Alarm {
case pb.AlarmType_NOSPACE:
plog.Infof("alarm disarmed %+v", ar)
a.s.applyV3 = newQuotaApplierV3(a.s, &applierV3backend{a.s})
a.s.applyV3 = a.s.newApplierV3()
default:
plog.Errorf("unimplemented alarm deactivation (%+v)", m)
}
Expand Down Expand Up @@ -550,8 +541,9 @@ func (a *applierV3backend) AuthDisable() (*pb.AuthDisableResponse, error) {
return &pb.AuthDisableResponse{}, nil
}

func (a *applierV3backend) Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error) {
return a.s.AuthStore().Authenticate(ctx, username, password)
func (a *applierV3backend) Authenticate(r *pb.InternalAuthenticateRequest) (*pb.AuthenticateResponse, error) {
ctx := context.WithValue(context.WithValue(context.TODO(), "index", a.s.consistIndex.ConsistentIndex()), "simpleToken", r.SimpleToken)
return a.s.AuthStore().Authenticate(ctx, r.Name, r.Password)
}

func (a *applierV3backend) UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error) {
Expand Down Expand Up @@ -727,36 +719,3 @@ func compareInt64(a, b int64) int {
func isGteRange(rangeEnd []byte) bool {
return len(rangeEnd) == 1 && rangeEnd[0] == 0
}

func needAdminPermission(r *pb.InternalRaftRequest) bool {
switch {
case r.AuthEnable != nil:
return true
case r.AuthDisable != nil:
return true
case r.AuthUserAdd != nil:
return true
case r.AuthUserDelete != nil:
return true
case r.AuthUserChangePassword != nil:
return true
case r.AuthUserGrantRole != nil:
return true
case r.AuthUserGet != nil:
return true
case r.AuthUserRevokeRole != nil:
return true
case r.AuthRoleAdd != nil:
return true
case r.AuthRoleGrantPermission != nil:
return true
case r.AuthRoleGet != nil:
return true
case r.AuthRoleRevokePermission != nil:
return true
case r.AuthRoleDelete != nil:
return true
default:
return false
}
}
95 changes: 95 additions & 0 deletions etcdserver/apply_auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package etcdserver

import (
"github.com/coreos/etcd/auth"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
)

type authApplierV3 struct {
applierV3
as auth.AuthStore
user string
}

func newAuthApplierV3(as auth.AuthStore, base applierV3) *authApplierV3 {
return &authApplierV3{base, as, ""}
}

func (aa *authApplierV3) Apply(r *pb.InternalRaftRequest) *applyResult {
user := r.Header.Username
if needAdminPermission(r) && !aa.as.IsAdminPermitted(user) {
return &applyResult{err: auth.ErrPermissionDenied}
}
aa.user = user
ret := aa.applierV3.Apply(r)
aa.user = ""
return ret
}

func (aa *authApplierV3) Put(txnID int64, r *pb.PutRequest) (*pb.PutResponse, error) {
if !aa.as.IsPutPermitted(aa.user, r.Key) {
return nil, auth.ErrPermissionDenied
}
return aa.applierV3.Put(txnID, r)
Copy link
Contributor

Choose a reason for hiding this comment

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

How about clearing aa.user after checking for ensuring the field is not used more than once?

}

func (aa *authApplierV3) Range(txnID int64, r *pb.RangeRequest) (*pb.RangeResponse, error) {
if !aa.as.IsRangePermitted(aa.user, r.Key, r.RangeEnd) {
return nil, auth.ErrPermissionDenied
}
return aa.applierV3.Range(txnID, r)
}

func (aa *authApplierV3) DeleteRange(txnID int64, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
if !aa.as.IsDeleteRangePermitted(aa.user, r.Key, r.RangeEnd) {
return nil, auth.ErrPermissionDenied
}
return aa.applierV3.DeleteRange(txnID, r)
}

func needAdminPermission(r *pb.InternalRaftRequest) bool {
switch {
case r.AuthEnable != nil:
return true
case r.AuthDisable != nil:
return true
case r.AuthUserAdd != nil:
return true
case r.AuthUserDelete != nil:
return true
case r.AuthUserChangePassword != nil:
return true
case r.AuthUserGrantRole != nil:
return true
case r.AuthUserGet != nil:
return true
case r.AuthUserRevokeRole != nil:
return true
case r.AuthRoleAdd != nil:
return true
case r.AuthRoleGrantPermission != nil:
return true
case r.AuthRoleGet != nil:
return true
case r.AuthRoleRevokePermission != nil:
return true
case r.AuthRoleDelete != nil:
return true
default:
return false
}
}
5 changes: 2 additions & 3 deletions etcdserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1072,7 +1072,7 @@ func (s *EtcdServer) applyEntryNormal(e *raftpb.Entry) {

// set the consistent index of current executing entry
s.consistIndex.setConsistentIndex(e.Index)
ar := s.applyV3Request(&raftReq)
ar := s.applyV3.Apply(&raftReq)
s.setAppliedIndex(e.Index)
if ar.err != ErrNoSpace || len(s.alarmStore.Get(pb.AlarmType_NOSPACE)) > 0 {
s.w.Trigger(id, ar)
Expand Down Expand Up @@ -1311,8 +1311,7 @@ func (s *EtcdServer) Backend() backend.Backend {
func (s *EtcdServer) AuthStore() auth.AuthStore { return s.authStore }

func (s *EtcdServer) restoreAlarms() error {
s.applyV3 = newQuotaApplierV3(s, &applierV3backend{s})

s.applyV3 = s.newApplierV3()
as, err := alarm.NewAlarmStore(s)
if err != nil {
return err
Expand Down
17 changes: 9 additions & 8 deletions etcdserver/v3_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"strings"
"time"

"github.com/coreos/etcd/auth"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/lease"
"github.com/coreos/etcd/lease/leasehttp"
Expand Down Expand Up @@ -76,19 +75,21 @@ type Authenticator interface {
}

func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) {
var result *applyResult
var err error

if r.Serializable {
user, err := s.usernameFromCtx(ctx)
if err != nil {
return nil, err
}
hdr := &pb.RequestHeader{Username: user}
if !s.AuthStore().IsRangePermitted(hdr, r.Key, r.RangeEnd) {
return nil, auth.ErrPermissionDenied
}
return s.applyV3.Range(noTxn, r)
result = s.applyV3.Apply(
&pb.InternalRaftRequest{
Header: &pb.RequestHeader{Username: user},
Range: r})
} else {
result, err = s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Range: r})
}

result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Range: r})
if err != nil {
return nil, err
}
Expand Down