Merge 16fd4ccbd8 into 14af2951e5
This commit is contained in:
commit
5a3443f47c
|
|
@ -159,6 +159,7 @@ We improved our supply chain security by added additional checks to prevent pote
|
||||||
## Changes since v7.14.2
|
## Changes since v7.14.2
|
||||||
|
|
||||||
- [#3183](https://github.com/oauth2-proxy/oauth2-proxy/pull/3183) fix: allow URL parameters to configure username, password and max idle connection timeout if the matching configuration is empty.
|
- [#3183](https://github.com/oauth2-proxy/oauth2-proxy/pull/3183) fix: allow URL parameters to configure username, password and max idle connection timeout if the matching configuration is empty.
|
||||||
|
- [#3335](https://github.com/oauth2-proxy/oauth2-proxy/pull/3335) feat: make session refresh timeouts user configurable (@raskinfe)
|
||||||
|
|
||||||
# V7.14.2
|
# V7.14.2
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -432,6 +432,9 @@ func buildSessionChain(opts *options.Options, provider providers.Provider, sessi
|
||||||
chain = chain.Append(middleware.NewStoredSessionLoader(&middleware.StoredSessionLoaderOptions{
|
chain = chain.Append(middleware.NewStoredSessionLoader(&middleware.StoredSessionLoaderOptions{
|
||||||
SessionStore: sessionStore,
|
SessionStore: sessionStore,
|
||||||
RefreshPeriod: opts.Cookie.Refresh,
|
RefreshPeriod: opts.Cookie.Refresh,
|
||||||
|
SessionRefreshLockDuration: opts.Cookie.SessionRefreshLockDuration,
|
||||||
|
SessionRefreshObtainTimeout: opts.Cookie.SessionRefreshObtainTimeout,
|
||||||
|
SessionRefreshRetryPeriod: opts.Cookie.SessionRefreshRetryPeriod,
|
||||||
RefreshSession: provider.RefreshSession,
|
RefreshSession: provider.RefreshSession,
|
||||||
ValidateSession: provider.ValidateSession,
|
ValidateSession: provider.ValidateSession,
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ type Cookie struct {
|
||||||
CSRFPerRequestLimit int `flag:"cookie-csrf-per-request-limit" cfg:"cookie_csrf_per_request_limit"`
|
CSRFPerRequestLimit int `flag:"cookie-csrf-per-request-limit" cfg:"cookie_csrf_per_request_limit"`
|
||||||
CSRFExpire time.Duration `flag:"cookie-csrf-expire" cfg:"cookie_csrf_expire"`
|
CSRFExpire time.Duration `flag:"cookie-csrf-expire" cfg:"cookie_csrf_expire"`
|
||||||
CSRFSameSite string `flag:"cookie-csrf-samesite" cfg:"cookie_csrf_samesite"`
|
CSRFSameSite string `flag:"cookie-csrf-samesite" cfg:"cookie_csrf_samesite"`
|
||||||
|
SessionRefreshLockDuration time.Duration `flag:"session-refresh-lock-duration" cfg:"session_refresh_lock_duration"`
|
||||||
|
SessionRefreshObtainTimeout time.Duration `flag:"session-refresh-obtain-timeout" cfg:"session_refresh_obtain_timeout"`
|
||||||
|
SessionRefreshRetryPeriod time.Duration `flag:"session-refresh-retry-period" cfg:"session_refresh_retry_period"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func cookieFlagSet() *pflag.FlagSet {
|
func cookieFlagSet() *pflag.FlagSet {
|
||||||
|
|
@ -44,6 +47,9 @@ func cookieFlagSet() *pflag.FlagSet {
|
||||||
flagSet.Int("cookie-csrf-per-request-limit", 0, "Sets a limit on the number of CSRF requests cookies that oauth2-proxy will create. The oldest cookies will be removed. Useful if users end up with 431 Request headers too large status codes.")
|
flagSet.Int("cookie-csrf-per-request-limit", 0, "Sets a limit on the number of CSRF requests cookies that oauth2-proxy will create. The oldest cookies will be removed. Useful if users end up with 431 Request headers too large status codes.")
|
||||||
flagSet.Duration("cookie-csrf-expire", time.Duration(15)*time.Minute, "expire timeframe for CSRF cookie")
|
flagSet.Duration("cookie-csrf-expire", time.Duration(15)*time.Minute, "expire timeframe for CSRF cookie")
|
||||||
flagSet.String("cookie-csrf-samesite", "", "set SameSite CSRF cookie attribute (ie: \"lax\", \"strict\", \"none\", or \"\"). When using the default setting, the CSRF cookie samesite value is taken from the session cookie configuration.")
|
flagSet.String("cookie-csrf-samesite", "", "set SameSite CSRF cookie attribute (ie: \"lax\", \"strict\", \"none\", or \"\"). When using the default setting, the CSRF cookie samesite value is taken from the session cookie configuration.")
|
||||||
|
flagSet.Duration("session-refresh-lock-duration", time.Duration(2)*time.Second, "maximum time allowed for a session refresh attempt; if the refresh request isn't finished within this time, the lock will be released")
|
||||||
|
flagSet.Duration("session-refresh-obtain-timeout", time.Duration(5)*time.Second, "timeout when attempting to obtain the session lock; if the lock is not obtained before this timeout, the refresh attempt will fail")
|
||||||
|
flagSet.Duration("session-refresh-retry-period", time.Duration(10)*time.Millisecond, "how long to wait after failing to obtain the lock before trying again")
|
||||||
return flagSet
|
return flagSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,6 +70,9 @@ func cookieDefaults() Cookie {
|
||||||
CSRFPerRequestLimit: 0,
|
CSRFPerRequestLimit: 0,
|
||||||
CSRFExpire: time.Duration(15) * time.Minute,
|
CSRFExpire: time.Duration(15) * time.Minute,
|
||||||
CSRFSameSite: "",
|
CSRFSameSite: "",
|
||||||
|
SessionRefreshLockDuration: time.Duration(2) * time.Second,
|
||||||
|
SessionRefreshObtainTimeout: time.Duration(5) * time.Second,
|
||||||
|
SessionRefreshRetryPeriod: time.Duration(10) * time.Millisecond,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,23 +15,6 @@ import (
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/v7/providers"
|
"github.com/oauth2-proxy/oauth2-proxy/v7/providers"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// When attempting to obtain the lock, if it's not done before this timeout
|
|
||||||
// then exit and fail the refresh attempt.
|
|
||||||
// TODO: This should probably be configurable by the end user.
|
|
||||||
sessionRefreshObtainTimeout = 5 * time.Second
|
|
||||||
|
|
||||||
// Maximum time allowed for a session refresh attempt.
|
|
||||||
// If the refresh request isn't finished within this time, the lock will be
|
|
||||||
// released.
|
|
||||||
// TODO: This should probably be configurable by the end user.
|
|
||||||
sessionRefreshLockDuration = 2 * time.Second
|
|
||||||
|
|
||||||
// How long to wait after failing to obtain the lock before trying again.
|
|
||||||
// TODO: This should probably be configurable by the end user.
|
|
||||||
sessionRefreshRetryPeriod = 10 * time.Millisecond
|
|
||||||
)
|
|
||||||
|
|
||||||
// isFatalRefreshError checks if a refresh error indicates a revoked or
|
// isFatalRefreshError checks if a refresh error indicates a revoked or
|
||||||
// non-existent session that should be immediately invalidated.
|
// non-existent session that should be immediately invalidated.
|
||||||
// Fatal errors indicate the session is no longer valid at the provider level.
|
// Fatal errors indicate the session is no longer valid at the provider level.
|
||||||
|
|
@ -59,6 +42,7 @@ func isFatalRefreshError(err error) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// StoredSessionLoaderOptions contains all of the requirements to construct
|
// StoredSessionLoaderOptions contains all of the requirements to construct
|
||||||
// a stored session loader.
|
// a stored session loader.
|
||||||
// All options must be provided.
|
// All options must be provided.
|
||||||
|
|
@ -69,6 +53,17 @@ type StoredSessionLoaderOptions struct {
|
||||||
// How often should sessions be refreshed
|
// How often should sessions be refreshed
|
||||||
RefreshPeriod time.Duration
|
RefreshPeriod time.Duration
|
||||||
|
|
||||||
|
// Maximum time allowed for a session refresh attempt.
|
||||||
|
// If the refresh request isn't finished within this time, the lock will be released.
|
||||||
|
SessionRefreshLockDuration time.Duration
|
||||||
|
|
||||||
|
// Timeout when attempting to obtain the session lock.
|
||||||
|
// If the lock is not obtained before this timeout, the refresh attempt will fail.
|
||||||
|
SessionRefreshObtainTimeout time.Duration
|
||||||
|
|
||||||
|
// How long to wait after failing to obtain the lock before trying again.
|
||||||
|
SessionRefreshRetryPeriod time.Duration
|
||||||
|
|
||||||
// Provider based session refreshing
|
// Provider based session refreshing
|
||||||
RefreshSession func(context.Context, *sessionsapi.SessionState) (bool, error)
|
RefreshSession func(context.Context, *sessionsapi.SessionState) (bool, error)
|
||||||
|
|
||||||
|
|
@ -86,6 +81,9 @@ func NewStoredSessionLoader(opts *StoredSessionLoaderOptions) alice.Constructor
|
||||||
ss := &storedSessionLoader{
|
ss := &storedSessionLoader{
|
||||||
store: opts.SessionStore,
|
store: opts.SessionStore,
|
||||||
refreshPeriod: opts.RefreshPeriod,
|
refreshPeriod: opts.RefreshPeriod,
|
||||||
|
sessionRefreshLockDuration: opts.SessionRefreshLockDuration,
|
||||||
|
sessionRefreshObtainTimeout: opts.SessionRefreshObtainTimeout,
|
||||||
|
sessionRefreshRetryPeriod: opts.SessionRefreshRetryPeriod,
|
||||||
sessionRefresher: opts.RefreshSession,
|
sessionRefresher: opts.RefreshSession,
|
||||||
sessionValidator: opts.ValidateSession,
|
sessionValidator: opts.ValidateSession,
|
||||||
}
|
}
|
||||||
|
|
@ -97,6 +95,9 @@ func NewStoredSessionLoader(opts *StoredSessionLoaderOptions) alice.Constructor
|
||||||
type storedSessionLoader struct {
|
type storedSessionLoader struct {
|
||||||
store sessionsapi.SessionStore
|
store sessionsapi.SessionStore
|
||||||
refreshPeriod time.Duration
|
refreshPeriod time.Duration
|
||||||
|
sessionRefreshLockDuration time.Duration
|
||||||
|
sessionRefreshObtainTimeout time.Duration
|
||||||
|
sessionRefreshRetryPeriod time.Duration
|
||||||
sessionRefresher func(context.Context, *sessionsapi.SessionState) (bool, error)
|
sessionRefresher func(context.Context, *sessionsapi.SessionState) (bool, error)
|
||||||
sessionValidator func(context.Context, *sessionsapi.SessionState) bool
|
sessionValidator func(context.Context, *sessionsapi.SessionState) bool
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +160,7 @@ func (s *storedSessionLoader) refreshSessionIfNeeded(rw http.ResponseWriter, req
|
||||||
}
|
}
|
||||||
|
|
||||||
var lockObtained bool
|
var lockObtained bool
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), sessionRefreshObtainTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), s.sessionRefreshObtainTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
for !lockObtained {
|
for !lockObtained {
|
||||||
|
|
@ -167,11 +168,11 @@ func (s *storedSessionLoader) refreshSessionIfNeeded(rw http.ResponseWriter, req
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return errors.New("timeout obtaining session lock")
|
return errors.New("timeout obtaining session lock")
|
||||||
default:
|
default:
|
||||||
err := session.ObtainLock(req.Context(), sessionRefreshLockDuration)
|
err := session.ObtainLock(req.Context(), s.sessionRefreshLockDuration)
|
||||||
if err != nil && !errors.Is(err, sessionsapi.ErrLockNotObtained) {
|
if err != nil && !errors.Is(err, sessionsapi.ErrLockNotObtained) {
|
||||||
return fmt.Errorf("error occurred while trying to obtain lock: %v", err)
|
return fmt.Errorf("error occurred while trying to obtain lock: %v", err)
|
||||||
} else if errors.Is(err, sessionsapi.ErrLockNotObtained) {
|
} else if errors.Is(err, sessionsapi.ErrLockNotObtained) {
|
||||||
time.Sleep(sessionRefreshRetryPeriod)
|
time.Sleep(s.sessionRefreshRetryPeriod)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// No error means we obtained the lock
|
// No error means we obtained the lock
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,9 @@ var _ = Describe("Stored Session Suite", func() {
|
||||||
opts := &StoredSessionLoaderOptions{
|
opts := &StoredSessionLoaderOptions{
|
||||||
SessionStore: in.store,
|
SessionStore: in.store,
|
||||||
RefreshPeriod: in.refreshPeriod,
|
RefreshPeriod: in.refreshPeriod,
|
||||||
|
SessionRefreshLockDuration: 2 * time.Second,
|
||||||
|
SessionRefreshObtainTimeout: 5 * time.Second,
|
||||||
|
SessionRefreshRetryPeriod: 10 * time.Millisecond,
|
||||||
RefreshSession: in.refreshSession,
|
RefreshSession: in.refreshSession,
|
||||||
ValidateSession: in.validateSession,
|
ValidateSession: in.validateSession,
|
||||||
}
|
}
|
||||||
|
|
@ -386,6 +389,9 @@ var _ = Describe("Stored Session Suite", func() {
|
||||||
opts := &StoredSessionLoaderOptions{
|
opts := &StoredSessionLoaderOptions{
|
||||||
SessionStore: store,
|
SessionStore: store,
|
||||||
RefreshPeriod: in.refreshPeriod,
|
RefreshPeriod: in.refreshPeriod,
|
||||||
|
SessionRefreshLockDuration: 2 * time.Second,
|
||||||
|
SessionRefreshObtainTimeout: 5 * time.Second,
|
||||||
|
SessionRefreshRetryPeriod: 10 * time.Millisecond,
|
||||||
RefreshSession: func(ctx context.Context, s *sessionsapi.SessionState) (bool, error) {
|
RefreshSession: func(ctx context.Context, s *sessionsapi.SessionState) (bool, error) {
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
sessionRefreshed = true
|
sessionRefreshed = true
|
||||||
|
|
@ -482,6 +488,9 @@ var _ = Describe("Stored Session Suite", func() {
|
||||||
s := &storedSessionLoader{
|
s := &storedSessionLoader{
|
||||||
refreshPeriod: in.refreshPeriod,
|
refreshPeriod: in.refreshPeriod,
|
||||||
store: store,
|
store: store,
|
||||||
|
sessionRefreshLockDuration: 2 * time.Second,
|
||||||
|
sessionRefreshObtainTimeout: 5 * time.Second,
|
||||||
|
sessionRefreshRetryPeriod: 10 * time.Millisecond,
|
||||||
sessionRefresher: func(_ context.Context, ss *sessionsapi.SessionState) (bool, error) {
|
sessionRefresher: func(_ context.Context, ss *sessionsapi.SessionState) (bool, error) {
|
||||||
refreshed = true
|
refreshed = true
|
||||||
switch ss.RefreshToken {
|
switch ss.RefreshToken {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue