From 1253a5628652eb9fdacf3945d0278873728419e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20B=C3=B6hlke?= Date: Mon, 19 Jan 2026 01:03:24 +0000 Subject: [PATCH 1/7] feat: add OIDC middleware support via traefik-oidc-auth plugin - Add OIDC configuration variables to entrypoint - Generate OIDC middleware config when OIDC_ENABLED is set - Apply oidc-auth middleware to routes automatically - Add documentation for plugin loading via command-line --- entrypoint.sh | 41 +++++++++++++++++++++++++++++++++++++++++ templates/traefik.yml | 6 ++++++ 2 files changed, 47 insertions(+) diff --git a/entrypoint.sh b/entrypoint.sh index 1083260..b61b06e 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -18,6 +18,13 @@ EXTERNAL_ADDRESS="${EXTERNAL_ADDRESS:-openslides.example.com}" ACME_ENDPOINT="${ACME_ENDPOINT:-}" ACME_EMAIL="${ACME_EMAIL:-}" +# OIDC configuration +OIDC_ENABLED="${OIDC_ENABLED:-}" +OIDC_SESSION_SECRET="${OIDC_SESSION_SECRET:-}" +OIDC_PROVIDER_URL="${OIDC_PROVIDER_URL:-}" +OIDC_CLIENT_ID="${OIDC_CLIENT_ID:-}" +OIDC_CLIENT_SECRET="${OIDC_CLIENT_SECRET:-}" + # Set default values for service endpoints ACTION_HOST="${ACTION_HOST:-backend}" ACTION_PORT="${ACTION_PORT:-9002}" @@ -161,6 +168,11 @@ EOF # Concatenate all enabled .router files for service in $SERVICES; do envsubst < "$SERVICES_DIR/${service}.router" >> "$DYNAMIC_CONFIG" + # Add OIDC middleware to routes if enabled (except for auth service) + if [ -n "$OIDC_ENABLED" ] && [ "$service" != "auth" ]; then + echo " middlewares:" >> "$DYNAMIC_CONFIG" + echo " - oidc-auth" >> "$DYNAMIC_CONFIG" + fi done # Add services section @@ -174,6 +186,35 @@ for service in $SERVICES; do envsubst < "$SERVICES_DIR/${service}.service" >> "$DYNAMIC_CONFIG" done +# Add OIDC middleware configuration if enabled +if [ -n "$OIDC_ENABLED" ]; then + echo "Enabling OIDC authentication middleware" + cat >> "$DYNAMIC_CONFIG" << EOF + + middlewares: + oidc-auth: + plugin: + traefik-oidc-auth: + Secret: "${OIDC_SESSION_SECRET}" + Provider: + Url: "${OIDC_PROVIDER_URL}" + ClientId: "${OIDC_CLIENT_ID}" + ClientSecret: "${OIDC_CLIENT_SECRET}" + UsePkce: true + Scopes: + - openid + - profile + - email + - roles + CallbackUri: /oauth2/callback + LogoutUri: /oauth2/logout + Headers: + Authorization: "Bearer {{ .AccessToken }}" + X-Forwarded-User: "{{ .Claims.preferred_username }}" + X-Auth-Request-Email: "{{ .Claims.email }}" +EOF +fi + # Finally start CMD exec "$@" diff --git a/templates/traefik.yml b/templates/traefik.yml index c5cb72f..a5e340f 100644 --- a/templates/traefik.yml +++ b/templates/traefik.yml @@ -1,5 +1,11 @@ # Traefik configuration for OpenSlides +# Experimental plugins configuration +# The traefik-oidc-auth plugin is loaded via command-line arguments +# when using docker-compose.oidc.yml overlay: +# --experimental.plugins.traefik-oidc-auth.modulename=github.com/sevensolutions/traefik-oidc-auth +# --experimental.plugins.traefik-oidc-auth.version=v0.19.3 + # Add provider to read routing config from file providers: file: From 7574a13be25b2b25f4286ad8854a54d18c834ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20B=C3=B6hlke?= Date: Mon, 2 Feb 2026 21:40:00 +0000 Subject: [PATCH 2/7] feat: fix OIDC Traefik plugin configuration and template escaping Pass CLI args through command.sh to enable plugin loading. Add experimental plugins section to static config when OIDC is enabled. Fix Go template escaping in OIDC middleware headers and add LoginUri configuration. --- dev/command.sh | 6 +++--- entrypoint.sh | 20 +++++++++++++++++--- services/oidc.router | 6 ++++++ 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 services/oidc.router diff --git a/dev/command.sh b/dev/command.sh index 33d0162..f8e9cd0 100644 --- a/dev/command.sh +++ b/dev/command.sh @@ -2,15 +2,15 @@ if [ "$APP_CONTEXT" = "dev" ]; then echo "Starting Traefik in development mode..." - exec traefik + exec traefik "$@" fi if [ "$APP_CONTEXT" = "prod" ]; then echo "Starting Traefik in production mode..." - exec traefik + exec traefik "$@" fi if [ "$APP_CONTEXT" = "tests" ]; then echo "Starting Traefik in test mode..." - exec traefik + exec traefik "$@" fi diff --git a/entrypoint.sh b/entrypoint.sh index b61b06e..dd6fce0 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -57,6 +57,19 @@ CLIENT_PORT="${CLIENT_PORT:-9001}" # Generate base config from template envsubst < /templates/traefik.yml > "$TRAEFIK_CONFIG" +# Add experimental plugins section if OIDC is enabled +if [ -n "$OIDC_ENABLED" ]; then + echo "Configuring OIDC plugin in static configuration" + cat >> "$TRAEFIK_CONFIG" << 'EOF' + +experimental: + plugins: + traefik-oidc-auth: + moduleName: github.com/sevensolutions/traefik-oidc-auth + version: v0.17.0 +EOF +fi + # Add dashboard if enabled if [ -n "$ENABLE_DASHBOARD" ]; then echo "Enabling dashboard. 'debug: true' for now. NOT FOR PRODUCTION" @@ -206,12 +219,13 @@ if [ -n "$OIDC_ENABLED" ]; then - profile - email - roles + LoginUri: /oauth2/login CallbackUri: /oauth2/callback LogoutUri: /oauth2/logout Headers: - Authorization: "Bearer {{ .AccessToken }}" - X-Forwarded-User: "{{ .Claims.preferred_username }}" - X-Auth-Request-Email: "{{ .Claims.email }}" + Authorization: 'Bearer {{ "{{" }} .AccessToken {{ "}}" }}' + X-Forwarded-User: '{{ "{{" }} .Claims.preferred_username {{ "}}" }}' + X-Auth-Request-Email: '{{ "{{" }} .Claims.email {{ "}}" }}' EOF fi diff --git a/services/oidc.router b/services/oidc.router new file mode 100644 index 0000000..74bceae --- /dev/null +++ b/services/oidc.router @@ -0,0 +1,6 @@ + oidc: + rule: "PathPrefix(`/oauth2`)" + service: client + entryPoints: + - main + priority: 10 From 153cbad57a2166bfa55b9e937e1724efbff24dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20B=C3=B6hlke?= Date: Tue, 3 Feb 2026 15:09:26 +0100 Subject: [PATCH 3/7] feat: configure OIDC provisioning route and fix middleware - Add /system/auth/oidc-provision route with OIDC middleware - Add /system/auth/who-am-i route without OIDC middleware - Skip auth service routing in OIDC mode - Fix Header template syntax for traefik-oidc-auth plugin - Enable debug logging for troubleshooting Co-Authored-By: Claude Opus 4.5 --- entrypoint.sh | 41 ++++++++++++++++++++++++++++++++++++----- templates/traefik.yml | 10 +++++++++- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index dd6fce0..8c083fb 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -164,6 +164,11 @@ for service_file in $SERVICES_DIR/*.service; do fi if eval [[ -n "\$${host_var}" ]]; then + # Skip auth service in OIDC mode - authentication handled by Keycloak + if [ -n "$OIDC_ENABLED" ] && [ "$service" = "auth" ]; then + echo "Skipping auth service in OIDC mode (auth handled by Keycloak)" + continue + fi eval "echo \"Adding config: $service (host: \$${host_var})\"" >&2 SERVICES="$SERVICES $service" else @@ -181,13 +186,34 @@ EOF # Concatenate all enabled .router files for service in $SERVICES; do envsubst < "$SERVICES_DIR/${service}.router" >> "$DYNAMIC_CONFIG" - # Add OIDC middleware to routes if enabled (except for auth service) - if [ -n "$OIDC_ENABLED" ] && [ "$service" != "auth" ]; then + # Add OIDC middleware only to client route (for login redirect) + # Backend services use OpenSlides JWT from cookie, not OIDC middleware + if [ -n "$OIDC_ENABLED" ] && [ "$service" = "client" ]; then echo " middlewares:" >> "$DYNAMIC_CONFIG" echo " - oidc-auth" >> "$DYNAMIC_CONFIG" fi done +# In OIDC mode, add provisioning and who-am-i routes to backend +if [ -n "$OIDC_ENABLED" ]; then + echo "Adding OIDC auth routers (routes to backend)" + cat >> "$DYNAMIC_CONFIG" << EOF + auth-oidc-provision: + rule: "PathPrefix(\`/system/auth/oidc-provision\`)" + service: action + entryPoints: + - main + middlewares: + - oidc-auth + priority: 15 + auth-who-am-i: + rule: "PathPrefix(\`/system/auth/who-am-i\`)" + service: action + entryPoints: + - main +EOF +fi + # Add services section cat >> "$DYNAMIC_CONFIG" << 'EOF' @@ -208,6 +234,7 @@ if [ -n "$OIDC_ENABLED" ]; then oidc-auth: plugin: traefik-oidc-auth: + LogLevel: debug Secret: "${OIDC_SESSION_SECRET}" Provider: Url: "${OIDC_PROVIDER_URL}" @@ -222,10 +249,14 @@ if [ -n "$OIDC_ENABLED" ]; then LoginUri: /oauth2/login CallbackUri: /oauth2/callback LogoutUri: /oauth2/logout + PostLoginRedirectUri: /system/auth/oidc-provision Headers: - Authorization: 'Bearer {{ "{{" }} .AccessToken {{ "}}" }}' - X-Forwarded-User: '{{ "{{" }} .Claims.preferred_username {{ "}}" }}' - X-Auth-Request-Email: '{{ "{{" }} .Claims.email {{ "}}" }}' + - Name: Authorization + Value: 'Bearer {{ "{{ .accessToken }}" }}' + - Name: X-Forwarded-User + Value: '{{ "{{ .claims.preferred_username }}" }}' + - Name: X-Auth-Request-Email + Value: '{{ "{{ .claims.email }}" }}' EOF fi diff --git a/templates/traefik.yml b/templates/traefik.yml index a5e340f..bdc4861 100644 --- a/templates/traefik.yml +++ b/templates/traefik.yml @@ -19,7 +19,15 @@ ping: {} log: level: ${TRAEFIK_LOG_LEVEL} -accessLog: {} +accessLog: + fields: + headers: + defaultMode: keep + names: + Authorization: keep + X-Forwarded-User: keep + X-Auth-Request-Email: keep + authentication: keep # entryPoints are generated dynamically in entrypoint.sh script as their # definitions depend on TLS configuration From d86b4149ca4658327cfa50db9b4af738b51b4744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20B=C3=B6hlke?= Date: Wed, 4 Feb 2026 06:38:14 +0100 Subject: [PATCH 4/7] feat: add OIDC middleware configuration for Traefik - Configure traefik-oidc-auth plugin for Keycloak authentication - Forward access token to backend via Authorization header Co-Authored-By: Claude Opus 4.5 --- entrypoint.sh | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 8c083fb..50546e7 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -186,11 +186,15 @@ EOF # Concatenate all enabled .router files for service in $SERVICES; do envsubst < "$SERVICES_DIR/${service}.router" >> "$DYNAMIC_CONFIG" - # Add OIDC middleware only to client route (for login redirect) - # Backend services use OpenSlides JWT from cookie, not OIDC middleware - if [ -n "$OIDC_ENABLED" ] && [ "$service" = "client" ]; then - echo " middlewares:" >> "$DYNAMIC_CONFIG" - echo " - oidc-auth" >> "$DYNAMIC_CONFIG" + # Add OIDC middleware to routes that need authentication + # In OIDC mode, Traefik injects the Authorization header with access token + if [ -n "$OIDC_ENABLED" ]; then + case "$service" in + client|autoupdate|action|presenter|icc|vote|search|media|projector) + echo " middlewares:" >> "$DYNAMIC_CONFIG" + echo " - oidc-auth" >> "$DYNAMIC_CONFIG" + ;; + esac fi done @@ -198,6 +202,12 @@ done if [ -n "$OIDC_ENABLED" ]; then echo "Adding OIDC auth routers (routes to backend)" cat >> "$DYNAMIC_CONFIG" << EOF + keycloak: + rule: "PathPrefix(\`/auth\`)" + service: keycloak + entryPoints: + - main + priority: 10 auth-oidc-provision: rule: "PathPrefix(\`/system/auth/oidc-provision\`)" service: action @@ -211,6 +221,9 @@ if [ -n "$OIDC_ENABLED" ]; then service: action entryPoints: - main + middlewares: + - oidc-auth + priority: 15 EOF fi @@ -225,6 +238,17 @@ for service in $SERVICES; do envsubst < "$SERVICES_DIR/${service}.service" >> "$DYNAMIC_CONFIG" done +# Add Keycloak service if OIDC is enabled +if [ -n "$OIDC_ENABLED" ]; then + cat >> "$DYNAMIC_CONFIG" << EOF + keycloak: + loadBalancer: + servers: + - url: "http://keycloak:8080" + passHostHeader: true +EOF +fi + # Add OIDC middleware configuration if enabled if [ -n "$OIDC_ENABLED" ]; then echo "Enabling OIDC authentication middleware" @@ -250,9 +274,12 @@ if [ -n "$OIDC_ENABLED" ]; then CallbackUri: /oauth2/callback LogoutUri: /oauth2/logout PostLoginRedirectUri: /system/auth/oidc-provision + UnauthorizedBehavior: Auto + SessionCookie: + SameSite: lax Headers: - - Name: Authorization - Value: 'Bearer {{ "{{ .accessToken }}" }}' + - Name: Authentication + Value: 'bearer {{ "{{ .accessToken }}" }}' - Name: X-Forwarded-User Value: '{{ "{{ .claims.preferred_username }}" }}' - Name: X-Auth-Request-Email From 60d278a180d604d41435dbf6ec67dd3bc00439f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20B=C3=B6hlke?= Date: Fri, 6 Feb 2026 08:10:53 +0100 Subject: [PATCH 5/7] feat: add theme endpoint and consolidate oauth2 route - Add autoupdate-theme router for anonymous theme data access - Move oauth2 route from static file to dynamic entrypoint.sh - Apply oidc-auth middleware to oauth2 route when OIDC enabled Co-Authored-By: Claude Opus 4.5 --- entrypoint.sh | 14 ++++++++++++++ services/oidc.router | 6 ------ 2 files changed, 14 insertions(+), 6 deletions(-) delete mode 100644 services/oidc.router diff --git a/entrypoint.sh b/entrypoint.sh index 50546e7..03fd102 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -224,6 +224,20 @@ if [ -n "$OIDC_ENABLED" ]; then middlewares: - oidc-auth priority: 15 + autoupdate-theme: + rule: "Path(\`/system/autoupdate/theme\`)" + service: autoupdate + entryPoints: + - main + priority: 15 + oauth2: + rule: "PathPrefix(\`/oauth2\`)" + service: client + entryPoints: + - main + middlewares: + - oidc-auth + priority: 10 EOF fi diff --git a/services/oidc.router b/services/oidc.router deleted file mode 100644 index 74bceae..0000000 --- a/services/oidc.router +++ /dev/null @@ -1,6 +0,0 @@ - oidc: - rule: "PathPrefix(`/oauth2`)" - service: client - entryPoints: - - main - priority: 10 From d51cff3663f228c32f6079198f8ca9f649c5662d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20B=C3=B6hlke?= Date: Sun, 8 Feb 2026 17:41:00 +0100 Subject: [PATCH 6/7] fix: enable OIDC logout by making session cookie accessible to JS Set HttpOnly: false so the client can detect the OIDC session cookie and use the correct logout flow. Add explicit PostLogoutRedirectUri to redirect back to the app after Keycloak logout. Co-Authored-By: Claude Opus 4.6 --- entrypoint.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/entrypoint.sh b/entrypoint.sh index 03fd102..2800028 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -289,8 +289,10 @@ if [ -n "$OIDC_ENABLED" ]; then LogoutUri: /oauth2/logout PostLoginRedirectUri: /system/auth/oidc-provision UnauthorizedBehavior: Auto + PostLogoutRedirectUri: / SessionCookie: SameSite: lax + HttpOnly: false Headers: - Name: Authentication Value: 'bearer {{ "{{ .accessToken }}" }}' From d03ef684abb3b4772e8978d4b7f5b3bf01657bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20B=C3=B6hlke?= Date: Sun, 8 Feb 2026 22:21:10 +0100 Subject: [PATCH 7/7] fix: increase autoupdate-theme route priority to avoid OIDC middleware The autoupdate-theme route (priority 15) was being matched after the general autoupdate route which has OIDC middleware, causing 401 errors for unauthenticated theme requests. Increase to priority 50 so it matches first without requiring authentication. Co-Authored-By: Claude Opus 4.6 --- entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entrypoint.sh b/entrypoint.sh index 2800028..82365d8 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -229,7 +229,7 @@ if [ -n "$OIDC_ENABLED" ]; then service: autoupdate entryPoints: - main - priority: 15 + priority: 50 oauth2: rule: "PathPrefix(\`/oauth2\`)" service: client