Skip to content

Commit

Permalink
auth, etcdserver: separate auth checking apply from core apply
Browse files Browse the repository at this point in the history
  • Loading branch information
Anthony Romano committed Jun 15, 2016
1 parent 16d86fd commit 84ba5ea
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 99 deletions.
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
}
}
101 changes: 101 additions & 0 deletions etcdserver/apply_auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// 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, err := aa.username(r)
if err != nil {
return &applyResult{err: err}
}
aa.user = user
return aa.applierV3.Apply(r)
}

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 (aa *authApplierV3) username(r *pb.InternalRaftRequest) (string, error) {
user := r.Header.Username
if needAdminPermission(r) && !aa.as.IsAdminPermitted(user) {
return "", auth.ErrPermissionDenied
}
return user, nil
}

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

0 comments on commit 84ba5ea

Please sign in to comment.