From 4488595e05340c603398e0cd49e4f7bb829f7533 Mon Sep 17 00:00:00 2001 From: cfc4n Date: Thu, 11 Jun 2020 17:15:42 +0800 Subject: [PATCH] auth: Customize simpleTokenTTL settings. see https://github.com/etcd-io/etcd/issues/11978 for more detail. --- auth/simple_token.go | 25 ++++++++++++++++++------- auth/simple_token_test.go | 6 +++--- auth/store.go | 6 ++++-- auth/store_test.go | 22 +++++++++++----------- embed/config.go | 8 ++++++-- embed/etcd.go | 1 + etcdmain/config.go | 1 + etcdmain/help.go | 2 ++ etcdserver/config.go | 1 + etcdserver/server.go | 1 + 10 files changed, 48 insertions(+), 25 deletions(-) diff --git a/auth/simple_token.go b/auth/simple_token.go index 934978c9857..a9dc5b715f6 100644 --- a/auth/simple_token.go +++ b/auth/simple_token.go @@ -37,7 +37,7 @@ const ( // var for testing purposes var ( - simpleTokenTTL = 5 * time.Minute + simpleTokenTTLDefault = 300 * time.Second simpleTokenTTLResolution = 1 * time.Second ) @@ -47,6 +47,7 @@ type simpleTokenTTLKeeper struct { stopc chan struct{} deleteTokenFunc func(string) mu *sync.Mutex + simpleTokenTTL time.Duration } func (tm *simpleTokenTTLKeeper) stop() { @@ -58,12 +59,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) } } @@ -101,6 +102,7 @@ type tokenSimple struct { simpleTokenKeeper *simpleTokenTTLKeeper simpleTokensMu sync.Mutex simpleTokens map[string]string // token -> username + simpleTokenTTL time.Duration } func (t *tokenSimple) genTokenPrefix() (string, error) { @@ -157,6 +159,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 { if t.lg != nil { @@ -177,6 +183,7 @@ func (t *tokenSimple) enable() { stopc: make(chan struct{}), deleteTokenFunc: delf, mu: &t.simpleTokensMu, + simpleTokenTTL: t.simpleTokenTTL, } go t.simpleTokenKeeper.run() } @@ -234,10 +241,14 @@ func (t *tokenSimple) isValidSimpleToken(ctx context.Context, token string) bool return false } -func newTokenProviderSimple(lg *zap.Logger, indexWaiter func(uint64) <-chan struct{}) *tokenSimple { +func newTokenProviderSimple(lg *zap.Logger, indexWaiter func(uint64) <-chan struct{}, TokenTTL time.Duration) *tokenSimple { + if lg == nil { + lg = zap.NewNop() + } return &tokenSimple{ - lg: lg, - simpleTokens: make(map[string]string), - indexWaiter: indexWaiter, + lg: lg, + simpleTokens: make(map[string]string), + indexWaiter: indexWaiter, + simpleTokenTTL: TokenTTL, } } diff --git a/auth/simple_token_test.go b/auth/simple_token_test.go index 598095aaaaa..bc6c0f0eb6b 100644 --- a/auth/simple_token_test.go +++ b/auth/simple_token_test.go @@ -24,9 +24,9 @@ import ( // TestSimpleTokenDisabled ensures that TokenProviderSimple behaves correctly when // disabled. func TestSimpleTokenDisabled(t *testing.T) { - initialState := newTokenProviderSimple(zap.NewExample(), dummyIndexWaiter) + initialState := newTokenProviderSimple(zap.NewExample(), dummyIndexWaiter, simpleTokenTTLDefault) - explicitlyDisabled := newTokenProviderSimple(zap.NewExample(), dummyIndexWaiter) + explicitlyDisabled := newTokenProviderSimple(zap.NewExample(), dummyIndexWaiter, simpleTokenTTLDefault) explicitlyDisabled.enable() explicitlyDisabled.disable() @@ -48,7 +48,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(zap.NewExample(), dummyIndexWaiter) + tp := newTokenProviderSimple(zap.NewExample(), 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 3565587a1a7..eac0e877584 100644 --- a/auth/store.go +++ b/auth/store.go @@ -23,6 +23,7 @@ import ( "strings" "sync" "sync/atomic" + "time" "go.etcd.io/etcd/auth/authpb" "go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes" @@ -1352,7 +1353,8 @@ func decomposeOpts(lg *zap.Logger, optstr string) (string, map[string]string, er func NewTokenProvider( lg *zap.Logger, tokenOpts string, - indexWaiter func(uint64) <-chan struct{}) (TokenProvider, error) { + indexWaiter func(uint64) <-chan struct{}, + TokenTTL time.Duration) (TokenProvider, error) { tokenType, typeSpecificOpts, err := decomposeOpts(lg, tokenOpts) if err != nil { return nil, ErrInvalidAuthOpts @@ -1365,7 +1367,7 @@ func NewTokenProvider( } else { plog.Warningf("simple token is not cryptographically signed") } - return newTokenProviderSimple(lg, indexWaiter), nil + return newTokenProviderSimple(lg, indexWaiter, TokenTTL), nil case tokenTypeJWT: return newTokenProviderJWT(lg, typeSpecificOpts) diff --git a/auth/store_test.go b/auth/store_test.go index bb971699509..0f289964ecc 100644 --- a/auth/store_test.go +++ b/auth/store_test.go @@ -48,7 +48,7 @@ func TestNewAuthStoreRevision(t *testing.T) { b, tPath := backend.NewDefaultTmpBackend() defer os.Remove(tPath) - tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter) + tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault) if err != nil { t.Fatal(err) } @@ -78,7 +78,7 @@ func TestNewAuthStoreBcryptCost(t *testing.T) { b, tPath := backend.NewDefaultTmpBackend() defer os.Remove(tPath) - tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter) + tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault) if err != nil { t.Fatal(err) } @@ -98,7 +98,7 @@ func TestNewAuthStoreBcryptCost(t *testing.T) { func setupAuthStore(t *testing.T) (store *authStore, teardownfunc func(t *testing.T)) { b, tPath := backend.NewDefaultTmpBackend() - tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter) + tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault) if err != nil { t.Fatal(err) } @@ -626,7 +626,7 @@ func TestAuthInfoFromCtxRace(t *testing.T) { b, tPath := backend.NewDefaultTmpBackend() defer os.Remove(tPath) - tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter) + tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault) if err != nil { t.Fatal(err) } @@ -692,7 +692,7 @@ func TestRecoverFromSnapshot(t *testing.T) { as.Close() - tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter) + tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault) if err != nil { t.Fatal(err) } @@ -725,13 +725,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) @@ -774,7 +774,7 @@ func TestRolesOrder(t *testing.T) { b, tPath := backend.NewDefaultTmpBackend() defer os.Remove(tPath) - tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter) + tp, err := NewTokenProvider(zap.NewExample(), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault) if err != nil { t.Fatal(err) } @@ -829,7 +829,7 @@ func testAuthInfoFromCtxWithRoot(t *testing.T, opts string) { b, tPath := backend.NewDefaultTmpBackend() defer os.Remove(tPath) - tp, err := NewTokenProvider(zap.NewExample(), opts, dummyIndexWaiter) + tp, err := NewTokenProvider(zap.NewExample(), opts, dummyIndexWaiter, simpleTokenTTLDefault) if err != nil { t.Fatal(err) } diff --git a/embed/config.go b/embed/config.go index 12d6401d76e..a21a41b0209 100644 --- a/embed/config.go +++ b/embed/config.go @@ -273,6 +273,9 @@ type Config struct { AuthToken string `json:"auth-token"` BcryptCost uint `json:"bcrypt-cost"` + //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"` @@ -410,8 +413,9 @@ func NewConfig() *Config { CORS: map[string]struct{}{"*": {}}, HostWhitelist: map[string]struct{}{"*": {}}, - AuthToken: "simple", - BcryptCost: uint(bcrypt.DefaultCost), + AuthToken: "simple", + BcryptCost: uint(bcrypt.DefaultCost), + AuthTokenTTL: 300, PreVote: false, // TODO: enable by default in v3.5 diff --git a/embed/etcd.go b/embed/etcd.go index 86bf7c97eeb..30e56f668a1 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -192,6 +192,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) { ClientCertAuthEnabled: cfg.ClientTLSInfo.ClientCertAuth, AuthToken: cfg.AuthToken, BcryptCost: cfg.BcryptCost, + TokenTTL: cfg.AuthTokenTTL, CORS: cfg.CORS, HostWhitelist: cfg.HostWhitelist, InitialCorruptCheck: cfg.ExperimentalInitialCorruptCheck, diff --git a/etcdmain/config.go b/etcdmain/config.go index 5bcb3b8796a..96dd697d32e 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -245,6 +245,7 @@ func newConfig() *config { // auth fs.StringVar(&cfg.ec.AuthToken, "auth-token", cfg.ec.AuthToken, "Specify auth token specific options.") fs.UintVar(&cfg.ec.BcryptCost, "bcrypt-cost", cfg.ec.BcryptCost, "Specify bcrypt algorithm cost factor for auth password hashing.") + fs.UintVar(&cfg.ec.AuthTokenTTL, "auth-token-ttl", cfg.ec.AuthTokenTTL, "The lifetime in seconds of the auth token.") // gateway fs.BoolVar(&cfg.ec.EnableGRPCGateway, "enable-grpc-gateway", true, "Enable GRPC gateway.") diff --git a/etcdmain/help.go b/etcdmain/help.go index 693b8af3ace..b298ecbcf15 100644 --- a/etcdmain/help.go +++ b/etcdmain/help.go @@ -162,6 +162,8 @@ Auth: Specify a v3 authentication token type and its options ('simple' or 'jwt'). --bcrypt-cost ` + fmt.Sprintf("%d", bcrypt.DefaultCost) + ` Specify the cost / strength of the bcrypt algorithm for hashing auth passwords. Valid values are between ` + fmt.Sprintf("%d", bcrypt.MinCost) + ` and ` + fmt.Sprintf("%d", bcrypt.MaxCost) + `. + --auth-token-ttl 300 + Time (in seconds) of the auth-token-ttl. Profiling and Monitoring: --enable-pprof 'false' diff --git a/etcdserver/config.go b/etcdserver/config.go index e7b71e7120d..b8cdc037a7f 100644 --- a/etcdserver/config.go +++ b/etcdserver/config.go @@ -126,6 +126,7 @@ type ServerConfig struct { AuthToken string BcryptCost uint + 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 fb98c05b7c6..a341625dccb 100644 --- a/etcdserver/server.go +++ b/etcdserver/server.go @@ -553,6 +553,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 { if cfg.Logger != nil {