mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
auth, etcdserver: protect revoking lease with auth
Currently clients can revoke any lease without permission. This commit lets etcdserver protect revoking with write permission. This commit adds a mechanism for generating internal token. It is used for indicating that LeaseRevoke was issued internally so it should be able to delete any attached keys.
This commit is contained in:
parent
3cbbb54927
commit
0c655902f2
@ -118,6 +118,8 @@ func (t *tokenSimple) genTokenPrefix() (string, error) {
|
||||
|
||||
func (t *tokenSimple) assignSimpleTokenToUser(username, token string) {
|
||||
t.simpleTokensMu.Lock()
|
||||
defer t.simpleTokensMu.Unlock()
|
||||
|
||||
_, ok := t.simpleTokens[token]
|
||||
if ok {
|
||||
plog.Panicf("token %s is alredy used", token)
|
||||
@ -125,7 +127,6 @@ func (t *tokenSimple) assignSimpleTokenToUser(username, token string) {
|
||||
|
||||
t.simpleTokens[token] = username
|
||||
t.simpleTokenKeeper.addSimpleToken(token)
|
||||
t.simpleTokensMu.Unlock()
|
||||
}
|
||||
|
||||
func (t *tokenSimple) invalidateUser(username string) {
|
||||
|
@ -162,6 +162,9 @@ type AuthStore interface {
|
||||
|
||||
// AuthInfoFromTLS gets AuthInfo from TLS info of gRPC's context
|
||||
AuthInfoFromTLS(ctx context.Context) *AuthInfo
|
||||
|
||||
// WithRoot generates and installs a token that can be used as a root credential
|
||||
WithRoot(ctx context.Context) context.Context
|
||||
}
|
||||
|
||||
type TokenProvider interface {
|
||||
@ -1057,3 +1060,35 @@ func NewTokenProvider(tokenOpts string, indexWaiter func(uint64) <-chan struct{}
|
||||
return nil, ErrInvalidAuthOpts
|
||||
}
|
||||
}
|
||||
|
||||
func (as *authStore) WithRoot(ctx context.Context) context.Context {
|
||||
if !as.isAuthEnabled() {
|
||||
return ctx
|
||||
}
|
||||
|
||||
var ctxForAssign context.Context
|
||||
if ts := as.tokenProvider.(*tokenSimple); ts != nil {
|
||||
ctx1 := context.WithValue(ctx, "index", uint64(0))
|
||||
prefix, err := ts.genTokenPrefix()
|
||||
if err != nil {
|
||||
plog.Errorf("failed to generate prefix of internally used token")
|
||||
return ctx
|
||||
}
|
||||
ctxForAssign = context.WithValue(ctx1, "simpleToken", prefix)
|
||||
} else {
|
||||
ctxForAssign = ctx
|
||||
}
|
||||
|
||||
token, err := as.tokenProvider.assign(ctxForAssign, "root", as.Revision())
|
||||
if err != nil {
|
||||
// this must not happen
|
||||
plog.Errorf("failed to assign token for lease revoking: %s", err)
|
||||
return ctx
|
||||
}
|
||||
|
||||
mdMap := map[string]string{
|
||||
"token": token,
|
||||
}
|
||||
tokenMD := metadata.New(mdMap)
|
||||
return metadata.NewContext(ctx, tokenMD)
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ func (s *EtcdServer) newApplierV3() applierV3 {
|
||||
return newAuthApplierV3(
|
||||
s.AuthStore(),
|
||||
newQuotaApplierV3(s, &applierV3backend{s}),
|
||||
s.lessor,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -19,12 +19,14 @@ import (
|
||||
|
||||
"github.com/coreos/etcd/auth"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
"github.com/coreos/etcd/lease"
|
||||
"github.com/coreos/etcd/mvcc"
|
||||
)
|
||||
|
||||
type authApplierV3 struct {
|
||||
applierV3
|
||||
as auth.AuthStore
|
||||
as auth.AuthStore
|
||||
lessor lease.Lessor
|
||||
|
||||
// mu serializes Apply so that user isn't corrupted and so that
|
||||
// serialized requests don't leak data from TOCTOU errors
|
||||
@ -33,8 +35,8 @@ type authApplierV3 struct {
|
||||
authInfo auth.AuthInfo
|
||||
}
|
||||
|
||||
func newAuthApplierV3(as auth.AuthStore, base applierV3) *authApplierV3 {
|
||||
return &authApplierV3{applierV3: base, as: as}
|
||||
func newAuthApplierV3(as auth.AuthStore, base applierV3, lessor lease.Lessor) *authApplierV3 {
|
||||
return &authApplierV3{applierV3: base, as: as, lessor: lessor}
|
||||
}
|
||||
|
||||
func (aa *authApplierV3) Apply(r *pb.InternalRaftRequest) *applyResult {
|
||||
@ -63,6 +65,15 @@ func (aa *authApplierV3) Put(txn mvcc.TxnWrite, r *pb.PutRequest) (*pb.PutRespon
|
||||
if err := aa.as.IsPutPermitted(&aa.authInfo, r.Key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := aa.checkLeasePuts(lease.LeaseID(r.Lease)); err != nil {
|
||||
// The specified lease is already attached with a key that cannot
|
||||
// be written by this user. It means the user cannot revoke the
|
||||
// lease so attaching the lease to the newly written key should
|
||||
// be forbidden.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.PrevKv {
|
||||
err := aa.as.IsRangePermitted(&aa.authInfo, r.Key, nil)
|
||||
if err != nil {
|
||||
@ -158,6 +169,26 @@ func (aa *authApplierV3) Txn(rt *pb.TxnRequest) (*pb.TxnResponse, error) {
|
||||
return aa.applierV3.Txn(rt)
|
||||
}
|
||||
|
||||
func (aa *authApplierV3) LeaseRevoke(lc *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error) {
|
||||
if err := aa.checkLeasePuts(lease.LeaseID(lc.ID)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return aa.applierV3.LeaseRevoke(lc)
|
||||
}
|
||||
|
||||
func (aa *authApplierV3) checkLeasePuts(leaseID lease.LeaseID) error {
|
||||
lease := aa.lessor.Lookup(leaseID)
|
||||
if lease != nil {
|
||||
for _, key := range lease.Keys() {
|
||||
if err := aa.as.IsPutPermitted(&aa.authInfo, []byte(key)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func needAdminPermission(r *pb.InternalRaftRequest) bool {
|
||||
switch {
|
||||
case r.AuthEnable != nil:
|
||||
|
@ -747,7 +747,8 @@ func (s *EtcdServer) run() {
|
||||
}
|
||||
lid := lease.ID
|
||||
s.goAttach(func() {
|
||||
s.LeaseRevoke(s.ctx, &pb.LeaseRevokeRequest{ID: int64(lid)})
|
||||
ctx := s.authStore.WithRoot(s.ctx)
|
||||
s.LeaseRevoke(ctx, &pb.LeaseRevokeRequest{ID: int64(lid)})
|
||||
<-c
|
||||
})
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user