diff --git a/auth/simple_token.go b/auth/simple_token.go index ac55ad7f1..ba0413101 100644 --- a/auth/simple_token.go +++ b/auth/simple_token.go @@ -35,7 +35,7 @@ const ( // var for testing purposes var ( - simpleTokenTTL = 5 * time.Minute + simpleTokenTTLDefault = 300 * time.Second simpleTokenTTLResolution = 1 * time.Second ) @@ -45,6 +45,7 @@ type simpleTokenTTLKeeper struct { stopc chan struct{} deleteTokenFunc func(string) mu *sync.Mutex + simpleTokenTTL time.Duration } func (tm *simpleTokenTTLKeeper) stop() { @@ -56,12 +57,12 @@ func (tm *simpleTokenTTLKeeper) stop() { } func (tm *simpleTokenTTLKeeper) addSimpleToken(token string) { - tm.tokens[token] = time.Now().Add(simpleTokenTTL) + tm.tokens[token] = time.Now().Add(tm.simpleTokenTTL) } func (tm *simpleTokenTTLKeeper) resetSimpleToken(token string) { if _, ok := tm.tokens[token]; ok { - tm.tokens[token] = time.Now().Add(simpleTokenTTL) + tm.tokens[token] = time.Now().Add(tm.simpleTokenTTL) } } @@ -98,6 +99,7 @@ type tokenSimple struct { simpleTokenKeeper *simpleTokenTTLKeeper simpleTokensMu sync.Mutex simpleTokens map[string]string // token -> username + simpleTokenTTL time.Duration } func (t *tokenSimple) genTokenPrefix() (string, error) { @@ -146,6 +148,10 @@ func (t *tokenSimple) invalidateUser(username string) { } func (t *tokenSimple) enable() { + if t.simpleTokenTTL <= 0 { + t.simpleTokenTTL = simpleTokenTTLDefault + } + delf := func(tk string) { if username, ok := t.simpleTokens[tk]; ok { plog.Infof("deleting token %s for user %s", tk, username) @@ -158,6 +164,7 @@ func (t *tokenSimple) enable() { stopc: make(chan struct{}), deleteTokenFunc: delf, mu: &t.simpleTokensMu, + simpleTokenTTL: t.simpleTokenTTL, } go t.simpleTokenKeeper.run() } @@ -215,9 +222,10 @@ func (t *tokenSimple) isValidSimpleToken(ctx context.Context, token string) bool return false } -func newTokenProviderSimple(indexWaiter func(uint64) <-chan struct{}) *tokenSimple { +func newTokenProviderSimple(indexWaiter func(uint64) <-chan struct{}, TokenTTL time.Duration) *tokenSimple { return &tokenSimple{ - simpleTokens: make(map[string]string), - indexWaiter: indexWaiter, + simpleTokens: make(map[string]string), + indexWaiter: indexWaiter, + simpleTokenTTL: TokenTTL, } } diff --git a/auth/simple_token_test.go b/auth/simple_token_test.go index 1890521d4..c1bdab5d3 100644 --- a/auth/simple_token_test.go +++ b/auth/simple_token_test.go @@ -22,9 +22,9 @@ import ( // TestSimpleTokenDisabled ensures that TokenProviderSimple behaves correctly when // disabled. func TestSimpleTokenDisabled(t *testing.T) { - initialState := newTokenProviderSimple(dummyIndexWaiter) + initialState := newTokenProviderSimple(dummyIndexWaiter, simpleTokenTTLDefault) - explicitlyDisabled := newTokenProviderSimple(dummyIndexWaiter) + explicitlyDisabled := newTokenProviderSimple(dummyIndexWaiter, simpleTokenTTLDefault) explicitlyDisabled.enable() explicitlyDisabled.disable() @@ -46,7 +46,7 @@ func TestSimpleTokenDisabled(t *testing.T) { // TestSimpleTokenAssign ensures that TokenProviderSimple can correctly assign a // token, look it up with info, and invalidate it by user. func TestSimpleTokenAssign(t *testing.T) { - tp := newTokenProviderSimple(dummyIndexWaiter) + tp := newTokenProviderSimple(dummyIndexWaiter, simpleTokenTTLDefault) tp.enable() ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy") token, err := tp.assign(ctx, "user1", 0) diff --git a/auth/store.go b/auth/store.go index 2c40af467..2877bb8cd 100644 --- a/auth/store.go +++ b/auth/store.go @@ -23,6 +23,7 @@ import ( "strings" "sync" "sync/atomic" + "time" "github.com/coreos/etcd/auth/authpb" pb "github.com/coreos/etcd/etcdserver/etcdserverpb" @@ -1087,7 +1088,11 @@ func decomposeOpts(optstr string) (string, map[string]string, error) { } -func NewTokenProvider(tokenOpts string, indexWaiter func(uint64) <-chan struct{}) (TokenProvider, error) { +// NewTokenProvider creates a new token provider. +func NewTokenProvider( + tokenOpts string, + indexWaiter func(uint64) <-chan struct{}, + TokenTTL time.Duration) (TokenProvider, error) { tokenType, typeSpecificOpts, err := decomposeOpts(tokenOpts) if err != nil { return nil, ErrInvalidAuthOpts @@ -1096,7 +1101,7 @@ func NewTokenProvider(tokenOpts string, indexWaiter func(uint64) <-chan struct{} switch tokenType { case tokenTypeSimple: plog.Warningf("simple token is not cryptographically signed") - return newTokenProviderSimple(indexWaiter), nil + return newTokenProviderSimple(indexWaiter, TokenTTL), nil case tokenTypeJWT: return newTokenProviderJWT(typeSpecificOpts) diff --git a/auth/store_test.go b/auth/store_test.go index a60ff4f3e..b447a2c2f 100644 --- a/auth/store_test.go +++ b/auth/store_test.go @@ -17,6 +17,7 @@ package auth import ( "context" "fmt" + "go.uber.org/zap" "os" "reflect" "strings" @@ -48,7 +49,7 @@ func TestNewAuthStoreRevision(t *testing.T) { b, tPath := backend.NewDefaultTmpBackend() defer os.Remove(tPath) - tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter) + tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault) if err != nil { t.Fatal(err) } @@ -73,10 +74,32 @@ func TestNewAuthStoreRevision(t *testing.T) { } } +// TestNewAuthStoreBryptCost ensures that NewAuthStore uses default when given bcrypt-cost is invalid +func TestNewAuthStoreBcryptCost(t *testing.T) { + b, tPath := backend.NewDefaultTmpBackend() + defer os.Remove(tPath) + + tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault) + if err != nil { + t.Fatal(err) + } + + invalidCosts := [2]int{bcrypt.MinCost - 1, bcrypt.MaxCost + 1} + for _, invalidCost := range invalidCosts { + as := NewAuthStore(b, nil, tp, invalidCost) + if as.BcryptCost() != bcrypt.DefaultCost { + t.Fatalf("expected DefaultCost when bcryptcost is invalid") + } + as.Close() + } + + b.Close() +} + func setupAuthStore(t *testing.T) (store *authStore, teardownfunc func(t *testing.T)) { b, tPath := backend.NewDefaultTmpBackend() - tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter) + tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault) if err != nil { t.Fatal(err) } @@ -513,7 +536,7 @@ func TestAuthInfoFromCtxRace(t *testing.T) { b, tPath := backend.NewDefaultTmpBackend() defer os.Remove(tPath) - tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter) + tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault) if err != nil { t.Fatal(err) } @@ -585,7 +608,11 @@ func TestRecoverFromSnapshot(t *testing.T) { as.Close() +<<<<<<< HEAD tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter) +======= + tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault) +>>>>>>> auth: Customize simpleTokenTTL settings. if err != nil { t.Fatal(err) } @@ -618,13 +645,13 @@ func contains(array []string, str string) bool { func TestHammerSimpleAuthenticate(t *testing.T) { // set TTL values low to try to trigger races - oldTTL, oldTTLRes := simpleTokenTTL, simpleTokenTTLResolution + oldTTL, oldTTLRes := simpleTokenTTLDefault, simpleTokenTTLResolution defer func() { - simpleTokenTTL = oldTTL + simpleTokenTTLDefault = oldTTL simpleTokenTTLResolution = oldTTLRes }() - simpleTokenTTL = 10 * time.Millisecond - simpleTokenTTLResolution = simpleTokenTTL + simpleTokenTTLDefault = 10 * time.Millisecond + simpleTokenTTLResolution = simpleTokenTTLDefault users := make(map[string]struct{}) as, tearDown := setupAuthStore(t) @@ -667,7 +694,7 @@ func TestRolesOrder(t *testing.T) { b, tPath := backend.NewDefaultTmpBackend() defer os.Remove(tPath) - tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter) + tp, err := NewTokenProvider(tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault) if err != nil { t.Fatal(err) } @@ -722,7 +749,7 @@ func testAuthInfoFromCtxWithRoot(t *testing.T, opts string) { b, tPath := backend.NewDefaultTmpBackend() defer os.Remove(tPath) - tp, err := NewTokenProvider(opts, dummyIndexWaiter) + tp, err := NewTokenProvider(opts, dummyIndexWaiter, simpleTokenTTLDefault) if err != nil { t.Fatal(err) } diff --git a/embed/config.go b/embed/config.go index a48de2948..473e757c6 100644 --- a/embed/config.go +++ b/embed/config.go @@ -222,6 +222,9 @@ type Config struct { // Experimental flags + //The AuthTokenTTL in seconds of the simple token + AuthTokenTTL uint `json:"auth-token-ttl"` + ExperimentalInitialCorruptCheck bool `json:"experimental-initial-corrupt-check"` ExperimentalCorruptCheckTime time.Duration `json:"experimental-corrupt-check-time"` ExperimentalEnableV2V3 string `json:"experimental-enable-v2v3"` @@ -284,6 +287,7 @@ func NewConfig() *Config { Metrics: "basic", EnableV2: DefaultEnableV2, AuthToken: "simple", + AuthTokenTTL: 300, } cfg.InitialCluster = cfg.InitialClusterFromName(cfg.Name) return cfg diff --git a/embed/etcd.go b/embed/etcd.go index b0f81063b..d656e179c 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -171,6 +171,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) { StrictReconfigCheck: cfg.StrictReconfigCheck, ClientCertAuthEnabled: cfg.ClientTLSInfo.ClientCertAuth, AuthToken: cfg.AuthToken, + TokenTTL: cfg.AuthTokenTTL, InitialCorruptCheck: cfg.ExperimentalInitialCorruptCheck, CorruptCheckTime: cfg.ExperimentalCorruptCheckTime, Debug: cfg.Debug, diff --git a/etcdmain/config.go b/etcdmain/config.go index 3ccd24e3e..5ae85778b 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -215,6 +215,7 @@ func newConfig() *config { // auth fs.StringVar(&cfg.ec.AuthToken, "auth-token", cfg.ec.AuthToken, "Specify auth token specific options.") + fs.UintVar(&cfg.ec.AuthTokenTTL, "auth-token-ttl", cfg.ec.AuthTokenTTL, "The lifetime in seconds of the auth token.") // experimental fs.BoolVar(&cfg.ec.ExperimentalInitialCorruptCheck, "experimental-initial-corrupt-check", cfg.ec.ExperimentalInitialCorruptCheck, "Enable to check data corruption before serving any client/peer traffic.") diff --git a/etcdmain/help.go b/etcdmain/help.go index 04c8ec8d1..784f20815 100644 --- a/etcdmain/help.go +++ b/etcdmain/help.go @@ -193,6 +193,8 @@ profiling flags: auth flags: --auth-token 'simple' Specify a v3 authentication token type and its options ('simple' or 'jwt'). + --auth-token-ttl 300 + Time (in seconds) of the auth-token-ttl. experimental flags: --experimental-initial-corrupt-check 'false' diff --git a/etcdserver/config.go b/etcdserver/config.go index 295d95299..78b74bfa5 100644 --- a/etcdserver/config.go +++ b/etcdserver/config.go @@ -95,6 +95,7 @@ type ServerConfig struct { ClientCertAuthEnabled bool AuthToken string + TokenTTL uint // InitialCorruptCheck is true to check data corruption on boot // before serving any peer/client traffic. diff --git a/etcdserver/server.go b/etcdserver/server.go index 4fedb2007..281c84b16 100644 --- a/etcdserver/server.go +++ b/etcdserver/server.go @@ -461,6 +461,7 @@ func NewServer(cfg ServerConfig) (srv *EtcdServer, err error) { func(index uint64) <-chan struct{} { return srv.applyWait.Wait(index) }, + time.Duration(cfg.TokenTTL)*time.Second, ) if err != nil { plog.Warningf("failed to create token provider,err is %v", err)