mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-14 04:07:46 +00:00
December 2025 Improvements - Azure AD, Internal Networks, Startup Race Condition (#100)
* Allow internal IPs for OIDC configuration via extra flag. Addresses issue #97 * Allow for internal IPs in OIDC configuration. Addresses issue #97. * feat: Add allowPrivateIPAddresses config option for internal networks Adds a new configuration option `allowPrivateIPAddresses` that allows OIDC provider URLs to use private IP addresses (10.x.x.x, 172.16-31.x.x, 192.168.x.x). This is useful for internal deployments where Keycloak or other OIDC providers run on private networks without DNS resolution. Security considerations: - Loopback addresses (127.0.0.1, localhost, ::1) remain blocked - Link-local addresses (169.254.x.x) remain blocked - Default is false (secure by default) Fixes #97 * feat: Support non-email user identifiers for Azure AD Add userIdentifierClaim configuration option to support Azure AD users without email addresses. This allows using alternative JWT claims like "sub", "oid", "upn", or "preferred_username" for user identification. - Default behavior uses "email" claim (backward compatible) - Falls back to "sub" claim if configured claim is missing - allowedUsers matches against the configured claim value - allowedUserDomains only applies when using email-based identification Fixes #95 * Race condition on traefik pod startup When the plugin initializes and calls GetMetadataWithRecovery(): 1. Checks cache first (if metadata is cached, returns immediately) 2. Creates a retry executor with startup-optimized settings (10 attempts, 1s delays) 3. Attempts to fetch metadata from the OIDC provider 4. If the fetch fails with a retryable error (connection refused, EOF, TLS/certificate errors, Traefik default cert), it waits and retries 5. After 10 attempts or on a non-retryable error, returns the error This allows the plugin to handle the race condition where: - Traefik initializes the plugin before routes are established - Traefik serves its default certificate before loading real ones - The OIDC provider pod isn't fully ready yet Fixes issue #90 * Race condition on traefik pod startup When the plugin initializes and calls GetMetadataWithRecovery(): 1. Checks cache first (if metadata is cached, returns immediately) 2. Creates a retry executor with startup-optimized settings (10 attempts, 1s delays) 3. Attempts to fetch metadata from the OIDC provider 4. If the fetch fails with a retryable error (connection refused, EOF, TLS/certificate errors, Traefik default cert), it waits and retries 5. After 10 attempts or on a non-retryable error, returns the error This allows the plugin to handle the race condition where: - Traefik initializes the plugin before routes are established - Traefik serves its default certificate before loading real ones - The OIDC provider pod isn't fully ready yet Fixes issue #90 * Headers too big and 431 responses Added new option `minimalHeaders` to reduce the size of forwarded headers from the auth middleware to backend services. - When minimalHeaders: false (default): All headers are forwarded as before - X-Forwarded-User (always set) - X-Auth-Request-Redirect - X-Auth-Request-User - X-Auth-Request-Token (the large ID token) - X-User-Groups, X-User-Roles (if configured) - When minimalHeaders: true: Reduces header overhead - X-Forwarded-User (always set) - X-User-Groups, X-User-Roles (still forwarded if configured) - Custom templated headers (still processed) - Skipped: X-Auth-Request-Token, X-Auth-Request-User, X-Auth-Request-Redirect Fixes issues #64 and #86
This commit is contained in:
+49
-4
@@ -189,11 +189,56 @@ func (mc *MetadataCache) GetMetadata(providerURL string, httpClient *http.Client
|
||||
return mc.GetProviderMetadata(ctx, providerURL, httpClient)
|
||||
}
|
||||
|
||||
// GetMetadataWithRecovery fetches metadata with recovery support
|
||||
// GetMetadataWithRecovery fetches metadata with retry support for startup scenarios.
|
||||
// This handles the race condition where Traefik initializes the plugin before the
|
||||
// OIDC provider routes are fully established, or before TLS certificates are loaded.
|
||||
// Uses aggressive retry settings (10 attempts, 1s intervals) to give the infrastructure
|
||||
// time to stabilize during cold starts.
|
||||
// See: https://github.com/lukaszraczylo/traefikoidc/issues/90
|
||||
func (mc *MetadataCache) GetMetadataWithRecovery(providerURL string, httpClient *http.Client, logger *Logger, errorRecoveryManager *ErrorRecoveryManager) (*ProviderMetadata, error) {
|
||||
// For now, just use regular GetMetadata
|
||||
// Recovery would be handled by ErrorRecoveryManager if needed
|
||||
return mc.GetMetadata(providerURL, httpClient, logger)
|
||||
// Check cache first - if we have valid cached metadata, use it
|
||||
if metadata, exists := mc.Get(providerURL); exists {
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
// Create a retry executor with metadata-fetch-specific configuration
|
||||
retryConfig := MetadataFetchRetryConfig()
|
||||
retryExecutor := NewRetryExecutor(retryConfig, logger)
|
||||
|
||||
var metadata *ProviderMetadata
|
||||
var lastErr error
|
||||
|
||||
// Use context with overall timeout for the entire retry sequence
|
||||
// 10 attempts * ~10s max delay = ~100s worst case, so use 2 minute timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
err := retryExecutor.ExecuteWithContext(ctx, func() error {
|
||||
// Create per-attempt context with shorter timeout
|
||||
attemptCtx, attemptCancel := context.WithTimeout(ctx, 15*time.Second)
|
||||
defer attemptCancel()
|
||||
|
||||
var fetchErr error
|
||||
metadata, fetchErr = mc.GetProviderMetadata(attemptCtx, providerURL, httpClient)
|
||||
if fetchErr != nil {
|
||||
lastErr = fetchErr
|
||||
if logger != nil {
|
||||
logger.Debugf("Metadata fetch attempt failed: %v", fetchErr)
|
||||
}
|
||||
return fetchErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
// Return the last actual error, not the retry wrapper error
|
||||
if lastErr != nil {
|
||||
return nil, lastErr
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
// GetStats returns cache statistics for testing
|
||||
|
||||
Reference in New Issue
Block a user