mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-13 04:07:37 +00:00
Complete rebuild of the plugin
* Fix bug affecting Azure OIDC authentication ( and most likely others ) * Fixes issue #51 * Ensure that appended roles are unique. Update the documentation. * Improvements targetting possible memory usage spikes. * Additional fixes and cleanup * Refactoring code to fix the issues identified by the users. * Modernize run * Fieldalignment * Multiple changes to improve performance and reduce complexity. - Optimise the errors and recovery. - Deduplicate code in metadata cache. - Remove unused performance monitoring code. - Simplify session management and settings handling. * Fix claims issue. * Add ability to overwrite the default scopes in the settings file * Well.. that escalated quickly. Completely forgot that Traefik uses outdated Yaegi and requires compatibility with 1.20 ( pre-generic Go code ). * Bugfix #51: Ensures that user provided scopes overrides work. * fixup! Bugfix #51: Ensures that user provided scopes overrides work. * fixup! fixup! Bugfix #51: Ensures that user provided scopes overrides work. * Abstract the provider logic into a separate package. * Additional micro fixes and cleanups. * Simplify all the things. * fixup! Simplify all the things. * fixup! fixup! Simplify all the things. * fixup! fixup! fixup! Simplify all the things. * fixup! fixup! fixup! fixup! Simplify all the things. * ... * Cleanup tests. * fixup! Cleanup tests. * fixup! fixup! fixup! Cleanup tests. * fixup! fixup! fixup! fixup! Cleanup tests. * fixup! fixup! fixup! fixup! fixup! Cleanup tests. * Issue #53: Fix CSRF token handling in reverse proxy 1. ✅ HTTPS Detection Fixed (session.go:723) - Now uses X-Forwarded-Proto header instead of r.URL.Scheme - Properly detects HTTPS in reverse proxy environments 2. ✅ SameSite Cookie Attribute Fixed - Removed automatic SameSiteStrictMode for HTTPS (would break OAuth) - Keeps SameSiteLaxMode to allow OAuth callbacks from external domains - Only uses Strict for AJAX requests which don't involve OAuth redirects 3. ✅ Cookie Domain Handling Fixed - Now respects X-Forwarded-Host header for cookie domain - Ensures cookies are set for the public domain, not internal proxy domain 4. ✅ EnhanceSessionSecurity Properly Integrated - Function is now actually called during session save - Applies security enhancements without breaking OAuth flow Why Issue #53 Failed Before: 1. Cookies were not marked Secure in HTTPS environments (browser wouldn't send them back) 2. If they had been Secure with SameSite=Strict, Azure callbacks would still fail 3. Cookie domain might have been wrong (internal vs public domain) Why It Works Now: 1. Cookies are properly marked Secure for HTTPS 2. Uses SameSite=Lax to allow OAuth provider callbacks 3. Cookie domain uses public domain from X-Forwarded-Host 4. CSRF token persists through the entire OAuth flow * Next set of enhancements together with memory usage improvements. * Memory leak fixes and optimisations. * CSRF and Cookie Domain fixes * fixup! CSRF and Cookie Domain fixes * Metadata cache leak fix + profiling * fixup! Metadata cache leak fix + profiling * Memory leaks hunting, part 1337. * Further pursue of perfection. * fixup! Further pursue of perfection. * fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection. * Clear race conditions * fixup! Clear race conditions * Weekend fun with memory leaks * Splitting code into multiple files with reasonable testing coverage. ``` ok github.com/lukaszraczylo/traefikoidc 117.017s coverage: 72.6% of statements ok github.com/lukaszraczylo/traefikoidc/auth 0.505s coverage: 87.1% of statements ok github.com/lukaszraczylo/traefikoidc/circuit_breaker 0.283s coverage: 99.0% of statements github.com/lukaszraczylo/traefikoidc/config coverage: 0.0% of statements ok github.com/lukaszraczylo/traefikoidc/handlers 0.349s coverage: 98.2% of statements ok github.com/lukaszraczylo/traefikoidc/internal/providers (cached) coverage: 94.3% of statements ok github.com/lukaszraczylo/traefikoidc/middleware 0.808s coverage: 78.0% of statements ok github.com/lukaszraczylo/traefikoidc/recovery 0.653s coverage: 100.0% of statements ok github.com/lukaszraczylo/traefikoidc/session/chunking (cached) coverage: 87.8% of statements ok github.com/lukaszraczylo/traefikoidc/session/core (cached) coverage: 85.6% of statements ok github.com/lukaszraczylo/traefikoidc/session/crypto (cached) coverage: 81.8% of statements ok github.com/lukaszraczylo/traefikoidc/session/storage (cached) coverage: 93.5% of statements ok github.com/lukaszraczylo/traefikoidc/session/validators (cached) coverage: 98.8% of statements ```` * fixup! Splitting code into multiple files with reasonable testing coverage. * fixup! fixup! Splitting code into multiple files with reasonable testing coverage. * Weekend fun with further optimisations. * fixup! Weekend fun with further optimisations. * fixup! fixup! Weekend fun with further optimisations. * fixup! fixup! fixup! Weekend fun with further optimisations. * fixup! fixup! fixup! fixup! Weekend fun with further optimisations. * fixup! fixup! fixup! fixup! fixup! Weekend fun with further optimisations. * Pre-release cleanup. * Enhance test coverage. * fixup! Enhance test coverage. * fixup! fixup! Enhance test coverage. * fixup! fixup! fixup! Enhance test coverage.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,211 @@
|
||||
// Package config provides configuration management for the OIDC middleware
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
minEncryptionKeyLength = 16
|
||||
ConstSessionTimeout = 86400
|
||||
)
|
||||
|
||||
//lint:ignore U1000 May be referenced for default exclusion patterns
|
||||
var defaultExcludedURLs = map[string]struct{}{
|
||||
"/favicon.ico": {},
|
||||
"/robots.txt": {},
|
||||
"/health": {},
|
||||
"/.well-known/": {},
|
||||
"/metrics": {},
|
||||
"/ping": {},
|
||||
"/api/": {},
|
||||
"/static/": {},
|
||||
"/assets/": {},
|
||||
"/js/": {},
|
||||
"/css/": {},
|
||||
"/images/": {},
|
||||
"/fonts/": {},
|
||||
}
|
||||
|
||||
// Settings manages configuration and initialization for the OIDC middleware
|
||||
type Settings struct {
|
||||
logger Logger
|
||||
}
|
||||
|
||||
// Logger interface for dependency injection
|
||||
type Logger interface {
|
||||
Debug(msg string)
|
||||
Debugf(format string, args ...interface{})
|
||||
Info(msg string)
|
||||
Infof(format string, args ...interface{})
|
||||
Error(msg string)
|
||||
Errorf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
// Config represents the configuration for the OIDC middleware
|
||||
type Config struct {
|
||||
ProviderURL string `json:"providerUrl"`
|
||||
ClientID string `json:"clientId"`
|
||||
ClientSecret string `json:"clientSecret"`
|
||||
CallbackURL string `json:"callbackUrl"`
|
||||
LogoutURL string `json:"logoutUrl"`
|
||||
PostLogoutRedirectURI string `json:"postLogoutRedirectUri"`
|
||||
SessionEncryptionKey string `json:"sessionEncryptionKey"`
|
||||
ForceHTTPS bool `json:"forceHttps"`
|
||||
LogLevel string `json:"logLevel"`
|
||||
Scopes []string `json:"scopes"`
|
||||
OverrideScopes bool `json:"overrideScopes"`
|
||||
AllowedUsers []string `json:"allowedUsers"`
|
||||
AllowedUserDomains []string `json:"allowedUserDomains"`
|
||||
AllowedRolesAndGroups []string `json:"allowedRolesAndGroups"`
|
||||
ExcludedURLs []string `json:"excludedUrls"`
|
||||
EnablePKCE bool `json:"enablePkce"`
|
||||
RateLimit int `json:"rateLimit"`
|
||||
RefreshGracePeriodSeconds int `json:"refreshGracePeriodSeconds"`
|
||||
Headers []HeaderConfig `json:"headers"`
|
||||
HTTPClient *http.Client `json:"-"`
|
||||
CookieDomain string `json:"cookieDomain"`
|
||||
}
|
||||
|
||||
// HeaderConfig represents header template configuration
|
||||
type HeaderConfig struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// NewSettings creates a new Settings instance
|
||||
func NewSettings(logger Logger) *Settings {
|
||||
return &Settings{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateConfig creates a default configuration
|
||||
func CreateConfig() *Config {
|
||||
return &Config{
|
||||
LogLevel: "INFO",
|
||||
ForceHTTPS: true,
|
||||
EnablePKCE: true,
|
||||
RateLimit: 10,
|
||||
RefreshGracePeriodSeconds: 60,
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
Headers: []HeaderConfig{},
|
||||
}
|
||||
}
|
||||
|
||||
// InitializeTraefikOidc would initialize and configure a new TraefikOidc instance
|
||||
// This functionality has been moved to the main New function in main.go
|
||||
// This function is kept for compatibility but should not be used
|
||||
func (s *Settings) InitializeTraefikOidc(ctx context.Context, next http.Handler, config *Config, name string) (interface{}, error) {
|
||||
return nil, fmt.Errorf("InitializeTraefikOidc is deprecated - use New function from main package instead")
|
||||
}
|
||||
|
||||
//lint:ignore U1000 Kept for backward compatibility
|
||||
func (s *Settings) setupHeaderTemplates(t interface{}, config *Config, logger Logger) error {
|
||||
logger.Debug("setupHeaderTemplates is deprecated")
|
||||
return nil
|
||||
}
|
||||
|
||||
//lint:ignore U1000 May be needed for future background service management
|
||||
func (s *Settings) startBackgroundServices(ctx context.Context, logger Logger) {
|
||||
startReplayCacheCleanup(ctx, logger)
|
||||
|
||||
// Start memory monitoring for leak detection and performance insights
|
||||
memoryMonitor := GetGlobalMemoryMonitor()
|
||||
memoryMonitor.StartMonitoring(ctx, 60*time.Second) // Monitor every minute
|
||||
logger.Debug("Started global memory monitoring")
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
|
||||
//lint:ignore U1000 May be needed for future scope processing
|
||||
func deduplicateScopes(scopes []string) []string {
|
||||
seen := make(map[string]bool)
|
||||
result := []string{}
|
||||
for _, scope := range scopes {
|
||||
if !seen[scope] {
|
||||
seen[scope] = true
|
||||
result = append(result, scope)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
//lint:ignore U1000 May be needed for future scope merging operations
|
||||
func mergeScopes(defaultScopes, userScopes []string) []string {
|
||||
result := make([]string, len(defaultScopes))
|
||||
copy(result, defaultScopes)
|
||||
return append(result, userScopes...)
|
||||
}
|
||||
|
||||
//lint:ignore U1000 May be needed for future utility operations
|
||||
func createStringMap(items []string) map[string]struct{} {
|
||||
result := make(map[string]struct{})
|
||||
for _, item := range items {
|
||||
result[item] = struct{}{}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
//lint:ignore U1000 May be needed for future case-insensitive operations
|
||||
func createCaseInsensitiveStringMap(items []string) map[string]struct{} {
|
||||
result := make(map[string]struct{})
|
||||
for _, item := range items {
|
||||
result[strings.ToLower(item)] = struct{}{}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
//lint:ignore U1000 May be needed for future test environment detection
|
||||
func isTestMode() bool {
|
||||
// This function should be implemented based on environment detection logic
|
||||
return false
|
||||
}
|
||||
|
||||
// External dependencies that need to be provided
|
||||
// TraefikOidc struct is defined in types.go
|
||||
|
||||
// These functions need to be provided by external packages
|
||||
func NewLogger(level string) Logger { return nil }
|
||||
func CreateDefaultHTTPClient() *http.Client { return nil }
|
||||
func CreateTokenHTTPClient() *http.Client { return nil }
|
||||
func GetGlobalCacheManager(*sync.WaitGroup) CacheManager { return nil }
|
||||
func NewSessionManager(string, bool, string, Logger) (SessionManager, error) { return nil, nil }
|
||||
func NewErrorRecoveryManager(Logger) ErrorRecoveryManager { return nil }
|
||||
|
||||
//lint:ignore U1000 May be needed for future token claim extraction
|
||||
func extractClaims(string) (map[string]interface{}, error) { return nil, nil }
|
||||
|
||||
//lint:ignore U1000 May be needed for future replay attack prevention
|
||||
func startReplayCacheCleanup(context.Context, Logger) {}
|
||||
func GetGlobalMemoryMonitor() MemoryMonitor { return nil }
|
||||
|
||||
// Interfaces for external dependencies
|
||||
type CacheManager interface {
|
||||
GetSharedTokenBlacklist() CacheInterface
|
||||
GetSharedTokenCache() *TokenCache
|
||||
GetSharedMetadataCache() *MetadataCache
|
||||
GetSharedJWKCache() JWKCacheInterface
|
||||
Close() error
|
||||
}
|
||||
type SessionManager interface{}
|
||||
type ErrorRecoveryManager interface{}
|
||||
type MemoryMonitor interface {
|
||||
StartMonitoring(ctx context.Context, interval time.Duration)
|
||||
}
|
||||
type CacheInterface interface {
|
||||
Set(key string, value interface{}, ttl time.Duration)
|
||||
Get(key string) (interface{}, bool)
|
||||
Delete(key string)
|
||||
SetMaxSize(size int)
|
||||
Cleanup()
|
||||
Close()
|
||||
}
|
||||
type TokenCache struct{}
|
||||
type MetadataCache struct{}
|
||||
type JWKCacheInterface interface{}
|
||||
Reference in New Issue
Block a user