diff --git a/CHANGELOG-6.md b/CHANGELOG-6.md index 119040c1a..8a9a7494b 100644 --- a/CHANGELOG-6.md +++ b/CHANGELOG-6.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [6.11.1] - 2024-09-12 + +### Changed +- ADD TTL to each user session in the etcd data store to prevent leak. +- TTl value is DefaultAccessTokenLifeSpan + 1 minute + ## [6.11.0] - 2024-01-31 ### Changed diff --git a/backend/authentication/jwt/jwt.go b/backend/authentication/jwt/jwt.go index 09c65d526..81fbb8444 100644 --- a/backend/authentication/jwt/jwt.go +++ b/backend/authentication/jwt/jwt.go @@ -26,7 +26,7 @@ const ( ) var ( - defaultAccessTokenLifespan = 5 * time.Minute + DefaultAccessTokenLifespan = 5 * time.Minute defaultRefreshTokenLifespan = 12 * time.Hour secret []byte privateKey *ecdsa.PrivateKey @@ -58,7 +58,7 @@ func AccessToken(claims *corev2.Claims) (*jwt.Token, string, error) { claims.Id = jti // Add an expiration to the token - claims.ExpiresAt = time.Now().Add(defaultAccessTokenLifespan).Unix() + claims.ExpiresAt = time.Now().Add(DefaultAccessTokenLifespan).Unix() token := jwt.NewWithClaims(signingMethod, claims) @@ -91,7 +91,7 @@ func NewClaims(user *corev2.User) (*corev2.Claims, error) { // library's documentation. We should replace its usage with // RegisteredClaims. StandardClaims: jwt.StandardClaims{ - ExpiresAt: time.Now().Add(defaultAccessTokenLifespan).Unix(), + ExpiresAt: time.Now().Add(DefaultAccessTokenLifespan).Unix(), Id: jti, Subject: user.Username, }, diff --git a/backend/authentication/jwt/jwt_test.go b/backend/authentication/jwt/jwt_test.go index 0abc6ba5e..bbfea6756 100644 --- a/backend/authentication/jwt/jwt_test.go +++ b/backend/authentication/jwt/jwt_test.go @@ -121,7 +121,7 @@ func TestValidateTokenError(t *testing.T) { assert.NoError(t, err) // The token should expire after the expiration time - testTime.Set(time.Now().Add(defaultAccessTokenLifespan + time.Hour)) + testTime.Set(time.Now().Add(DefaultAccessTokenLifespan + time.Hour)) _, err = ValidateToken(tokenString) assert.Error(t, err) } @@ -134,7 +134,7 @@ func TestValidateExpiredToken(t *testing.T) { _, tokenString, _ := AccessToken(claims) // Wait for the token to expire - testTime.Set(time.Now().Add(defaultAccessTokenLifespan + time.Second)) + testTime.Set(time.Now().Add(DefaultAccessTokenLifespan + time.Second)) _, err := ValidateExpiredToken(tokenString) assert.NoError(t, err, "An expired token should not be considered as invalid") } @@ -158,7 +158,7 @@ func TestValidateExpiredTokenInvalid(t *testing.T) { _, tokenString, _ := AccessToken(claims) // The token will expire - testTime.Set(time.Now().Add(defaultAccessTokenLifespan + time.Second)) + testTime.Set(time.Now().Add(DefaultAccessTokenLifespan + time.Second)) // Modify the secret so it's no longer valid secret = []byte("qux") diff --git a/backend/store/etcd/session.go b/backend/store/etcd/session.go index 9f95b85bf..2af98e839 100644 --- a/backend/store/etcd/session.go +++ b/backend/store/etcd/session.go @@ -3,7 +3,7 @@ package etcd import ( "context" "fmt" - + "github.com/sensu/sensu-go/backend/authentication/jwt" "github.com/sensu/sensu-go/backend/store" "go.etcd.io/etcd/client/v3" ) @@ -30,12 +30,18 @@ func (s *Store) GetSession(ctx context.Context, username, sessionID string) (str } // UpdateSession applies the supplied state to the session uniquely identified -// by the given username and session ID. +// by the given username and session ID with attached lease for TTL of the key func (s *Store) UpdateSession(ctx context.Context, username, sessionID, state string) error { - if _, err := s.client.Put(ctx, userSessionPath(username, sessionID), state); err != nil { + + leaseDuration := jwt.DefaultAccessTokenLifespan + ttl := int64(leaseDuration.Minutes()+1) * 60 + leaseResp, err := s.client.Grant(ctx, ttl) + if err != nil { + return fmt.Errorf("%s", err) + } + if _, err := s.client.Put(ctx, userSessionPath(username, sessionID), state, clientv3.WithLease(leaseResp.ID)); err != nil { return err } - return nil } @@ -45,6 +51,5 @@ func (s *Store) DeleteSession(ctx context.Context, username, sessionID string) e if _, err := s.client.Delete(ctx, userSessionPath(username, sessionID)); err != nil { return err } - return nil }