Files
traefikoidc/.traefik.yml
T
lukaszraczylo f728f61777 fix(config): wire introspectionURL override and repair dead endpoint overrides (#152)
Issue #152 asks for a manual introspection endpoint because some IdPs
omit introspection_endpoint from discovery. Analysis found the two
documented overrides revocationURL and oidcEndSessionURL were dead
code: the 1b49e13 rebuild dropped their constructor wiring.

Add the IntrospectionURL config field and wire all three overrides.
The overrides apply inside updateMetadataEndpoints, so they survive
every metadata refresh. A manual value always wins over discovery.
The constructor also seeds the live fields, so a failed discovery
does not lose operator configuration.

Manual values skip the poisoned-discovery defences on purpose. They
sit at the same trust tier as providerURL and isValidSecureURL gates
them at config time.

Add issue152_regression_test.go: 8 tests pin precedence, refresh
survival, cold-start seeding, end-to-end introspection over HTTP,
and the validation contract.
2026-08-09 01:59:47 +01:00

110 lines
4.7 KiB
YAML

displayName: Traefik OIDC
type: middleware
import: github.com/lukaszraczylo/traefikoidc
summary: |
OpenID Connect authentication middleware for Traefik. Replaces forward-auth
+ oauth2-proxy with a single plugin that auto-detects all major OIDC
providers (Google, Azure AD, Auth0, Okta, Keycloak, AWS Cognito, GitLab,
generic) and OAuth 2.0 for GitHub.
Features: ID-token validation with auto-discovery, session encryption,
proactive token refresh, RBAC via roles/groups claims, domain restriction,
templated downstream headers, security headers (CSP, HSTS, CORS), rate
limiting, PKCE, opaque-token introspection (RFC 7662), back/front-channel
logout, Dynamic Client Registration (RFC 7591), and Redis-backed shared
state for multi-replica deployments.
Full documentation: https://github.com/lukaszraczylo/traefikoidc
testData:
# Required
providerURL: https://accounts.google.com
clientID: 1234567890.apps.googleusercontent.com
clientSecret: your-client-secret
# Alternative: RFC 7523 private_key_jwt client authentication (Entra ID,
# Okta, Auth0, Keycloak). Replaces clientSecret with a signed JWT assertion.
# See README "Client authentication via private key JWT".
# clientAuthMethod: private_key_jwt
# clientAssertionKeyID: my-key-2026
# clientAssertionAlg: RS256 # default; or PS256/384/512, ES256/384/512
# # File path option:
# clientAssertionKeyPath: /etc/traefik/oidc/client-key.pem
# # Or inline PEM (PKCS#8 / PKCS#1 / SEC1):
# clientAssertionPrivateKey: |
# -----BEGIN PRIVATE KEY-----
# MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDexampleexample
# -----END PRIVATE KEY-----
sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long
callbackURL: /oauth2/callback
# Common production knobs
logoutURL: /oauth2/logout
postLogoutRedirectURI: /
forceHTTPS: true # default; only set false for plaintext HTTP local dev
logLevel: info
rateLimit: 100
# Access control
allowedUserDomains:
- company.com
allowedRolesAndGroups:
- admin
- developer
excludedURLs:
- /health
- /metrics
# Scopes are appended to the defaults ["openid", "profile", "email"]
scopes:
- roles
# Templated headers forwarded to backends. Values are Go templates and must
# be written with plain double braces — the catalog analyzer feeds them to
# the plugin verbatim (issue #151; quadruple braces do NOT get collapsed).
# Only when your dynamic config file is rendered by Traefik's file-provider
# Go templating do the braces need escaping there, e.g.
# value: "{{`{{.Claims.email}}`}}"
# (see README "Templated headers").
headers:
- name: X-User-Email
value: "{{.Claims.email}}"
- name: Authorization
value: "Bearer {{.AccessToken}}"
# Security headers (default profile is enabled out of the box)
securityHeaders:
enabled: true
profile: default
# Optional: Redis for multi-replica deployments. See docs/REDIS.md.
# redis:
# enabled: true
# address: redis:6379
# password: urn:k8s:secret:redis:password
# cacheMode: hybrid
# Optional: bearer-token authentication for M2M (machine-to-machine) API
# clients. Default off. When enabled, requests presenting
# "Authorization: Bearer <jwt>" are validated against the configured OIDC
# provider (signature/issuer/audience/exp) and forwarded without creating
# a cookie session. The bearer path REJECTS ID tokens, requires a non-
# default audience, and never trusts the `email` claim as the identifier.
# See docs/BEARER_AUTH.md for the full threat model.
#
# enableBearerAuth: true # opt-in
# audience: https://api.example.com # REQUIRED when bearer is enabled
# bearerIdentifierClaim: sub # default; used as X-Forwarded-User. `email` is rejected.
# stripAuthorizationHeader: true # default; drops the raw token before forwarding
# bearerEmitWWWAuthenticate: true # default; RFC 6750 hint on 401s
# bearerOverridesCookie: false # default; cookie wins when both are present
# requireTokenIntrospection: false # opt-in; calls RFC 7662 introspection per request
# introspectionURL: https://idp.example.com/oauth/introspect # manual override; set when discovery omits introspection_endpoint
# maxTokenAgeSeconds: 86400 # 24h cap on iat (rejects clock-skew/forever tokens)
# maxIdentifierLength: 256 # cap on the sanitised principal identifier
# bearerFailureThreshold: 20 # consecutive 401s/IP that trip the throttle
# bearerFailureWindowSeconds: 60 # rolling window over which 401s are counted
# bearerFailurePenaltySeconds: 60 # 429 + Retry-After duration after threshold trips