Browse Source

Merge pull request #5615 from mitake/auth-v3-consistent-token

auth, etcdserver: make auth tokens consistent for all nodes
Hitoshi Mitake 9 years ago
parent
commit
2781553a9e

+ 3 - 1
Documentation/dev-guide/api_reference_v3.md

@@ -203,6 +203,7 @@ Empty field.
 | ----- | ----------- | ---- |
 | ----- | ----------- | ---- |
 | role |  | string |
 | role |  | string |
 | key |  | string |
 | key |  | string |
+| range_end |  | string |
 
 
 
 
 
 
@@ -755,8 +756,9 @@ Permission is a single entity
 
 
 | Field | Description | Type |
 | Field | Description | Type |
 | ----- | ----------- | ---- |
 | ----- | ----------- | ---- |
-| key |  | bytes |
 | permType |  | Type |
 | permType |  | Type |
+| key |  | bytes |
+| range_end |  | bytes |
 
 
 
 
 
 

+ 8 - 29
auth/simple_token.go

@@ -20,7 +20,6 @@ package auth
 import (
 import (
 	"crypto/rand"
 	"crypto/rand"
 	"math/big"
 	"math/big"
-	"sync"
 )
 )
 
 
 const (
 const (
@@ -28,16 +27,7 @@ const (
 	defaultSimpleTokenLength = 16
 	defaultSimpleTokenLength = 16
 )
 )
 
 
-var (
-	simpleTokensMu sync.RWMutex
-	simpleTokens   map[string]string // token -> username
-)
-
-func init() {
-	simpleTokens = make(map[string]string)
-}
-
-func genSimpleToken() (string, error) {
+func (as *authStore) GenSimpleToken() (string, error) {
 	ret := make([]byte, defaultSimpleTokenLength)
 	ret := make([]byte, defaultSimpleTokenLength)
 
 
 	for i := 0; i < defaultSimpleTokenLength; i++ {
 	for i := 0; i < defaultSimpleTokenLength; i++ {
@@ -52,25 +42,14 @@ func genSimpleToken() (string, error) {
 	return string(ret), nil
 	return string(ret), nil
 }
 }
 
 
-func genSimpleTokenForUser(username string) (string, error) {
-	var token string
-	var err error
-
-	for {
-		// generating random numbers in RSM would't a good idea
-		token, err = genSimpleToken()
-		if err != nil {
-			return "", err
-		}
+func (as *authStore) assignSimpleTokenToUser(username, token string) {
+	as.simpleTokensMu.Lock()
 
 
-		if _, ok := simpleTokens[token]; !ok {
-			break
-		}
+	_, ok := as.simpleTokens[token]
+	if ok {
+		plog.Panicf("token %s is alredy used", token)
 	}
 	}
 
 
-	simpleTokensMu.Lock()
-	simpleTokens[token] = username
-	simpleTokensMu.Unlock()
-
-	return token, nil
+	as.simpleTokens[token] = username
+	as.simpleTokensMu.Unlock()
 }
 }

+ 25 - 18
auth/store.go

@@ -17,6 +17,7 @@ package auth
 import (
 import (
 	"bytes"
 	"bytes"
 	"errors"
 	"errors"
+	"fmt"
 	"sort"
 	"sort"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
@@ -26,6 +27,7 @@ import (
 	"github.com/coreos/etcd/mvcc/backend"
 	"github.com/coreos/etcd/mvcc/backend"
 	"github.com/coreos/pkg/capnslog"
 	"github.com/coreos/pkg/capnslog"
 	"golang.org/x/crypto/bcrypt"
 	"golang.org/x/crypto/bcrypt"
+	"golang.org/x/net/context"
 )
 )
 
 
 var (
 var (
@@ -63,11 +65,8 @@ type AuthStore interface {
 	// AuthDisable turns off the authentication feature
 	// AuthDisable turns off the authentication feature
 	AuthDisable()
 	AuthDisable()
 
 
-	// Authenticate does authentication based on given user name and password,
-	// and returns a token for successful case.
-	// Note that the generated token is valid only for the member the client
-	// connected to within fixed time duration. Reauth is required after the duration.
-	Authenticate(name string, password string) (*pb.AuthenticateResponse, error)
+	// Authenticate does authentication based on given user name and password
+	Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error)
 
 
 	// Recover recovers the state of auth store from the given backend
 	// Recover recovers the state of auth store from the given backend
 	Recover(b backend.Backend)
 	Recover(b backend.Backend)
@@ -116,6 +115,9 @@ type AuthStore interface {
 
 
 	// IsAdminPermitted checks admin permission of the user
 	// IsAdminPermitted checks admin permission of the user
 	IsAdminPermitted(username string) bool
 	IsAdminPermitted(username string) bool
+
+	// GenSimpleToken produces a simple random string
+	GenSimpleToken() (string, error)
 }
 }
 
 
 type authStore struct {
 type authStore struct {
@@ -124,6 +126,9 @@ type authStore struct {
 	enabledMu sync.RWMutex
 	enabledMu sync.RWMutex
 
 
 	rangePermCache map[string]*unifiedRangePermissions // username -> unifiedRangePermissions
 	rangePermCache map[string]*unifiedRangePermissions // username -> unifiedRangePermissions
+
+	simpleTokensMu sync.RWMutex
+	simpleTokens   map[string]string // token -> username
 }
 }
 
 
 func (as *authStore) AuthEnable() error {
 func (as *authStore) AuthEnable() error {
@@ -172,28 +177,29 @@ func (as *authStore) AuthDisable() {
 	plog.Noticef("Authentication disabled")
 	plog.Noticef("Authentication disabled")
 }
 }
 
 
-func (as *authStore) Authenticate(name string, password string) (*pb.AuthenticateResponse, error) {
+func (as *authStore) Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error) {
+	// TODO(mitake): after adding jwt support, branching based on values of ctx is required
+	index := ctx.Value("index").(uint64)
+	simpleToken := ctx.Value("simpleToken").(string)
+
 	tx := as.be.BatchTx()
 	tx := as.be.BatchTx()
 	tx.Lock()
 	tx.Lock()
 	defer tx.Unlock()
 	defer tx.Unlock()
 
 
-	user := getUser(tx, name)
+	user := getUser(tx, username)
 	if user == nil {
 	if user == nil {
 		return nil, ErrAuthFailed
 		return nil, ErrAuthFailed
 	}
 	}
 
 
 	if bcrypt.CompareHashAndPassword(user.Password, []byte(password)) != nil {
 	if bcrypt.CompareHashAndPassword(user.Password, []byte(password)) != nil {
-		plog.Noticef("authentication failed, invalid password for user %s", name)
+		plog.Noticef("authentication failed, invalid password for user %s", username)
 		return &pb.AuthenticateResponse{}, ErrAuthFailed
 		return &pb.AuthenticateResponse{}, ErrAuthFailed
 	}
 	}
 
 
-	token, err := genSimpleTokenForUser(name)
-	if err != nil {
-		plog.Errorf("failed to generate simple token: %s", err)
-		return nil, err
-	}
+	token := fmt.Sprintf("%s.%d", simpleToken, index)
+	as.assignSimpleTokenToUser(username, token)
 
 
-	plog.Infof("authorized %s, token is %s", name, token)
+	plog.Infof("authorized %s, token is %s", username, token)
 	return &pb.AuthenticateResponse{Token: token}, nil
 	return &pb.AuthenticateResponse{Token: token}, nil
 }
 }
 
 
@@ -482,9 +488,9 @@ func (as *authStore) RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse,
 }
 }
 
 
 func (as *authStore) UsernameFromToken(token string) (string, bool) {
 func (as *authStore) UsernameFromToken(token string) (string, bool) {
-	simpleTokensMu.RLock()
-	defer simpleTokensMu.RUnlock()
-	t, ok := simpleTokens[token]
+	as.simpleTokensMu.RLock()
+	defer as.simpleTokensMu.RUnlock()
+	t, ok := as.simpleTokens[token]
 	return t, ok
 	return t, ok
 }
 }
 
 
@@ -680,7 +686,8 @@ func NewAuthStore(be backend.Backend) *authStore {
 	be.ForceCommit()
 	be.ForceCommit()
 
 
 	return &authStore{
 	return &authStore{
-		be: be,
+		be:           be,
+		simpleTokens: make(map[string]string),
 	}
 	}
 }
 }
 
 

+ 13 - 5
auth/store_test.go

@@ -20,6 +20,7 @@ import (
 
 
 	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
 	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
 	"github.com/coreos/etcd/mvcc/backend"
 	"github.com/coreos/etcd/mvcc/backend"
+	"golang.org/x/net/context"
 )
 )
 
 
 func TestUserAdd(t *testing.T) {
 func TestUserAdd(t *testing.T) {
@@ -60,7 +61,8 @@ func TestAuthenticate(t *testing.T) {
 	}
 	}
 
 
 	// auth a non-existing user
 	// auth a non-existing user
-	_, err = as.Authenticate("foo-test", "bar")
+	ctx1 := context.WithValue(context.WithValue(context.TODO(), "index", uint64(1)), "simpleToken", "dummy")
+	_, err = as.Authenticate(ctx1, "foo-test", "bar")
 	if err == nil {
 	if err == nil {
 		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
 		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
 	}
 	}
@@ -69,13 +71,15 @@ func TestAuthenticate(t *testing.T) {
 	}
 	}
 
 
 	// auth an existing user with correct password
 	// auth an existing user with correct password
-	_, err = as.Authenticate("foo", "bar")
+	ctx2 := context.WithValue(context.WithValue(context.TODO(), "index", uint64(2)), "simpleToken", "dummy")
+	_, err = as.Authenticate(ctx2, "foo", "bar")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
 	// auth an existing user but with wrong password
 	// auth an existing user but with wrong password
-	_, err = as.Authenticate("foo", "")
+	ctx3 := context.WithValue(context.WithValue(context.TODO(), "index", uint64(3)), "simpleToken", "dummy")
+	_, err = as.Authenticate(ctx3, "foo", "")
 	if err == nil {
 	if err == nil {
 		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
 		t.Fatalf("expected %v, got %v", ErrAuthFailed, err)
 	}
 	}
@@ -129,7 +133,9 @@ func TestUserChangePassword(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	_, err = as.Authenticate("foo", "")
+
+	ctx1 := context.WithValue(context.WithValue(context.TODO(), "index", uint64(1)), "simpleToken", "dummy")
+	_, err = as.Authenticate(ctx1, "foo", "")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -138,7 +144,9 @@ func TestUserChangePassword(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	_, err = as.Authenticate("foo", "bar")
+
+	ctx2 := context.WithValue(context.WithValue(context.TODO(), "index", uint64(2)), "simpleToken", "dummy")
+	_, err = as.Authenticate(ctx2, "foo", "bar")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 6 - 4
etcdserver/apply.go

@@ -26,6 +26,7 @@ import (
 	"github.com/coreos/etcd/mvcc/mvccpb"
 	"github.com/coreos/etcd/mvcc/mvccpb"
 	"github.com/coreos/etcd/pkg/types"
 	"github.com/coreos/etcd/pkg/types"
 	"github.com/gogo/protobuf/proto"
 	"github.com/gogo/protobuf/proto"
+	"golang.org/x/net/context"
 )
 )
 
 
 const (
 const (
@@ -59,7 +60,7 @@ type applierV3 interface {
 
 
 	AuthEnable() (*pb.AuthEnableResponse, error)
 	AuthEnable() (*pb.AuthEnableResponse, error)
 	AuthDisable() (*pb.AuthDisableResponse, error)
 	AuthDisable() (*pb.AuthDisableResponse, error)
-	Authenticate(r *pb.AuthenticateRequest) (*pb.AuthenticateResponse, error)
+	Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error)
 	UserAdd(ua *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error)
 	UserAdd(ua *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error)
 	UserDelete(ua *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error)
 	UserDelete(ua *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error)
 	UserChangePassword(ua *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error)
 	UserChangePassword(ua *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error)
@@ -117,7 +118,8 @@ func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) *applyResult {
 	case r.AuthDisable != nil:
 	case r.AuthDisable != nil:
 		ar.resp, ar.err = s.applyV3.AuthDisable()
 		ar.resp, ar.err = s.applyV3.AuthDisable()
 	case r.Authenticate != nil:
 	case r.Authenticate != nil:
-		ar.resp, ar.err = s.applyV3.Authenticate(r.Authenticate)
+		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)
 	case r.AuthUserAdd != nil:
 	case r.AuthUserAdd != nil:
 		ar.resp, ar.err = s.applyV3.UserAdd(r.AuthUserAdd)
 		ar.resp, ar.err = s.applyV3.UserAdd(r.AuthUserAdd)
 	case r.AuthUserDelete != nil:
 	case r.AuthUserDelete != nil:
@@ -541,8 +543,8 @@ func (a *applierV3backend) AuthDisable() (*pb.AuthDisableResponse, error) {
 	return &pb.AuthDisableResponse{}, nil
 	return &pb.AuthDisableResponse{}, nil
 }
 }
 
 
-func (a *applierV3backend) Authenticate(r *pb.AuthenticateRequest) (*pb.AuthenticateResponse, error) {
-	return a.s.AuthStore().Authenticate(r.Name, r.Password)
+func (a *applierV3backend) Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error) {
+	return a.s.AuthStore().Authenticate(ctx, username, password)
 }
 }
 
 
 func (a *applierV3backend) UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error) {
 func (a *applierV3backend) UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error) {

+ 27 - 26
etcdserver/etcdserverpb/etcdserver.pb.go

@@ -16,6 +16,7 @@
 		RequestHeader
 		RequestHeader
 		InternalRaftRequest
 		InternalRaftRequest
 		EmptyResponse
 		EmptyResponse
+		InternalAuthenticateRequest
 		ResponseHeader
 		ResponseHeader
 		RangeRequest
 		RangeRequest
 		RangeResponse
 		RangeResponse
@@ -1006,30 +1007,30 @@ var (
 )
 )
 
 
 var fileDescriptorEtcdserver = []byte{
 var fileDescriptorEtcdserver = []byte{
-	// 398 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x5c, 0x92, 0xd1, 0x6e, 0xd3, 0x30,
-	0x14, 0x86, 0xe7, 0xd6, 0x4d, 0x1b, 0x33, 0xa0, 0x58, 0x13, 0x3a, 0x9a, 0x50, 0xa8, 0x2a, 0x2e,
-	0x7a, 0x05, 0xef, 0x30, 0xba, 0x8b, 0x4a, 0x0c, 0x8d, 0x0e, 0x8d, 0x6b, 0xd3, 0x1c, 0x56, 0x4b,
-	0x49, 0x9c, 0xd9, 0x27, 0xa1, 0x6f, 0xc0, 0xab, 0xf5, 0x92, 0x27, 0x40, 0xd0, 0x27, 0x41, 0x76,
-	0x96, 0x62, 0x76, 0x67, 0x7d, 0xff, 0xef, 0xdf, 0xbf, 0xed, 0x23, 0xa6, 0x48, 0x9b, 0xdc, 0xa1,
-	0x6d, 0xd1, 0xbe, 0xad, 0xad, 0x21, 0x23, 0x4f, 0xff, 0x91, 0xfa, 0xeb, 0xf9, 0xd9, 0x9d, 0xb9,
-	0x33, 0x41, 0x78, 0xe7, 0x57, 0x9d, 0x67, 0xfe, 0x83, 0x8b, 0xf1, 0x1a, 0xef, 0x1b, 0x74, 0x24,
-	0xcf, 0xc4, 0x60, 0xb5, 0x04, 0x36, 0x63, 0x0b, 0x7e, 0xc1, 0xf7, 0xbf, 0x5e, 0x9f, 0xac, 0x07,
-	0x7a, 0x29, 0x5f, 0x89, 0xe4, 0x0a, 0x69, 0x6b, 0x72, 0x18, 0xcc, 0xd8, 0x22, 0x7d, 0x50, 0x92,
-	0x32, 0x30, 0x09, 0x82, 0x5f, 0x2b, 0xda, 0xc2, 0x30, 0xd2, 0x78, 0xad, 0x68, 0x2b, 0x5f, 0x8a,
-	0xe1, 0xad, 0x2a, 0x80, 0x47, 0xc2, 0xb0, 0x55, 0x85, 0xe7, 0x4b, 0x6d, 0x61, 0x34, 0x63, 0x8b,
-	0x49, 0xcf, 0x73, 0x6d, 0xe5, 0x5c, 0xa4, 0xd7, 0x16, 0xdb, 0x5b, 0x55, 0x34, 0x08, 0x49, 0xb4,
-	0x2b, 0xad, 0x7b, 0xdc, 0x7b, 0x56, 0x55, 0x8e, 0x3b, 0x18, 0x47, 0x45, 0x83, 0x27, 0xe0, 0xde,
-	0x73, 0xb9, 0xd3, 0x8e, 0x60, 0x72, 0x3c, 0x85, 0x75, 0x9e, 0x80, 0xe5, 0x1b, 0x21, 0x2e, 0x77,
-	0xb5, 0xb6, 0x8a, 0xb4, 0xa9, 0x20, 0x9d, 0xb1, 0xc5, 0xf0, 0x21, 0x48, 0xe0, 0x91, 0xfb, 0xbb,
-	0x7d, 0x51, 0x9a, 0x40, 0x44, 0x55, 0xf9, 0x77, 0xa5, 0x49, 0x9e, 0x8b, 0xd1, 0x8d, 0xae, 0x36,
-	0x08, 0x4f, 0xa2, 0x0e, 0x23, 0xe7, 0x91, 0x3f, 0x7f, 0x8d, 0x9b, 0xc6, 0x3a, 0xdd, 0x22, 0x9c,
-	0x46, 0x5b, 0x53, 0xdb, 0x63, 0xff, 0xa6, 0x37, 0xc6, 0x12, 0xe6, 0xf0, 0x34, 0x32, 0x24, 0x2e,
-	0x30, 0xaf, 0x7e, 0x6a, 0x8c, 0x6d, 0x4a, 0x78, 0x16, 0xab, 0xf7, 0x81, 0xf9, 0x56, 0x9f, 0x75,
-	0x89, 0xf0, 0x3c, 0x6a, 0xcd, 0x49, 0x97, 0x5d, 0x2a, 0x59, 0x54, 0x25, 0x4c, 0xff, 0x4b, 0x0d,
-	0x4c, 0x66, 0xfe, 0xa3, 0xbf, 0x59, 0x74, 0x5b, 0x78, 0x11, 0xbd, 0xca, 0xd8, 0x76, 0x70, 0xfe,
-	0x41, 0x4c, 0xae, 0x90, 0x54, 0xae, 0x48, 0xf9, 0xa4, 0x8f, 0x26, 0xc7, 0x47, 0xd3, 0x90, 0x54,
-	0x81, 0xf9, 0x1b, 0xbe, 0x2f, 0x1a, 0x47, 0x68, 0x57, 0xcb, 0x30, 0x14, 0xc7, 0x5f, 0xd8, 0xf4,
-	0xf8, 0x62, 0xba, 0xff, 0x93, 0x9d, 0xec, 0x0f, 0x19, 0xfb, 0x79, 0xc8, 0xd8, 0xef, 0x43, 0xc6,
-	0xfe, 0x06, 0x00, 0x00, 0xff, 0xff, 0xfb, 0x8e, 0x1a, 0x0d, 0xa0, 0x02, 0x00, 0x00,
+	// 388 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x5c, 0x92, 0xd1, 0xee, 0xd2, 0x30,
+	0x14, 0xc6, 0xff, 0xdb, 0xca, 0x60, 0x15, 0x15, 0x1b, 0x62, 0x4e, 0x88, 0x41, 0x43, 0xbc, 0xf0,
+	0x4a, 0xdf, 0x01, 0xe1, 0x82, 0x44, 0x0d, 0x82, 0xd1, 0xeb, 0xba, 0x1d, 0xa1, 0x09, 0x5b, 0x47,
+	0xdb, 0x4d, 0xde, 0xc0, 0x57, 0xe3, 0xd2, 0x27, 0x30, 0xea, 0x93, 0xd8, 0x16, 0x86, 0xd5, 0x8b,
+	0x26, 0xcb, 0xef, 0xfb, 0xce, 0xe9, 0xd7, 0x73, 0x46, 0x47, 0x68, 0xf2, 0x42, 0xa3, 0x6a, 0x51,
+	0xbd, 0xac, 0x95, 0x34, 0x92, 0x0d, 0xff, 0x92, 0xfa, 0xf3, 0x64, 0xbc, 0x93, 0x3b, 0xe9, 0x85,
+	0x57, 0xee, 0xeb, 0xe2, 0x99, 0x7d, 0x23, 0xb4, 0xbf, 0xc1, 0x63, 0x83, 0xda, 0xb0, 0x31, 0x8d,
+	0x57, 0x0b, 0x88, 0x9e, 0x45, 0x2f, 0xc8, 0x9c, 0x9c, 0x7f, 0x3c, 0xbd, 0xdb, 0xc4, 0x62, 0xc1,
+	0x9e, 0xd0, 0xf4, 0x2d, 0x9a, 0xbd, 0x2c, 0x20, 0xb6, 0x4a, 0x76, 0x55, 0xd2, 0xd2, 0x33, 0x06,
+	0x94, 0xac, 0xb9, 0xd9, 0x43, 0x12, 0x68, 0xa4, 0xb6, 0x84, 0x3d, 0xa6, 0xc9, 0x47, 0x7e, 0x00,
+	0x12, 0x08, 0x49, 0xcb, 0x0f, 0x8e, 0x2f, 0x84, 0x82, 0x9e, 0xe5, 0x83, 0x8e, 0x17, 0x42, 0xb1,
+	0x19, 0xcd, 0xd6, 0x0a, 0x5b, 0x5b, 0xd3, 0x20, 0xa4, 0x41, 0x55, 0x56, 0x77, 0xb8, 0xf3, 0xac,
+	0xaa, 0x02, 0x4f, 0xd0, 0x0f, 0x82, 0x7a, 0x8f, 0xc7, 0x9d, 0x67, 0x79, 0x12, 0xda, 0xc0, 0xe0,
+	0x76, 0x4b, 0x74, 0xf1, 0x78, 0xcc, 0x9e, 0x53, 0xba, 0x3c, 0xd5, 0x42, 0x71, 0x23, 0x64, 0x05,
+	0x99, 0x35, 0x25, 0xd7, 0x46, 0x14, 0x6f, 0xdc, 0xbd, 0xed, 0x13, 0x17, 0x06, 0x68, 0x10, 0x95,
+	0x7c, 0xb5, 0x84, 0x4d, 0x68, 0x6f, 0x2b, 0xaa, 0x1c, 0xe1, 0x5e, 0x90, 0xa1, 0xa7, 0x1d, 0x72,
+	0xf7, 0x6f, 0x30, 0x6f, 0x94, 0x16, 0x2d, 0xc2, 0x30, 0x28, 0xcd, 0x54, 0x87, 0xdd, 0x4c, 0xb7,
+	0x52, 0x19, 0x2c, 0xe0, 0x7e, 0x60, 0x48, 0xb5, 0x67, 0x4e, 0x7d, 0xdf, 0x48, 0xd5, 0x94, 0xf0,
+	0x20, 0x54, 0x8f, 0x9e, 0xb9, 0x54, 0x1f, 0x44, 0x89, 0xf0, 0x30, 0x48, 0x4d, 0x8c, 0x25, 0xbe,
+	0xab, 0x51, 0xc8, 0x4b, 0x18, 0xfd, 0xd3, 0xd5, 0x33, 0x36, 0x75, 0x8b, 0xfe, 0xa2, 0x50, 0xef,
+	0xe1, 0x51, 0x30, 0x95, 0xbe, 0xba, 0xc0, 0xd9, 0x1b, 0x3a, 0xb0, 0x7b, 0xe6, 0x05, 0x37, 0xdc,
+	0x75, 0x7a, 0x27, 0x0b, 0xfc, 0xef, 0x6f, 0x48, 0x2b, 0xcf, 0xdc, 0x0b, 0x5f, 0x1f, 0x1a, 0x6d,
+	0x50, 0x59, 0x43, 0x1c, 0x6e, 0x21, 0xef, 0xf0, 0x7c, 0x74, 0xfe, 0x35, 0xbd, 0x3b, 0xff, 0x9e,
+	0x46, 0xdf, 0xed, 0xf9, 0x69, 0xcf, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfb, 0x8e, 0x1a, 0x0d,
+	0xa0, 0x02, 0x00, 0x00,
 }
 }

+ 261 - 49
etcdserver/etcdserverpb/raft_internal.pb.go

@@ -46,7 +46,7 @@ type InternalRaftRequest struct {
 	Alarm                    *AlarmRequest                    `protobuf:"bytes,10,opt,name=alarm" json:"alarm,omitempty"`
 	Alarm                    *AlarmRequest                    `protobuf:"bytes,10,opt,name=alarm" json:"alarm,omitempty"`
 	AuthEnable               *AuthEnableRequest               `protobuf:"bytes,1000,opt,name=auth_enable,json=authEnable" json:"auth_enable,omitempty"`
 	AuthEnable               *AuthEnableRequest               `protobuf:"bytes,1000,opt,name=auth_enable,json=authEnable" json:"auth_enable,omitempty"`
 	AuthDisable              *AuthDisableRequest              `protobuf:"bytes,1011,opt,name=auth_disable,json=authDisable" json:"auth_disable,omitempty"`
 	AuthDisable              *AuthDisableRequest              `protobuf:"bytes,1011,opt,name=auth_disable,json=authDisable" json:"auth_disable,omitempty"`
-	Authenticate             *AuthenticateRequest             `protobuf:"bytes,1012,opt,name=authenticate" json:"authenticate,omitempty"`
+	Authenticate             *InternalAuthenticateRequest     `protobuf:"bytes,1012,opt,name=authenticate" json:"authenticate,omitempty"`
 	AuthUserAdd              *AuthUserAddRequest              `protobuf:"bytes,1100,opt,name=auth_user_add,json=authUserAdd" json:"auth_user_add,omitempty"`
 	AuthUserAdd              *AuthUserAddRequest              `protobuf:"bytes,1100,opt,name=auth_user_add,json=authUserAdd" json:"auth_user_add,omitempty"`
 	AuthUserDelete           *AuthUserDeleteRequest           `protobuf:"bytes,1101,opt,name=auth_user_delete,json=authUserDelete" json:"auth_user_delete,omitempty"`
 	AuthUserDelete           *AuthUserDeleteRequest           `protobuf:"bytes,1101,opt,name=auth_user_delete,json=authUserDelete" json:"auth_user_delete,omitempty"`
 	AuthUserGet              *AuthUserGetRequest              `protobuf:"bytes,1102,opt,name=auth_user_get,json=authUserGet" json:"auth_user_get,omitempty"`
 	AuthUserGet              *AuthUserGetRequest              `protobuf:"bytes,1102,opt,name=auth_user_get,json=authUserGet" json:"auth_user_get,omitempty"`
@@ -73,10 +73,28 @@ func (m *EmptyResponse) String() string            { return proto.CompactTextStr
 func (*EmptyResponse) ProtoMessage()               {}
 func (*EmptyResponse) ProtoMessage()               {}
 func (*EmptyResponse) Descriptor() ([]byte, []int) { return fileDescriptorRaftInternal, []int{2} }
 func (*EmptyResponse) Descriptor() ([]byte, []int) { return fileDescriptorRaftInternal, []int{2} }
 
 
+// What is the difference between AuthenticateRequest (defined in rpc.proto) and InternalAuthenticateRequest?
+// InternalAuthenticateRequest has a member that is filled by etcdserver and shouldn't be user-facing.
+// For avoiding misusage the field, we have an internal version of AuthenticateRequest.
+type InternalAuthenticateRequest struct {
+	Name     string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
+	// simple_token is generated in API layer (etcdserver/v3_server.go)
+	SimpleToken string `protobuf:"bytes,3,opt,name=simple_token,json=simpleToken,proto3" json:"simple_token,omitempty"`
+}
+
+func (m *InternalAuthenticateRequest) Reset()         { *m = InternalAuthenticateRequest{} }
+func (m *InternalAuthenticateRequest) String() string { return proto.CompactTextString(m) }
+func (*InternalAuthenticateRequest) ProtoMessage()    {}
+func (*InternalAuthenticateRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptorRaftInternal, []int{3}
+}
+
 func init() {
 func init() {
 	proto.RegisterType((*RequestHeader)(nil), "etcdserverpb.RequestHeader")
 	proto.RegisterType((*RequestHeader)(nil), "etcdserverpb.RequestHeader")
 	proto.RegisterType((*InternalRaftRequest)(nil), "etcdserverpb.InternalRaftRequest")
 	proto.RegisterType((*InternalRaftRequest)(nil), "etcdserverpb.InternalRaftRequest")
 	proto.RegisterType((*EmptyResponse)(nil), "etcdserverpb.EmptyResponse")
 	proto.RegisterType((*EmptyResponse)(nil), "etcdserverpb.EmptyResponse")
+	proto.RegisterType((*InternalAuthenticateRequest)(nil), "etcdserverpb.InternalAuthenticateRequest")
 }
 }
 func (m *RequestHeader) Marshal() (data []byte, err error) {
 func (m *RequestHeader) Marshal() (data []byte, err error) {
 	size := m.Size()
 	size := m.Size()
@@ -418,6 +436,42 @@ func (m *EmptyResponse) MarshalTo(data []byte) (int, error) {
 	return i, nil
 	return i, nil
 }
 }
 
 
+func (m *InternalAuthenticateRequest) Marshal() (data []byte, err error) {
+	size := m.Size()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *InternalAuthenticateRequest) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Name) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintRaftInternal(data, i, uint64(len(m.Name)))
+		i += copy(data[i:], m.Name)
+	}
+	if len(m.Password) > 0 {
+		data[i] = 0x12
+		i++
+		i = encodeVarintRaftInternal(data, i, uint64(len(m.Password)))
+		i += copy(data[i:], m.Password)
+	}
+	if len(m.SimpleToken) > 0 {
+		data[i] = 0x1a
+		i++
+		i = encodeVarintRaftInternal(data, i, uint64(len(m.SimpleToken)))
+		i += copy(data[i:], m.SimpleToken)
+	}
+	return i, nil
+}
+
 func encodeFixed64RaftInternal(data []byte, offset int, v uint64) int {
 func encodeFixed64RaftInternal(data []byte, offset int, v uint64) int {
 	data[offset] = uint8(v)
 	data[offset] = uint8(v)
 	data[offset+1] = uint8(v >> 8)
 	data[offset+1] = uint8(v >> 8)
@@ -569,6 +623,24 @@ func (m *EmptyResponse) Size() (n int) {
 	return n
 	return n
 }
 }
 
 
+func (m *InternalAuthenticateRequest) Size() (n int) {
+	var l int
+	_ = l
+	l = len(m.Name)
+	if l > 0 {
+		n += 1 + l + sovRaftInternal(uint64(l))
+	}
+	l = len(m.Password)
+	if l > 0 {
+		n += 1 + l + sovRaftInternal(uint64(l))
+	}
+	l = len(m.SimpleToken)
+	if l > 0 {
+		n += 1 + l + sovRaftInternal(uint64(l))
+	}
+	return n
+}
+
 func sovRaftInternal(x uint64) (n int) {
 func sovRaftInternal(x uint64) (n int) {
 	for {
 	for {
 		n++
 		n++
@@ -1151,7 +1223,7 @@ func (m *InternalRaftRequest) Unmarshal(data []byte) error {
 				return io.ErrUnexpectedEOF
 				return io.ErrUnexpectedEOF
 			}
 			}
 			if m.Authenticate == nil {
 			if m.Authenticate == nil {
-				m.Authenticate = &AuthenticateRequest{}
+				m.Authenticate = &InternalAuthenticateRequest{}
 			}
 			}
 			if err := m.Authenticate.Unmarshal(data[iNdEx:postIndex]); err != nil {
 			if err := m.Authenticate.Unmarshal(data[iNdEx:postIndex]); err != nil {
 				return err
 				return err
@@ -1591,6 +1663,143 @@ func (m *EmptyResponse) Unmarshal(data []byte) error {
 	}
 	}
 	return nil
 	return nil
 }
 }
+func (m *InternalAuthenticateRequest) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowRaftInternal
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: InternalAuthenticateRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: InternalAuthenticateRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowRaftInternal
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthRaftInternal
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Name = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowRaftInternal
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthRaftInternal
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Password = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field SimpleToken", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowRaftInternal
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				stringLen |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthRaftInternal
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.SimpleToken = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipRaftInternal(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthRaftInternal
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func skipRaftInternal(data []byte) (n int, err error) {
 func skipRaftInternal(data []byte) (n int, err error) {
 	l := len(data)
 	l := len(data)
 	iNdEx := 0
 	iNdEx := 0
@@ -1697,51 +1906,54 @@ var (
 )
 )
 
 
 var fileDescriptorRaftInternal = []byte{
 var fileDescriptorRaftInternal = []byte{
-	// 725 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x7c, 0x95, 0x5b, 0x4f, 0x13, 0x41,
-	0x14, 0xc7, 0x6d, 0xb9, 0x76, 0x4a, 0x11, 0x07, 0xd0, 0xb1, 0x24, 0x15, 0x31, 0x2a, 0xf1, 0x82,
-	0x06, 0x1e, 0x7d, 0xd0, 0x4a, 0x11, 0x49, 0x8c, 0x21, 0x1b, 0x4d, 0x4c, 0x7c, 0xd8, 0x0c, 0xdd,
-	0x43, 0x5b, 0xdd, 0xee, 0xae, 0xb3, 0xd3, 0x8a, 0xdf, 0xca, 0xdb, 0x87, 0xe0, 0xc1, 0x0b, 0xfa,
-	0x09, 0xb4, 0x4f, 0xbe, 0xeb, 0x07, 0x30, 0x73, 0xd9, 0xd9, 0x6e, 0x3b, 0xcb, 0xdb, 0x72, 0xce,
-	0xff, 0xfc, 0xce, 0x39, 0xf3, 0x9f, 0xa1, 0x68, 0x91, 0xd1, 0x43, 0xee, 0x76, 0x02, 0x0e, 0x2c,
-	0xa0, 0xfe, 0x46, 0xc4, 0x42, 0x1e, 0xe2, 0x39, 0xe0, 0x4d, 0x2f, 0x06, 0xd6, 0x07, 0x16, 0x1d,
-	0x54, 0x97, 0x5a, 0x61, 0x2b, 0x94, 0x89, 0x3b, 0xe2, 0x4b, 0x69, 0xaa, 0x0b, 0xa9, 0x46, 0x47,
-	0x4a, 0x2c, 0x6a, 0xaa, 0xcf, 0xb5, 0x7b, 0xa8, 0xe2, 0xc0, 0x9b, 0x1e, 0xc4, 0xfc, 0x31, 0x50,
-	0x0f, 0x18, 0x9e, 0x47, 0xc5, 0xbd, 0x06, 0x29, 0xac, 0x16, 0xd6, 0x27, 0x9d, 0x62, 0xa7, 0x81,
-	0xab, 0x68, 0xb6, 0x17, 0x8b, 0x96, 0x5d, 0x20, 0xc5, 0xd5, 0xc2, 0x7a, 0xc9, 0x31, 0x7f, 0xaf,
-	0xfd, 0xac, 0xa0, 0xc5, 0x3d, 0x3d, 0x90, 0x43, 0x0f, 0xb9, 0x26, 0x8d, 0x31, 0xae, 0xa2, 0x62,
-	0x7f, 0x53, 0x56, 0x97, 0x37, 0x97, 0x37, 0x86, 0x47, 0xde, 0xd0, 0x25, 0x4e, 0xb1, 0xbf, 0x89,
-	0xef, 0xa2, 0x29, 0x46, 0x83, 0x16, 0x90, 0x09, 0xa9, 0xac, 0x8e, 0x28, 0x45, 0x2a, 0x91, 0x2b,
-	0x21, 0xbe, 0x81, 0x26, 0xa2, 0x1e, 0x27, 0x93, 0x52, 0x4f, 0xb2, 0xfa, 0xfd, 0x5e, 0x32, 0x8f,
-	0x23, 0x44, 0x78, 0x1b, 0xcd, 0x79, 0xe0, 0x03, 0x07, 0x57, 0x35, 0x99, 0x92, 0x45, 0xab, 0xd9,
-	0xa2, 0x86, 0x54, 0x64, 0x5a, 0x95, 0xbd, 0x34, 0x26, 0x1a, 0xf2, 0xa3, 0x80, 0x4c, 0xdb, 0x1a,
-	0x3e, 0x3b, 0x0a, 0x4c, 0x43, 0x7e, 0x14, 0xe0, 0xfb, 0x08, 0x35, 0xc3, 0x6e, 0x44, 0x9b, 0xbc,
-	0x13, 0x06, 0x64, 0x46, 0x96, 0x5c, 0xca, 0x96, 0x6c, 0x9b, 0x7c, 0x52, 0x39, 0x54, 0x82, 0x1f,
-	0xa0, 0xb2, 0x0f, 0x34, 0x06, 0xb7, 0xc5, 0x68, 0xc0, 0xc9, 0xac, 0x8d, 0xf0, 0x44, 0x08, 0x76,
-	0x45, 0xde, 0x10, 0x7c, 0x13, 0x12, 0x3b, 0x2b, 0x02, 0x83, 0x7e, 0xf8, 0x1a, 0x48, 0xc9, 0xb6,
-	0xb3, 0x44, 0x38, 0x52, 0x60, 0x76, 0xf6, 0xd3, 0x98, 0xb0, 0x85, 0xfa, 0x94, 0x75, 0x09, 0xb2,
-	0xd9, 0x52, 0x17, 0x29, 0x63, 0x8b, 0x14, 0xe2, 0x2d, 0x34, 0xdd, 0x96, 0xb7, 0x89, 0x78, 0xb2,
-	0x64, 0xc5, 0xea, 0xb9, 0xba, 0x70, 0x8e, 0x96, 0xe2, 0x3a, 0x2a, 0xd3, 0x1e, 0x6f, 0xbb, 0x10,
-	0xd0, 0x03, 0x1f, 0xc8, 0x1f, 0xeb, 0x81, 0xd5, 0x7b, 0xbc, 0xbd, 0x23, 0x05, 0x66, 0x5d, 0x6a,
-	0x42, 0xb8, 0x81, 0xe6, 0x24, 0xc2, 0xeb, 0xc4, 0x92, 0xf1, 0x77, 0xc6, 0xb6, 0xaf, 0x60, 0x34,
-	0x94, 0xc2, 0xec, 0x4b, 0xd3, 0x18, 0x7e, 0xa4, 0x28, 0x10, 0xf0, 0x4e, 0x93, 0x72, 0x20, 0xff,
-	0x14, 0xe5, 0xf2, 0x38, 0x25, 0x91, 0x24, 0x98, 0x4c, 0x1d, 0xde, 0x41, 0x15, 0x39, 0x8d, 0x78,
-	0x2e, 0x2e, 0xf5, 0x3c, 0xf2, 0x65, 0x36, 0x6f, 0x9c, 0xe7, 0x31, 0xb0, 0xba, 0xe7, 0x65, 0xc6,
-	0xd1, 0x31, 0xfc, 0x14, 0x2d, 0xa4, 0x18, 0x75, 0x17, 0xc9, 0x57, 0x45, 0xba, 0x62, 0x27, 0xe9,
-	0x4b, 0xac, 0x61, 0xf3, 0x34, 0x13, 0xce, 0x8e, 0xd5, 0x02, 0x4e, 0xbe, 0x9d, 0x3a, 0xd6, 0x2e,
-	0xf0, 0xb1, 0xb1, 0x76, 0x81, 0xe3, 0x16, 0xba, 0x98, 0x62, 0x9a, 0x6d, 0xf1, 0x3a, 0xdc, 0x88,
-	0xc6, 0xf1, 0xdb, 0x90, 0x79, 0xe4, 0xbb, 0x42, 0xde, 0xb4, 0x23, 0xb7, 0xa5, 0x7a, 0x5f, 0x8b,
-	0x13, 0xfa, 0x79, 0x6a, 0x4d, 0xe3, 0x17, 0x68, 0x69, 0x68, 0x5e, 0x71, 0xad, 0x5d, 0x16, 0xfa,
-	0x40, 0x4e, 0x54, 0x8f, 0x6b, 0x39, 0x63, 0xcb, 0x27, 0x11, 0xa6, 0x16, 0x9f, 0xa3, 0xa3, 0x19,
-	0xfc, 0x12, 0x2d, 0xa7, 0x64, 0xf5, 0x42, 0x14, 0xfa, 0x87, 0x42, 0x5f, 0xb7, 0xa3, 0xf5, 0x53,
-	0x19, 0x62, 0x63, 0x3a, 0x96, 0x32, 0xc7, 0x2c, 0x80, 0xd2, 0xfd, 0xf7, 0xa5, 0xbc, 0x63, 0x16,
-	0xfa, 0x51, 0xf7, 0x75, 0xcc, 0xb8, 0x2f, 0x31, 0xda, 0xfd, 0x0f, 0xa5, 0x3c, 0xf7, 0x45, 0x95,
-	0xc5, 0xfd, 0x34, 0x9c, 0x1d, 0x4b, 0xb8, 0xff, 0xf1, 0xd4, 0xb1, 0x46, 0xdd, 0xd7, 0x31, 0xfc,
-	0x0a, 0x55, 0x87, 0x30, 0xd2, 0x94, 0x08, 0x58, 0xb7, 0x13, 0xc7, 0xe2, 0x7f, 0xdd, 0x27, 0xc5,
-	0xbc, 0x95, 0xc3, 0x14, 0xf2, 0x7d, 0xa3, 0x4e, 0xf8, 0x17, 0xa8, 0x3d, 0x8f, 0xbb, 0x68, 0x25,
-	0xed, 0xa5, 0x6d, 0x1a, 0x6a, 0xf6, 0x59, 0x35, 0xbb, 0x6d, 0x6f, 0xa6, 0x1c, 0x19, 0xef, 0x46,
-	0x68, 0x8e, 0x60, 0xed, 0x2c, 0xaa, 0xec, 0x74, 0x23, 0xfe, 0xce, 0x81, 0x38, 0x0a, 0x83, 0x18,
-	0x1e, 0x2e, 0x1c, 0xff, 0xae, 0x9d, 0x39, 0x1e, 0xd4, 0x0a, 0x27, 0x83, 0x5a, 0xe1, 0xd7, 0xa0,
-	0x56, 0x38, 0x98, 0x96, 0xbf, 0x9d, 0x5b, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0xac, 0x8d, 0xf6,
-	0xc5, 0x93, 0x07, 0x00, 0x00,
+	// 770 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x7c, 0x95, 0x4b, 0x6f, 0x13, 0x3b,
+	0x14, 0xc7, 0x6f, 0xd2, 0x67, 0x9c, 0xa4, 0xb7, 0xd7, 0x6d, 0x2f, 0x26, 0x95, 0x4a, 0x09, 0xe2,
+	0x0d, 0x05, 0xb5, 0x4b, 0x16, 0x10, 0x9a, 0xaa, 0x54, 0x42, 0xa8, 0x1a, 0x15, 0x09, 0x89, 0xc5,
+	0xc8, 0xcd, 0x98, 0x74, 0x60, 0x5e, 0x78, 0x9c, 0x52, 0xbe, 0x15, 0xaf, 0x0f, 0xd1, 0x05, 0x8f,
+	0xb2, 0x65, 0x05, 0xac, 0xd8, 0xc3, 0x07, 0xc0, 0xaf, 0xf1, 0xcc, 0x24, 0x4e, 0x17, 0x91, 0x66,
+	0xce, 0xf9, 0x9f, 0xdf, 0x39, 0x9e, 0xbf, 0x1d, 0x83, 0x05, 0x8a, 0x9f, 0x31, 0xd7, 0x8f, 0x18,
+	0xa1, 0x11, 0x0e, 0xd6, 0x12, 0x1a, 0xb3, 0x18, 0x36, 0x08, 0xeb, 0x79, 0x29, 0xa1, 0x87, 0x84,
+	0x26, 0xfb, 0xad, 0xc5, 0x7e, 0xdc, 0x8f, 0x65, 0xe2, 0x96, 0x78, 0x52, 0x9a, 0xd6, 0x7c, 0xae,
+	0xd1, 0x91, 0x1a, 0x4d, 0x7a, 0xea, 0xb1, 0x7d, 0x07, 0x34, 0x1d, 0xf2, 0x72, 0x40, 0x52, 0xf6,
+	0x80, 0x60, 0x8f, 0x50, 0x38, 0x07, 0xaa, 0x3b, 0x5d, 0x54, 0x59, 0xad, 0x5c, 0x99, 0x74, 0xaa,
+	0x7e, 0x17, 0xb6, 0xc0, 0xec, 0x20, 0x15, 0x2d, 0x43, 0x82, 0xaa, 0x3c, 0x5a, 0x73, 0xcc, 0x7b,
+	0xfb, 0x5b, 0x13, 0x2c, 0xec, 0xe8, 0x81, 0x1c, 0x3e, 0x9d, 0x26, 0x8d, 0x30, 0x2e, 0x82, 0xea,
+	0xe1, 0xba, 0xac, 0xae, 0xaf, 0x2f, 0xad, 0x15, 0x47, 0x5e, 0xd3, 0x25, 0x0e, 0x17, 0xc0, 0xdb,
+	0x60, 0x8a, 0xe2, 0xa8, 0x4f, 0xd0, 0x84, 0x54, 0xb6, 0x86, 0x94, 0x22, 0x95, 0xc9, 0x95, 0x10,
+	0x5e, 0x03, 0x13, 0xc9, 0x80, 0xa1, 0x49, 0xa9, 0x47, 0x65, 0xfd, 0xee, 0x20, 0x9b, 0xc7, 0x11,
+	0x22, 0xb8, 0x09, 0x1a, 0x1e, 0x09, 0x08, 0x23, 0xae, 0x6a, 0x32, 0x25, 0x8b, 0x56, 0xcb, 0x45,
+	0x5d, 0xa9, 0x28, 0xb5, 0xaa, 0x7b, 0x79, 0x4c, 0x34, 0x64, 0x47, 0x11, 0x9a, 0xb6, 0x35, 0xdc,
+	0x3b, 0x8a, 0x4c, 0x43, 0x2e, 0x82, 0x77, 0x01, 0xe8, 0xc5, 0x61, 0x82, 0x7b, 0xcc, 0x8f, 0x23,
+	0x34, 0x23, 0x4b, 0xce, 0x95, 0x4b, 0x36, 0x4d, 0x3e, 0xab, 0x2c, 0x94, 0xc0, 0x7b, 0xa0, 0x1e,
+	0x10, 0x9c, 0x12, 0xb7, 0xcf, 0x27, 0x66, 0x68, 0xd6, 0x46, 0x78, 0x28, 0x04, 0xdb, 0x22, 0x6f,
+	0x08, 0x81, 0x09, 0x89, 0x35, 0x2b, 0x02, 0x25, 0x87, 0xf1, 0x0b, 0x82, 0x6a, 0xb6, 0x35, 0x4b,
+	0x84, 0x23, 0x05, 0x66, 0xcd, 0x41, 0x1e, 0x13, 0xb6, 0xe0, 0x00, 0xd3, 0x10, 0x01, 0x9b, 0x2d,
+	0x1d, 0x91, 0x32, 0xb6, 0x48, 0x21, 0xdc, 0x00, 0xd3, 0x07, 0x72, 0x37, 0x21, 0x4f, 0x96, 0x2c,
+	0x5b, 0x3d, 0x57, 0x1b, 0xce, 0xd1, 0x52, 0xd8, 0x01, 0x75, 0x3c, 0x60, 0x07, 0x2e, 0x89, 0xf0,
+	0x7e, 0x40, 0xd0, 0x2f, 0xeb, 0x07, 0xeb, 0x70, 0xc5, 0x96, 0x14, 0x98, 0xe5, 0x62, 0x13, 0x82,
+	0x5d, 0xd0, 0x90, 0x08, 0xcf, 0x4f, 0x25, 0xe3, 0xf7, 0x8c, 0x6d, 0xbd, 0x82, 0xd1, 0x55, 0x0a,
+	0xb3, 0x5e, 0x9c, 0xc7, 0xe0, 0x23, 0x45, 0x21, 0x11, 0xf3, 0x7b, 0x98, 0x11, 0xf4, 0x47, 0x51,
+	0xae, 0x96, 0x29, 0xd9, 0xbe, 0xef, 0x14, 0xa4, 0x19, 0xae, 0x54, 0x0f, 0xb7, 0x40, 0x53, 0x4e,
+	0x25, 0x8e, 0x8d, 0x8b, 0x3d, 0x0f, 0x7d, 0x9c, 0x1d, 0x37, 0xd6, 0x63, 0xfe, 0xd6, 0xf1, 0xbc,
+	0xd2, 0x58, 0x3a, 0xc6, 0xc7, 0x9a, 0xcf, 0x31, 0x6a, 0x4f, 0xa2, 0x4f, 0x8a, 0x74, 0xc1, 0x4e,
+	0xd2, 0x9b, 0x59, 0xc3, 0xe6, 0x70, 0x29, 0x5c, 0x1e, 0xab, 0x4f, 0x18, 0xfa, 0x7c, 0xea, 0x58,
+	0xdb, 0x84, 0x8d, 0x8c, 0xc5, 0x63, 0xb0, 0x0f, 0xce, 0xe6, 0x98, 0xde, 0x81, 0x38, 0x25, 0x6e,
+	0x82, 0xd3, 0xf4, 0x55, 0x4c, 0x3d, 0xf4, 0x45, 0x21, 0xaf, 0xdb, 0x91, 0x9b, 0x52, 0xbd, 0xab,
+	0xc5, 0x19, 0xfd, 0x7f, 0x6c, 0x4d, 0xc3, 0x27, 0x60, 0xb1, 0x30, 0xaf, 0xd8, 0xde, 0x2e, 0x8d,
+	0xb9, 0xc9, 0x27, 0xaa, 0xc7, 0xa5, 0x31, 0x63, 0xcb, 0xa3, 0x11, 0xe7, 0x56, 0xff, 0x87, 0x87,
+	0x33, 0xf0, 0x29, 0x58, 0xca, 0xc9, 0xea, 0xa4, 0x28, 0xf4, 0x57, 0x85, 0xbe, 0x6c, 0x47, 0xeb,
+	0x23, 0x53, 0x60, 0x43, 0x3c, 0x92, 0x32, 0x9f, 0x59, 0x00, 0xa5, 0xfb, 0x6f, 0x6a, 0xe3, 0x3e,
+	0xb3, 0xd0, 0x0f, 0xbb, 0xaf, 0x63, 0xc6, 0x7d, 0x89, 0xd1, 0xee, 0xbf, 0xad, 0x8d, 0x73, 0x5f,
+	0x54, 0x59, 0xdc, 0xcf, 0xc3, 0xe5, 0xb1, 0x84, 0xfb, 0xef, 0x4e, 0x1d, 0x6b, 0xd8, 0x7d, 0x1d,
+	0x83, 0xcf, 0x41, 0xab, 0x80, 0x91, 0xa6, 0x24, 0x84, 0x86, 0x7e, 0x9a, 0x8a, 0xff, 0xbc, 0xf7,
+	0x8a, 0x79, 0x63, 0x0c, 0x53, 0xc8, 0x77, 0x8d, 0x3a, 0xe3, 0x9f, 0xc1, 0xf6, 0x3c, 0x0c, 0xc1,
+	0x72, 0xde, 0x4b, 0xdb, 0x54, 0x68, 0xf6, 0x41, 0x35, 0xbb, 0x69, 0x6f, 0xa6, 0x1c, 0x19, 0xed,
+	0x86, 0xf0, 0x18, 0x41, 0xfb, 0x5f, 0xd0, 0xdc, 0x0a, 0x13, 0xf6, 0xda, 0x21, 0x69, 0x12, 0x47,
+	0x29, 0x69, 0x27, 0x60, 0xf9, 0x94, 0x43, 0x0f, 0x21, 0x98, 0x94, 0x97, 0x64, 0x45, 0x5e, 0x92,
+	0xf2, 0x59, 0x5c, 0x9e, 0xe6, 0x2c, 0xe8, 0xcb, 0x33, 0x7b, 0x87, 0xe7, 0x41, 0x23, 0xf5, 0xc3,
+	0x84, 0xaf, 0x85, 0xf1, 0xc6, 0x91, 0xbc, 0xf4, 0x6a, 0x4e, 0x5d, 0xc5, 0xf6, 0x44, 0xe8, 0xfe,
+	0xfc, 0xf1, 0x8f, 0x95, 0x7f, 0x8e, 0x7f, 0xae, 0x54, 0x4e, 0xf8, 0xef, 0x3b, 0xff, 0xed, 0x4f,
+	0xcb, 0x5b, 0x7b, 0xe3, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa6, 0x96, 0x31, 0x96, 0x0d, 0x08,
+	0x00, 0x00,
 }
 }

+ 13 - 1
etcdserver/etcdserverpb/raft_internal.proto

@@ -38,7 +38,7 @@ message InternalRaftRequest {
   AuthEnableRequest auth_enable = 1000;
   AuthEnableRequest auth_enable = 1000;
   AuthDisableRequest auth_disable = 1011;
   AuthDisableRequest auth_disable = 1011;
 
 
-  AuthenticateRequest authenticate = 1012;
+  InternalAuthenticateRequest authenticate = 1012;
 
 
   AuthUserAddRequest auth_user_add = 1100;
   AuthUserAddRequest auth_user_add = 1100;
   AuthUserDeleteRequest auth_user_delete = 1101;
   AuthUserDeleteRequest auth_user_delete = 1101;
@@ -56,3 +56,15 @@ message InternalRaftRequest {
 
 
 message EmptyResponse {
 message EmptyResponse {
 }
 }
+
+// What is the difference between AuthenticateRequest (defined in rpc.proto) and InternalAuthenticateRequest?
+// InternalAuthenticateRequest has a member that is filled by etcdserver and shouldn't be user-facing.
+// For avoiding misusage the field, we have an internal version of AuthenticateRequest.
+message InternalAuthenticateRequest {
+  string name = 1;
+  string password = 2;
+
+  // simple_token is generated in API layer (etcdserver/v3_server.go)
+  string simple_token = 3;
+}
+

+ 11 - 0
etcdserver/server.go

@@ -214,6 +214,8 @@ type EtcdServer struct {
 	// wg is used to wait for the go routines that depends on the server state
 	// wg is used to wait for the go routines that depends on the server state
 	// to exit when stopping the server.
 	// to exit when stopping the server.
 	wg sync.WaitGroup
 	wg sync.WaitGroup
+
+	appliedIndex uint64
 }
 }
 
 
 // NewServer creates a new EtcdServer from the supplied configuration. The
 // NewServer creates a new EtcdServer from the supplied configuration. The
@@ -1064,6 +1066,7 @@ func (s *EtcdServer) applyEntryNormal(e *raftpb.Entry) {
 	// set the consistent index of current executing entry
 	// set the consistent index of current executing entry
 	s.consistIndex.setConsistentIndex(e.Index)
 	s.consistIndex.setConsistentIndex(e.Index)
 	ar := s.applyV3Request(&raftReq)
 	ar := s.applyV3Request(&raftReq)
+	s.setAppliedIndex(e.Index)
 	if ar.err != ErrNoSpace || len(s.alarmStore.Get(pb.AlarmType_NOSPACE)) > 0 {
 	if ar.err != ErrNoSpace || len(s.alarmStore.Get(pb.AlarmType_NOSPACE)) > 0 {
 		s.w.Trigger(id, ar)
 		s.w.Trigger(id, ar)
 		return
 		return
@@ -1313,3 +1316,11 @@ func (s *EtcdServer) restoreAlarms() error {
 	}
 	}
 	return nil
 	return nil
 }
 }
+
+func (s *EtcdServer) getAppliedIndex() uint64 {
+	return atomic.LoadUint64(&s.appliedIndex)
+}
+
+func (s *EtcdServer) setAppliedIndex(v uint64) {
+	atomic.StoreUint64(&s.appliedIndex, v)
+}

+ 48 - 1
etcdserver/v3_server.go

@@ -15,6 +15,8 @@
 package etcdserver
 package etcdserver
 
 
 import (
 import (
+	"strconv"
+	"strings"
 	"time"
 	"time"
 
 
 	"github.com/coreos/etcd/auth"
 	"github.com/coreos/etcd/auth"
@@ -271,7 +273,18 @@ func (s *EtcdServer) AuthDisable(ctx context.Context, r *pb.AuthDisableRequest)
 }
 }
 
 
 func (s *EtcdServer) Authenticate(ctx context.Context, r *pb.AuthenticateRequest) (*pb.AuthenticateResponse, error) {
 func (s *EtcdServer) Authenticate(ctx context.Context, r *pb.AuthenticateRequest) (*pb.AuthenticateResponse, error) {
-	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Authenticate: r})
+	st, err := s.AuthStore().GenSimpleToken()
+	if err != nil {
+		return nil, err
+	}
+
+	internalReq := &pb.InternalAuthenticateRequest{
+		Name:        r.Name,
+		Password:    r.Password,
+		SimpleToken: st,
+	}
+
+	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Authenticate: internalReq})
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -402,6 +415,36 @@ func (s *EtcdServer) RoleDelete(ctx context.Context, r *pb.AuthRoleDeleteRequest
 	return result.resp.(*pb.AuthRoleDeleteResponse), nil
 	return result.resp.(*pb.AuthRoleDeleteResponse), nil
 }
 }
 
 
+func (s *EtcdServer) isValidSimpleToken(token string) bool {
+	splitted := strings.Split(token, ".")
+	if len(splitted) != 2 {
+		return false
+	}
+	index, err := strconv.Atoi(splitted[1])
+	if err != nil {
+		return false
+	}
+
+	// CAUTION: below index synchronization is required because this node
+	// might not receive and apply the log entry of Authenticate() RPC.
+	authApplied := false
+	for i := 0; i < 10; i++ {
+		if uint64(index) <= s.getAppliedIndex() {
+			authApplied = true
+			break
+		}
+
+		time.Sleep(100 * time.Millisecond)
+	}
+
+	if !authApplied {
+		plog.Errorf("timeout of waiting Authenticate() RPC")
+		return false
+	}
+
+	return true
+}
+
 func (s *EtcdServer) usernameFromCtx(ctx context.Context) (string, error) {
 func (s *EtcdServer) usernameFromCtx(ctx context.Context) (string, error) {
 	md, ok := metadata.FromContext(ctx)
 	md, ok := metadata.FromContext(ctx)
 	if !ok {
 	if !ok {
@@ -414,6 +457,10 @@ func (s *EtcdServer) usernameFromCtx(ctx context.Context) (string, error) {
 	}
 	}
 
 
 	token := ts[0]
 	token := ts[0]
+	if !s.isValidSimpleToken(token) {
+		return "", ErrInvalidAuthToken
+	}
+
 	username, uok := s.AuthStore().UsernameFromToken(token)
 	username, uok := s.AuthStore().UsernameFromToken(token)
 	if !uok {
 	if !uok {
 		plog.Warningf("invalid auth token: %s", token)
 		plog.Warningf("invalid auth token: %s", token)