mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-18 04:47:28 +00:00
fix(security): allow loopback hosts for loopback providerURL (#154)
Issue #154: a providerURL that points at a localhost IdP caused an infinite redirect loop. validateHost rejected loopback hosts unconditionally, so buildAuthURL returned an empty string. Derive allowLoopbackHosts from a loopback providerURL at construction time. Discovery validation already used this derivation, so the two validation layers now agree. This adds no new config surface. Cloud metadata hosts (169.254.169.254, metadata.google.internal), link-local, and unspecified addresses stay blocked unconditionally. Non-loopback providers keep identical behaviour. Add issue154_regression_test.go: 8 tests pin the new allowance, the default block, and the security envelope.
This commit is contained in:
@@ -91,100 +91,105 @@ type TraefikOidc struct {
|
||||
// Hot-path readers (middleware.ServeHTTP, token verification) load it
|
||||
// directly; less-frequent paths still acquire metadataMu.RLock and
|
||||
// read the individual fields below.
|
||||
metadataSnapshot atomic.Value
|
||||
metadataSnapshot atomic.Value
|
||||
// lastMetadataRetryNano is the UnixNano timestamp of the last metadata
|
||||
// recovery attempt. Stored atomically so the hot ServeHTTP path can
|
||||
// throttle retries without acquiring metadataRetryMutex on every request.
|
||||
lastMetadataRetryNano int64
|
||||
lastMetadataRetryNano int64
|
||||
// firstRequestStarted is 0 until the very first non-health request fires
|
||||
// the background-task bootstrap; then it flips to 1 via CAS. Replaces the
|
||||
// firstRequestMutex + firstRequestReceived combo which previously took
|
||||
// a write lock on every non-health request forever.
|
||||
firstRequestStarted int32
|
||||
firstRequestStarted int32
|
||||
// metadataRefreshStartedAtomic is the CAS-only variant of the old
|
||||
// metadataRefreshStarted bool. Both flags live under the same atomic so
|
||||
// concurrent first-request goroutines race exactly once.
|
||||
metadataRefreshStartedAtomic int32
|
||||
jwkCache JWKCacheInterface
|
||||
jwtVerifier JWTVerifier
|
||||
ctx context.Context
|
||||
tokenVerifier TokenVerifier
|
||||
next http.Handler
|
||||
tokenExchanger TokenExchanger
|
||||
tokenBlacklist CacheInterface
|
||||
tokenTypeCache CacheInterface
|
||||
introspectionCache CacheInterface
|
||||
initComplete chan struct{}
|
||||
limiter *rate.Limiter
|
||||
headerTemplates map[string]*template.Template
|
||||
sessionManager *SessionManager
|
||||
tokenCleanupStopChan chan struct{}
|
||||
excludedURLs map[string]struct{}
|
||||
extractClaimsFunc func(tokenString string) (map[string]any, error)
|
||||
initiateAuthenticationFunc func(rw http.ResponseWriter, req *http.Request, session *SessionData, redirectURL string)
|
||||
metadataCache *MetadataCache
|
||||
allowedRolesAndGroups map[string]struct{}
|
||||
allowedUsers map[string]struct{}
|
||||
allowedUserDomains map[string]struct{}
|
||||
tokenCache *TokenCache
|
||||
httpClient *http.Client
|
||||
tokenHTTPClient *http.Client
|
||||
logger *Logger
|
||||
metadataRefreshStopChan chan struct{}
|
||||
cancelFunc context.CancelFunc
|
||||
errorRecoveryManager *ErrorRecoveryManager
|
||||
tokenResilienceManager *TokenResilienceManager
|
||||
refreshCoordinator *RefreshCoordinator
|
||||
goroutineWG *sync.WaitGroup
|
||||
dcrConfig *DynamicClientRegistrationConfig
|
||||
dynamicClientRegistrar *DynamicClientRegistrar
|
||||
scopeFilter *ScopeFilter
|
||||
securityHeadersApplier func(http.ResponseWriter, *http.Request)
|
||||
userIdentifierClaim string
|
||||
revocationURL string
|
||||
name string
|
||||
redirURLPath string
|
||||
logoutURLPath string
|
||||
tokenURL string
|
||||
authURL string
|
||||
endSessionURL string
|
||||
postLogoutRedirectURI string
|
||||
jwksURL string
|
||||
issuerURL string
|
||||
groupClaimName string
|
||||
introspectionURL string
|
||||
providerURL string
|
||||
roleClaimName string
|
||||
audience string
|
||||
clientID string
|
||||
clientSecret string
|
||||
clientAuthMethod string
|
||||
clientAssertion *ClientAssertionSigner
|
||||
registrationURL string
|
||||
backchannelLogoutPath string
|
||||
frontchannelLogoutPath string
|
||||
scopesSupported []string
|
||||
scopes []string
|
||||
extraAuthParams map[string]string
|
||||
refreshGracePeriod time.Duration
|
||||
maxRefreshTokenAge time.Duration
|
||||
metadataMu sync.RWMutex
|
||||
shutdownOnce sync.Once
|
||||
sessionInvalidationCache CacheInterface
|
||||
refreshResultCache CacheInterface
|
||||
minimalHeaders bool
|
||||
stripAuthCookies bool
|
||||
enableBackchannelLogout bool
|
||||
enableFrontchannelLogout bool
|
||||
requireTokenIntrospection bool
|
||||
allowPrivateIPAddresses bool
|
||||
disableReplayDetection bool
|
||||
allowOpaqueTokens bool
|
||||
strictAudienceValidation bool
|
||||
overrideScopes bool
|
||||
enablePKCE bool
|
||||
forceHTTPS bool
|
||||
suppressDiagnosticLogs bool
|
||||
jwkCache JWKCacheInterface
|
||||
jwtVerifier JWTVerifier
|
||||
ctx context.Context
|
||||
tokenVerifier TokenVerifier
|
||||
next http.Handler
|
||||
tokenExchanger TokenExchanger
|
||||
tokenBlacklist CacheInterface
|
||||
tokenTypeCache CacheInterface
|
||||
introspectionCache CacheInterface
|
||||
initComplete chan struct{}
|
||||
limiter *rate.Limiter
|
||||
headerTemplates map[string]*template.Template
|
||||
sessionManager *SessionManager
|
||||
tokenCleanupStopChan chan struct{}
|
||||
excludedURLs map[string]struct{}
|
||||
extractClaimsFunc func(tokenString string) (map[string]any, error)
|
||||
initiateAuthenticationFunc func(rw http.ResponseWriter, req *http.Request, session *SessionData, redirectURL string)
|
||||
metadataCache *MetadataCache
|
||||
allowedRolesAndGroups map[string]struct{}
|
||||
allowedUsers map[string]struct{}
|
||||
allowedUserDomains map[string]struct{}
|
||||
tokenCache *TokenCache
|
||||
httpClient *http.Client
|
||||
tokenHTTPClient *http.Client
|
||||
logger *Logger
|
||||
metadataRefreshStopChan chan struct{}
|
||||
cancelFunc context.CancelFunc
|
||||
errorRecoveryManager *ErrorRecoveryManager
|
||||
tokenResilienceManager *TokenResilienceManager
|
||||
refreshCoordinator *RefreshCoordinator
|
||||
goroutineWG *sync.WaitGroup
|
||||
dcrConfig *DynamicClientRegistrationConfig
|
||||
dynamicClientRegistrar *DynamicClientRegistrar
|
||||
scopeFilter *ScopeFilter
|
||||
securityHeadersApplier func(http.ResponseWriter, *http.Request)
|
||||
userIdentifierClaim string
|
||||
revocationURL string
|
||||
name string
|
||||
redirURLPath string
|
||||
logoutURLPath string
|
||||
tokenURL string
|
||||
authURL string
|
||||
endSessionURL string
|
||||
postLogoutRedirectURI string
|
||||
jwksURL string
|
||||
issuerURL string
|
||||
groupClaimName string
|
||||
introspectionURL string
|
||||
providerURL string
|
||||
roleClaimName string
|
||||
audience string
|
||||
clientID string
|
||||
clientSecret string
|
||||
clientAuthMethod string
|
||||
clientAssertion *ClientAssertionSigner
|
||||
registrationURL string
|
||||
backchannelLogoutPath string
|
||||
frontchannelLogoutPath string
|
||||
scopesSupported []string
|
||||
scopes []string
|
||||
extraAuthParams map[string]string
|
||||
refreshGracePeriod time.Duration
|
||||
maxRefreshTokenAge time.Duration
|
||||
metadataMu sync.RWMutex
|
||||
shutdownOnce sync.Once
|
||||
sessionInvalidationCache CacheInterface
|
||||
refreshResultCache CacheInterface
|
||||
minimalHeaders bool
|
||||
stripAuthCookies bool
|
||||
enableBackchannelLogout bool
|
||||
enableFrontchannelLogout bool
|
||||
requireTokenIntrospection bool
|
||||
allowPrivateIPAddresses bool
|
||||
// allowLoopbackHosts permits loopback/localhost hosts in outbound URL
|
||||
// validation (validateHost). Derived at construction time from a loopback
|
||||
// providerURL (local development), never operator-set directly. Mirrors
|
||||
// the allowLoopback derivation in updateMetadataEndpoints.
|
||||
allowLoopbackHosts bool
|
||||
disableReplayDetection bool
|
||||
allowOpaqueTokens bool
|
||||
strictAudienceValidation bool
|
||||
overrideScopes bool
|
||||
enablePKCE bool
|
||||
forceHTTPS bool
|
||||
suppressDiagnosticLogs bool
|
||||
|
||||
// Bearer-auth runtime state (populated only when EnableBearerAuth=true).
|
||||
bearerIdentifierClaim string
|
||||
|
||||
Reference in New Issue
Block a user