Browse Source

Merge pull request #5672 from heyitsanthony/applier-auth-layer

auth, etcdserver: separate auth checking apply from core apply
Anthony Romano 9 years ago
parent
commit
b98fa063c8
5 changed files with 153 additions and 99 deletions
  1. 6 6
      auth/store.go
  2. 41 82
      etcdserver/apply.go
  3. 95 0
      etcdserver/apply_auth.go
  4. 2 3
      etcdserver/server.go
  5. 9 8
      etcdserver/v3_server.go

+ 6 - 6
auth/store.go

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

+ 41 - 82
etcdserver/apply.go

@@ -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"
@@ -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)
@@ -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)
@@ -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")
 	}
@@ -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)
 		}
@@ -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) {
@@ -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 - 0
etcdserver/apply_auth.go

@@ -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)
+}
+
+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
+	}
+}

+ 2 - 3
etcdserver/server.go

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

+ 9 - 8
etcdserver/v3_server.go

@@ -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"
@@ -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
 	}