From 5ee24b38a2c8eb652d98369a402a2cdf682e569c Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Tue, 21 Apr 2026 10:03:02 +0200 Subject: [PATCH 01/23] feat: Adding oidc middleware and keycloak server services --- entrypoint.sh | 62 ++++++++++++++++++++++++++++++++++++++ services/action.router | 2 ++ services/autoupdate.router | 2 ++ services/client.router | 2 ++ services/icc.router | 2 ++ services/keycloak.router | 5 +++ services/keycloak.service | 6 ++++ services/media.router | 2 ++ services/presenter.router | 2 ++ services/projector.router | 2 ++ services/search.router | 2 ++ services/vote.router | 2 ++ 12 files changed, 91 insertions(+) create mode 100644 services/keycloak.router create mode 100644 services/keycloak.service diff --git a/entrypoint.sh b/entrypoint.sh index 1083260..5aba728 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -41,6 +41,12 @@ VOTE_HOST="${VOTE_HOST:-vote}" VOTE_PORT="${VOTE_PORT:-9013}" CLIENT_HOST="${CLIENT_HOST:-client}" CLIENT_PORT="${CLIENT_PORT:-9001}" +KEYCLOAK_HOST="${KEYCLOAK_HOST:-keycloak-server}" +KEYCLOAK_HOST_PORT="${KEYCLOAK_HOST_PORT:-8080}" +OIDC_KEYCLOAK_URL="${OIDC_KEYCLOAK_URL:-http://localhost:8080/realms/openslides}" +OIDC_CLIENT_ID="${OIDC_CLIENT_ID:-proxy-client}" +OIDC_CLIENT_SECRET="${OIDC_CLIENT_SECRET:-proxy-secret}" +OIDC_SECRET="${OIDC_SECRET:-qvAcTGWBIGg7aWKCKRyUsTf33jK3lsmK}" # ================================= @@ -50,6 +56,18 @@ CLIENT_PORT="${CLIENT_PORT:-9001}" # Generate base config from template envsubst < /templates/traefik.yml > "$TRAEFIK_CONFIG" +# Add OIDC plugin +echo "Adding OIDC Plugin" +cat >> "$TRAEFIK_CONFIG" << 'EOF' + +experimental: + plugins: + traefik-oidc-auth: + moduleName: github.com/sevensolutions/traefik-oidc-auth + version: v0.19.0 +EOF + + # Add dashboard if enabled if [ -n "$ENABLE_DASHBOARD" ]; then echo "Enabling dashboard. 'debug: true' for now. NOT FOR PRODUCTION" @@ -174,6 +192,50 @@ for service in $SERVICES; do envsubst < "$SERVICES_DIR/${service}.service" >> "$DYNAMIC_CONFIG" done +# OIDC Middleware +cat >> "$DYNAMIC_CONFIG" << EOF + keycloak-server: + loadBalancer: + servers: + - url: "http://localhost:8080" + passHostHeader: true +EOF + +echo "Enabling OIDC authentication middleware" + cat >> "$DYNAMIC_CONFIG" << EOF + + middlewares: + oidc-auth: + plugin: + traefik-oidc-auth: + LogLevel: debug + Secret: "${OIDC_SECRET}" + Provider: + Url: "${OIDC_KEYCLOAK_URL}" + ClientId: "${OIDC_CLIENT_ID}" + ClientSecret: "${OIDC_CLIENT_SECRET}" + UsePkce: true + ValidateIssuer: true + ValidIssuer: "${OIDC_KEYCLOAK_URL}" + Scopes: + - openid + - profile + - email + LoginUri: /login + CallbackUri: /callback + LogoutUri: /logout + UnauthorizedBehavior: Challenge + SessionCookie: + SameSite: lax + HttpOnly: false + Headers: + - Name: Authentication + Value: 'bearer {{ "{{ .accessToken }}" }}' + - Name: X-Forwarded-User + Value: '{{ "{{ .claims.preferred_username }}" }}' + - Name: X-Auth-Request-Email + Value: '{{ "{{ .claims.email }}" }}' +EOF # Finally start CMD exec "$@" diff --git a/services/action.router b/services/action.router index 8cdc6b6..5f7db26 100644 --- a/services/action.router +++ b/services/action.router @@ -3,3 +3,5 @@ service: action entryPoints: - main + middlewares: + - oidc-auth diff --git a/services/autoupdate.router b/services/autoupdate.router index 19a3806..9aeda44 100644 --- a/services/autoupdate.router +++ b/services/autoupdate.router @@ -3,3 +3,5 @@ service: autoupdate entryPoints: - main + middlewares: + - oidc-auth diff --git a/services/client.router b/services/client.router index 5064e01..a962d0e 100644 --- a/services/client.router +++ b/services/client.router @@ -5,3 +5,5 @@ - main # Priority ensures this catch-all route is evaluated last priority: 1 + middlewares: + - oidc-auth diff --git a/services/icc.router b/services/icc.router index eb7ac0f..9ec790a 100644 --- a/services/icc.router +++ b/services/icc.router @@ -3,3 +3,5 @@ service: icc entryPoints: - main + middlewares: + - oidc-auth diff --git a/services/keycloak.router b/services/keycloak.router new file mode 100644 index 0000000..b5983d6 --- /dev/null +++ b/services/keycloak.router @@ -0,0 +1,5 @@ + keycloak: + rule: "PathPrefix(`/system/keycloak`) + service: keycloak + entryPoints: + - main diff --git a/services/keycloak.service b/services/keycloak.service new file mode 100644 index 0000000..6c8fc17 --- /dev/null +++ b/services/keycloak.service @@ -0,0 +1,6 @@ + keycloak: + loadBalancer: + servers: + - url: "http://${KEYCLOAK_HOST}:${KEYCLOAK_PORT}" + # Forward the original Host header to the backend service + passHostHeader: true diff --git a/services/media.router b/services/media.router index c428375..f44a71b 100644 --- a/services/media.router +++ b/services/media.router @@ -3,3 +3,5 @@ service: media entryPoints: - main + middlewares: + - oidc-auth diff --git a/services/presenter.router b/services/presenter.router index 62fd449..b9a9b64 100644 --- a/services/presenter.router +++ b/services/presenter.router @@ -3,3 +3,5 @@ service: presenter entryPoints: - main + middlewares: + - oidc-auth diff --git a/services/projector.router b/services/projector.router index 5e43d5c..7e94244 100644 --- a/services/projector.router +++ b/services/projector.router @@ -3,3 +3,5 @@ service: projector entryPoints: - main + middlewares: + - oidc-auth diff --git a/services/search.router b/services/search.router index d6af74c..bb0e24f 100644 --- a/services/search.router +++ b/services/search.router @@ -3,3 +3,5 @@ service: search entryPoints: - main + middlewares: + - oidc-auth diff --git a/services/vote.router b/services/vote.router index a357117..5c654eb 100644 --- a/services/vote.router +++ b/services/vote.router @@ -3,3 +3,5 @@ service: vote entryPoints: - main + middlewares: + - oidc-auth From c75883a4baa51d57b5d2d9381870361c0ff39938 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Tue, 21 Apr 2026 13:43:58 +0200 Subject: [PATCH 02/23] feat: Redirect to keycloak login --- entrypoint.sh | 34 ++++------------------------------ services/keycloak.router | 5 ----- services/keycloak.service | 6 ------ 3 files changed, 4 insertions(+), 41 deletions(-) delete mode 100644 services/keycloak.router delete mode 100644 services/keycloak.service diff --git a/entrypoint.sh b/entrypoint.sh index 5aba728..ef51aba 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -43,7 +43,8 @@ CLIENT_HOST="${CLIENT_HOST:-client}" CLIENT_PORT="${CLIENT_PORT:-9001}" KEYCLOAK_HOST="${KEYCLOAK_HOST:-keycloak-server}" KEYCLOAK_HOST_PORT="${KEYCLOAK_HOST_PORT:-8080}" -OIDC_KEYCLOAK_URL="${OIDC_KEYCLOAK_URL:-http://localhost:8080/realms/openslides}" +OIDC_KEYCLOAK_URL="${OIDC_KEYCLOAK_URL:-http://localhost:8000/auth/realms/openslides}" +OIDC_KEYCLOAK_URL_DOCKER="${OIDC_KEYCLOAK_URL_DOCKER:-http://keycloak-server:8080/realms/openslides}" OIDC_CLIENT_ID="${OIDC_CLIENT_ID:-proxy-client}" OIDC_CLIENT_SECRET="${OIDC_CLIENT_SECRET:-proxy-secret}" OIDC_SECRET="${OIDC_SECRET:-qvAcTGWBIGg7aWKCKRyUsTf33jK3lsmK}" @@ -193,14 +194,6 @@ for service in $SERVICES; do done # OIDC Middleware -cat >> "$DYNAMIC_CONFIG" << EOF - keycloak-server: - loadBalancer: - servers: - - url: "http://localhost:8080" - passHostHeader: true -EOF - echo "Enabling OIDC authentication middleware" cat >> "$DYNAMIC_CONFIG" << EOF @@ -208,33 +201,14 @@ echo "Enabling OIDC authentication middleware" oidc-auth: plugin: traefik-oidc-auth: - LogLevel: debug Secret: "${OIDC_SECRET}" Provider: - Url: "${OIDC_KEYCLOAK_URL}" + Url: "${OIDC_KEYCLOAK_URL_DOCKER}" ClientId: "${OIDC_CLIENT_ID}" ClientSecret: "${OIDC_CLIENT_SECRET}" - UsePkce: true ValidateIssuer: true ValidIssuer: "${OIDC_KEYCLOAK_URL}" - Scopes: - - openid - - profile - - email - LoginUri: /login - CallbackUri: /callback - LogoutUri: /logout - UnauthorizedBehavior: Challenge - SessionCookie: - SameSite: lax - HttpOnly: false - Headers: - - Name: Authentication - Value: 'bearer {{ "{{ .accessToken }}" }}' - - Name: X-Forwarded-User - Value: '{{ "{{ .claims.preferred_username }}" }}' - - Name: X-Auth-Request-Email - Value: '{{ "{{ .claims.email }}" }}' + Scopes: ["openid", "profile", "email"] EOF # Finally start CMD diff --git a/services/keycloak.router b/services/keycloak.router deleted file mode 100644 index b5983d6..0000000 --- a/services/keycloak.router +++ /dev/null @@ -1,5 +0,0 @@ - keycloak: - rule: "PathPrefix(`/system/keycloak`) - service: keycloak - entryPoints: - - main diff --git a/services/keycloak.service b/services/keycloak.service deleted file mode 100644 index 6c8fc17..0000000 --- a/services/keycloak.service +++ /dev/null @@ -1,6 +0,0 @@ - keycloak: - loadBalancer: - servers: - - url: "http://${KEYCLOAK_HOST}:${KEYCLOAK_PORT}" - # Forward the original Host header to the backend service - passHostHeader: true From 7dc1cb42078577341ce00d529688ba8bcb0cefff Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Tue, 21 Apr 2026 16:28:03 +0200 Subject: [PATCH 03/23] fix: Public issuer url is now equal to keycloaks known issuer url --- entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entrypoint.sh b/entrypoint.sh index ef51aba..84e8981 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -43,7 +43,7 @@ CLIENT_HOST="${CLIENT_HOST:-client}" CLIENT_PORT="${CLIENT_PORT:-9001}" KEYCLOAK_HOST="${KEYCLOAK_HOST:-keycloak-server}" KEYCLOAK_HOST_PORT="${KEYCLOAK_HOST_PORT:-8080}" -OIDC_KEYCLOAK_URL="${OIDC_KEYCLOAK_URL:-http://localhost:8000/auth/realms/openslides}" +OIDC_KEYCLOAK_URL="${OIDC_KEYCLOAK_URL:-http://localhost:8080/realms/openslides}" OIDC_KEYCLOAK_URL_DOCKER="${OIDC_KEYCLOAK_URL_DOCKER:-http://keycloak-server:8080/realms/openslides}" OIDC_CLIENT_ID="${OIDC_CLIENT_ID:-proxy-client}" OIDC_CLIENT_SECRET="${OIDC_CLIENT_SECRET:-proxy-secret}" From c13f28347d0ae79e0852b566f5778307d78118cb Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Tue, 2 Jun 2026 16:55:36 +0200 Subject: [PATCH 04/23] feat: Adding identity service. Expanding oidc provider configurations --- entrypoint.sh | 23 ++++++++++++++++++----- services/identity.router | 7 +++++++ services/identity.service | 6 ++++++ 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 services/identity.router create mode 100644 services/identity.service diff --git a/entrypoint.sh b/entrypoint.sh index 84e8981..86eea20 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -37,14 +37,16 @@ MEDIA_HOST="${MEDIA_HOST:-media}" MEDIA_PORT="${MEDIA_PORT:-9006}" MANAGE_HOST="${MANAGE_HOST:-manage}" MANAGE_PORT="${MANAGE_PORT:-9008}" +IDENTITY_HOST="${IDENTITY_HOST:-identity}" +IDENTITY_PORT="${IDENTITY_PORT:-9014}" VOTE_HOST="${VOTE_HOST:-vote}" VOTE_PORT="${VOTE_PORT:-9013}" CLIENT_HOST="${CLIENT_HOST:-client}" CLIENT_PORT="${CLIENT_PORT:-9001}" KEYCLOAK_HOST="${KEYCLOAK_HOST:-keycloak-server}" KEYCLOAK_HOST_PORT="${KEYCLOAK_HOST_PORT:-8080}" -OIDC_KEYCLOAK_URL="${OIDC_KEYCLOAK_URL:-http://localhost:8080/realms/openslides}" -OIDC_KEYCLOAK_URL_DOCKER="${OIDC_KEYCLOAK_URL_DOCKER:-http://keycloak-server:8080/realms/openslides}" +OIDC_ISSUER_URL="${OIDC_ISSUER_URL:-http://localhost:8080/realms/openslides}" +OIDC_ISSUER_URL_DOCKER="${OIDC_ISSUER_URL_DOCKER:-http://keycloak-server:8080/realms/openslides}" OIDC_CLIENT_ID="${OIDC_CLIENT_ID:-proxy-client}" OIDC_CLIENT_SECRET="${OIDC_CLIENT_SECRET:-proxy-secret}" OIDC_SECRET="${OIDC_SECRET:-qvAcTGWBIGg7aWKCKRyUsTf33jK3lsmK}" @@ -65,7 +67,7 @@ experimental: plugins: traefik-oidc-auth: moduleName: github.com/sevensolutions/traefik-oidc-auth - version: v0.19.0 + version: v0.20.0 EOF @@ -202,14 +204,25 @@ echo "Enabling OIDC authentication middleware" plugin: traefik-oidc-auth: Secret: "${OIDC_SECRET}" + LogLevel: DEBUG Provider: - Url: "${OIDC_KEYCLOAK_URL_DOCKER}" + Url: "${OIDC_ISSUER_URL_DOCKER}" ClientId: "${OIDC_CLIENT_ID}" ClientSecret: "${OIDC_CLIENT_SECRET}" ValidateIssuer: true - ValidIssuer: "${OIDC_KEYCLOAK_URL}" + ValidIssuer: "${OIDC_ISSUER_URL}" + UnauthorizedBehavior: Forward + BypassAuthenticationRule: "PathPrefix(\`/\`)" + LoginUri: "/system/login" + LogoutUri: "/system/logout" + Headers: + - Name: "Authorization" + Value: "{{\`Bearer: {{ .accessToken }}\`}}" + IncludeWhen: "Public" Scopes: ["openid", "profile", "email"] EOF +cat $DYNAMIC_CONFIG + # Finally start CMD exec "$@" diff --git a/services/identity.router b/services/identity.router new file mode 100644 index 0000000..a3478d0 --- /dev/null +++ b/services/identity.router @@ -0,0 +1,7 @@ + identity: + rule: "PathPrefix(`/system/identity`)" + service: identity + entryPoints: + - main + middlewares: + - oidc-auth diff --git a/services/identity.service b/services/identity.service new file mode 100644 index 0000000..09f8c08 --- /dev/null +++ b/services/identity.service @@ -0,0 +1,6 @@ + identity: + loadBalancer: + servers: + - url: "http://${IDENTITY_HOST}:${IDENTITY_PORT}" + # Forward the original Host header to the backend service + passHostHeader: true From 8c5d77bbce29ea4fbaa6f3363bfc4bf636b5b450 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 4 Jun 2026 15:01:10 +0200 Subject: [PATCH 05/23] feat: Created local-only plugin to append user-id header to traefik headers --- entrypoint.sh | 6 ++ .../openslides/user-id-header/.traefik.yaml | 5 ++ .../openslides/user-id-header/LICENSE | 21 +++++ .../openslides/user-id-header/go.mod | 5 ++ .../openslides/user-id-header/go.sum | 2 + .../openslides/user-id-header/plugin.go | 77 +++++++++++++++++++ services/action.router | 1 + services/autoupdate.router | 1 + services/client.router | 1 + services/icc.router | 1 + services/identity.router | 1 + services/media.router | 1 + services/presenter.router | 1 + services/projector.router | 1 + services/search.router | 1 + services/vote.router | 1 + 16 files changed, 126 insertions(+) create mode 100644 plugins-local/src/github.com/openslides/user-id-header/.traefik.yaml create mode 100644 plugins-local/src/github.com/openslides/user-id-header/LICENSE create mode 100644 plugins-local/src/github.com/openslides/user-id-header/go.mod create mode 100644 plugins-local/src/github.com/openslides/user-id-header/go.sum create mode 100644 plugins-local/src/github.com/openslides/user-id-header/plugin.go diff --git a/entrypoint.sh b/entrypoint.sh index 86eea20..627ebdd 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -68,6 +68,9 @@ experimental: traefik-oidc-auth: moduleName: github.com/sevensolutions/traefik-oidc-auth version: v0.20.0 + localPlugins: + user-id-header: + moduleName: github.com/openslides/user-id-header EOF @@ -220,6 +223,9 @@ echo "Enabling OIDC authentication middleware" Value: "{{\`Bearer: {{ .accessToken }}\`}}" IncludeWhen: "Public" Scopes: ["openid", "profile", "email"] + user-id: + plugin: + user-id-header: {} EOF cat $DYNAMIC_CONFIG diff --git a/plugins-local/src/github.com/openslides/user-id-header/.traefik.yaml b/plugins-local/src/github.com/openslides/user-id-header/.traefik.yaml new file mode 100644 index 0000000..01fc6d4 --- /dev/null +++ b/plugins-local/src/github.com/openslides/user-id-header/.traefik.yaml @@ -0,0 +1,5 @@ +displayName: User ID Header Insert +type: middleware +import: github.com/openslides/user-id-header +summary: Appends user id to headers +testData: {} diff --git a/plugins-local/src/github.com/openslides/user-id-header/LICENSE b/plugins-local/src/github.com/openslides/user-id-header/LICENSE new file mode 100644 index 0000000..da02d3d --- /dev/null +++ b/plugins-local/src/github.com/openslides/user-id-header/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Since 2011 Authors of OpenSlides, see https://github.com/OpenSlides/OpenSlides/blob/master/AUTHORS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins-local/src/github.com/openslides/user-id-header/go.mod b/plugins-local/src/github.com/openslides/user-id-header/go.mod new file mode 100644 index 0000000..fda9df0 --- /dev/null +++ b/plugins-local/src/github.com/openslides/user-id-header/go.mod @@ -0,0 +1,5 @@ +module github.com/openslides/user-id-header + +go 1.25.0 + +require github.com/golang-jwt/jwt/v4 v4.5.2 diff --git a/plugins-local/src/github.com/openslides/user-id-header/go.sum b/plugins-local/src/github.com/openslides/user-id-header/go.sum new file mode 100644 index 0000000..adbc53d --- /dev/null +++ b/plugins-local/src/github.com/openslides/user-id-header/go.sum @@ -0,0 +1,2 @@ +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= diff --git a/plugins-local/src/github.com/openslides/user-id-header/plugin.go b/plugins-local/src/github.com/openslides/user-id-header/plugin.go new file mode 100644 index 0000000..7433171 --- /dev/null +++ b/plugins-local/src/github.com/openslides/user-id-header/plugin.go @@ -0,0 +1,77 @@ +package myplugin + +import ( + "context" + "fmt" + "net/http" + "strings" + + "github.com/golang-jwt/jwt/v4" +) + +// Mandatory config struct +type Config struct { +} + +func CreateConfig() *Config { + return &Config{} +} + +type UserIDHeaderInsert struct { + next http.Handler + name string + sourceHeader string + targetHeader string +} + +// Create new plugin instance +func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) { + return &UserIDHeaderInsert{ + next: next, + name: name, + }, nil +} + +type payloadKeycloak struct { + jwt.RegisteredClaims + KeycloakID string `json:"sub"` + SessionID string `json:"sid"` // Keycloak session ID + Email string `json:"email"` + Username string `json:"preferred_username"` + ClientName string `json:"azp"` + OSUserID string `json:"os_id"` +} + +func extractUserID(r *http.Request) int { + header := r.Header.Get("Authorization") + encodedToken := strings.TrimPrefix(header, "Bearer: ") + + if header == encodedToken { + // No token. Handle the request as public access requst. + return 0 + } + + token, _, err := new(jwt.Parser).ParseUnverified(header, &payloadKeycloak{}) + if err != nil { + fmt.Println("parsing token: %w", err) + return 0 + } + + user_id, ok := token.Header["os_id"].(int) + if !ok { + fmt.Println("missing user id in token header") + return 0 + } + return user_id +} + +func (p *UserIDHeaderInsert) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Extract User ID + user_id := extractUserID(r) + + // Write it as a new header + r.Header.Set("X-User-ID", fmt.Sprint(user_id)) + + // Pass + p.next.ServeHTTP(w, r) +} diff --git a/services/action.router b/services/action.router index 5f7db26..4423e3b 100644 --- a/services/action.router +++ b/services/action.router @@ -5,3 +5,4 @@ - main middlewares: - oidc-auth + - user-id diff --git a/services/autoupdate.router b/services/autoupdate.router index 9aeda44..ddae1c9 100644 --- a/services/autoupdate.router +++ b/services/autoupdate.router @@ -5,3 +5,4 @@ - main middlewares: - oidc-auth + - user-id diff --git a/services/client.router b/services/client.router index a962d0e..63c501a 100644 --- a/services/client.router +++ b/services/client.router @@ -7,3 +7,4 @@ priority: 1 middlewares: - oidc-auth + - user-id diff --git a/services/icc.router b/services/icc.router index 9ec790a..67cd54e 100644 --- a/services/icc.router +++ b/services/icc.router @@ -5,3 +5,4 @@ - main middlewares: - oidc-auth + - user-id diff --git a/services/identity.router b/services/identity.router index a3478d0..19a8e13 100644 --- a/services/identity.router +++ b/services/identity.router @@ -5,3 +5,4 @@ - main middlewares: - oidc-auth + - user-id diff --git a/services/media.router b/services/media.router index f44a71b..567a417 100644 --- a/services/media.router +++ b/services/media.router @@ -5,3 +5,4 @@ - main middlewares: - oidc-auth + - user-id diff --git a/services/presenter.router b/services/presenter.router index b9a9b64..8610b88 100644 --- a/services/presenter.router +++ b/services/presenter.router @@ -5,3 +5,4 @@ - main middlewares: - oidc-auth + - user-id diff --git a/services/projector.router b/services/projector.router index 7e94244..93dc66b 100644 --- a/services/projector.router +++ b/services/projector.router @@ -5,3 +5,4 @@ - main middlewares: - oidc-auth + - user-id diff --git a/services/search.router b/services/search.router index bb0e24f..99cdf56 100644 --- a/services/search.router +++ b/services/search.router @@ -5,3 +5,4 @@ - main middlewares: - oidc-auth + - user-id diff --git a/services/vote.router b/services/vote.router index 5c654eb..b513a40 100644 --- a/services/vote.router +++ b/services/vote.router @@ -5,3 +5,4 @@ - main middlewares: - oidc-auth + - user-id From 72951c0b5e83befd568d3634cd044602af1b916b Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 4 Jun 2026 15:23:06 +0200 Subject: [PATCH 06/23] fix: Writing own JWT interpreter to avoid vendoring. Adding local plugin to docker image --- Dockerfile | 11 +++--- .../openslides/user-id-header/go.mod | 2 -- .../openslides/user-id-header/plugin.go | 36 ++++++++++--------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/Dockerfile b/Dockerfile index 126a006..56df51d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,14 +11,15 @@ ENV APP_CONTEXT=${CONTEXT} RUN apk add --no-cache curl gettext # Copy configuration files -COPY entrypoint.sh /entrypoint.sh -COPY certs /certs -COPY services /services -COPY templates /templates +COPY entrypoint.sh ./entrypoint.sh +COPY certs ./certs +COPY services ./services +COPY templates ./templates +COPY plugins-local ./plugins-local # Create dynamic config directory and make entrypoint executable RUN mkdir -p /etc/traefik/dynamic -RUN chmod +x /entrypoint.sh +RUN chmod +x ./entrypoint.sh # External Information LABEL org.opencontainers.image.title="OpenSlides Traefik Proxy" diff --git a/plugins-local/src/github.com/openslides/user-id-header/go.mod b/plugins-local/src/github.com/openslides/user-id-header/go.mod index fda9df0..33605d0 100644 --- a/plugins-local/src/github.com/openslides/user-id-header/go.mod +++ b/plugins-local/src/github.com/openslides/user-id-header/go.mod @@ -1,5 +1,3 @@ module github.com/openslides/user-id-header go 1.25.0 - -require github.com/golang-jwt/jwt/v4 v4.5.2 diff --git a/plugins-local/src/github.com/openslides/user-id-header/plugin.go b/plugins-local/src/github.com/openslides/user-id-header/plugin.go index 7433171..0173e31 100644 --- a/plugins-local/src/github.com/openslides/user-id-header/plugin.go +++ b/plugins-local/src/github.com/openslides/user-id-header/plugin.go @@ -2,11 +2,11 @@ package myplugin import ( "context" + "encoding/base64" + "encoding/json" "fmt" "net/http" "strings" - - "github.com/golang-jwt/jwt/v4" ) // Mandatory config struct @@ -32,16 +32,6 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h }, nil } -type payloadKeycloak struct { - jwt.RegisteredClaims - KeycloakID string `json:"sub"` - SessionID string `json:"sid"` // Keycloak session ID - Email string `json:"email"` - Username string `json:"preferred_username"` - ClientName string `json:"azp"` - OSUserID string `json:"os_id"` -} - func extractUserID(r *http.Request) int { header := r.Header.Get("Authorization") encodedToken := strings.TrimPrefix(header, "Bearer: ") @@ -51,18 +41,30 @@ func extractUserID(r *http.Request) int { return 0 } - token, _, err := new(jwt.Parser).ParseUnverified(header, &payloadKeycloak{}) + parts := strings.Split(encodedToken, ".") + if len(parts) != 3 { + fmt.Println("JWT partition not length 3") + return 0 + } + + payload, err := base64.RawURLEncoding.DecodeString(parts[1]) if err != nil { - fmt.Println("parsing token: %w", err) + fmt.Println("decoding token payload:", err) + return 0 + } + + var claims map[string]interface{} + if err := json.Unmarshal(payload, &claims); err != nil { + fmt.Println("parsing token claims:", err) return 0 } - user_id, ok := token.Header["os_id"].(int) + osID, ok := claims["os_id"].(float64) if !ok { - fmt.Println("missing user id in token header") + fmt.Println("missing or invalid os_id in token claims") return 0 } - return user_id + return int(osID) } func (p *UserIDHeaderInsert) ServeHTTP(w http.ResponseWriter, r *http.Request) { From 4c6f5cb19fbc16c2b4d882c14da9d03c695c5825 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 4 Jun 2026 15:39:30 +0200 Subject: [PATCH 07/23] fix: Move files in dockerfile again --- Dockerfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 56df51d..972e59a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,15 +11,15 @@ ENV APP_CONTEXT=${CONTEXT} RUN apk add --no-cache curl gettext # Copy configuration files -COPY entrypoint.sh ./entrypoint.sh -COPY certs ./certs -COPY services ./services -COPY templates ./templates -COPY plugins-local ./plugins-local +COPY entrypoint.sh /entrypoint.sh +COPY certs /certs +COPY services /services +COPY templates /templates +COPY plugins-local /plugins-local # Create dynamic config directory and make entrypoint executable RUN mkdir -p /etc/traefik/dynamic -RUN chmod +x ./entrypoint.sh +RUN chmod +x /entrypoint.sh # External Information LABEL org.opencontainers.image.title="OpenSlides Traefik Proxy" From c93e5b20dda051aa2aac5e4b518ab114ef99200d Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 4 Jun 2026 16:25:47 +0200 Subject: [PATCH 08/23] feat: Plugin now loaded correctly. User ID written into the response, making it available for client --- Dockerfile | 3 +- entrypoint.sh | 6 +-- .../openslides/user-id-header/go.mod | 3 -- .../openslides/user-id-header/go.sum | 2 - .../.traefik.yml} | 2 +- .../LICENSE | 0 .../openslides/user_id_header/go.mod | 3 ++ .../plugin.go | 53 +++++++++++++++---- 8 files changed, 51 insertions(+), 21 deletions(-) delete mode 100644 plugins-local/src/github.com/openslides/user-id-header/go.mod delete mode 100644 plugins-local/src/github.com/openslides/user-id-header/go.sum rename plugins-local/src/github.com/openslides/{user-id-header/.traefik.yaml => user_id_header/.traefik.yml} (69%) rename plugins-local/src/github.com/openslides/{user-id-header => user_id_header}/LICENSE (100%) create mode 100644 plugins-local/src/github.com/openslides/user_id_header/go.mod rename plugins-local/src/github.com/openslides/{user-id-header => user_id_header}/plugin.go (58%) diff --git a/Dockerfile b/Dockerfile index 972e59a..6c21ee5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ COPY entrypoint.sh /entrypoint.sh COPY certs /certs COPY services /services COPY templates /templates -COPY plugins-local /plugins-local +COPY plugins-local ./plugins-local # Create dynamic config directory and make entrypoint executable RUN mkdir -p /etc/traefik/dynamic @@ -57,6 +57,7 @@ FROM base AS prod RUN adduser -S -D -H appuser RUN chown -R appuser /app/ && \ chown -R appuser /etc/traefik/ && \ + chown -R appuser ./plugins-local/ && \ chown appuser /entrypoint.sh USER appuser diff --git a/entrypoint.sh b/entrypoint.sh index 627ebdd..b6bb57a 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -69,8 +69,8 @@ experimental: moduleName: github.com/sevensolutions/traefik-oidc-auth version: v0.20.0 localPlugins: - user-id-header: - moduleName: github.com/openslides/user-id-header + user_id_header: + moduleName: github.com/openslides/user_id_header EOF @@ -225,7 +225,7 @@ echo "Enabling OIDC authentication middleware" Scopes: ["openid", "profile", "email"] user-id: plugin: - user-id-header: {} + user_id_header: {} EOF cat $DYNAMIC_CONFIG diff --git a/plugins-local/src/github.com/openslides/user-id-header/go.mod b/plugins-local/src/github.com/openslides/user-id-header/go.mod deleted file mode 100644 index 33605d0..0000000 --- a/plugins-local/src/github.com/openslides/user-id-header/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/openslides/user-id-header - -go 1.25.0 diff --git a/plugins-local/src/github.com/openslides/user-id-header/go.sum b/plugins-local/src/github.com/openslides/user-id-header/go.sum deleted file mode 100644 index adbc53d..0000000 --- a/plugins-local/src/github.com/openslides/user-id-header/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= -github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= diff --git a/plugins-local/src/github.com/openslides/user-id-header/.traefik.yaml b/plugins-local/src/github.com/openslides/user_id_header/.traefik.yml similarity index 69% rename from plugins-local/src/github.com/openslides/user-id-header/.traefik.yaml rename to plugins-local/src/github.com/openslides/user_id_header/.traefik.yml index 01fc6d4..574767f 100644 --- a/plugins-local/src/github.com/openslides/user-id-header/.traefik.yaml +++ b/plugins-local/src/github.com/openslides/user_id_header/.traefik.yml @@ -1,5 +1,5 @@ displayName: User ID Header Insert type: middleware -import: github.com/openslides/user-id-header +import: github.com/openslides/user_id_header summary: Appends user id to headers testData: {} diff --git a/plugins-local/src/github.com/openslides/user-id-header/LICENSE b/plugins-local/src/github.com/openslides/user_id_header/LICENSE similarity index 100% rename from plugins-local/src/github.com/openslides/user-id-header/LICENSE rename to plugins-local/src/github.com/openslides/user_id_header/LICENSE diff --git a/plugins-local/src/github.com/openslides/user_id_header/go.mod b/plugins-local/src/github.com/openslides/user_id_header/go.mod new file mode 100644 index 0000000..292ece6 --- /dev/null +++ b/plugins-local/src/github.com/openslides/user_id_header/go.mod @@ -0,0 +1,3 @@ +module github.com/openslides/user_id_header + +go 1.23.0 diff --git a/plugins-local/src/github.com/openslides/user-id-header/plugin.go b/plugins-local/src/github.com/openslides/user_id_header/plugin.go similarity index 58% rename from plugins-local/src/github.com/openslides/user-id-header/plugin.go rename to plugins-local/src/github.com/openslides/user_id_header/plugin.go index 0173e31..f8583ae 100644 --- a/plugins-local/src/github.com/openslides/user-id-header/plugin.go +++ b/plugins-local/src/github.com/openslides/user_id_header/plugin.go @@ -1,4 +1,4 @@ -package myplugin +package user_id_header import ( "context" @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "net/http" + "strconv" "strings" ) @@ -18,10 +19,8 @@ func CreateConfig() *Config { } type UserIDHeaderInsert struct { - next http.Handler - name string - sourceHeader string - targetHeader string + next http.Handler + name string } // Create new plugin instance @@ -59,12 +58,37 @@ func extractUserID(r *http.Request) int { return 0 } - osID, ok := claims["os_id"].(float64) - if !ok { - fmt.Println("missing or invalid os_id in token claims") + rawOsID, exists := claims["os_id"] + if !exists { + fmt.Println("missing os_id in token claims") return 0 } - return int(osID) + + osID, err := strconv.Atoi(fmt.Sprintf("%v", rawOsID)) + if err != nil { + fmt.Println("invalid os_id value:", rawOsID) + return 0 + } + return osID +} + +type responseWriter struct { + http.ResponseWriter + userID string + headerSet bool +} + +func (w *responseWriter) WriteHeader(code int) { + w.Header().Set(userIDHeader, w.userID) + w.headerSet = true + w.ResponseWriter.WriteHeader(code) +} + +func (w *responseWriter) Write(b []byte) (int, error) { + if !w.headerSet { + w.Header().Set(userIDHeader, w.userID) + } + return w.ResponseWriter.Write(b) } func (p *UserIDHeaderInsert) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -72,8 +96,15 @@ func (p *UserIDHeaderInsert) ServeHTTP(w http.ResponseWriter, r *http.Request) { user_id := extractUserID(r) // Write it as a new header - r.Header.Set("X-User-ID", fmt.Sprint(user_id)) + r.Header.Set(userIDHeader, fmt.Sprint(user_id)) // Pass - p.next.ServeHTTP(w, r) + p.next.ServeHTTP(&responseWriter{ + ResponseWriter: w, + userID: fmt.Sprint(user_id), + }, r) } + +const ( + userIDHeader string = "X-User-ID" +) From a77760229dc0560a9d3dd7702f84e7afc7fb463c Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Fri, 5 Jun 2026 12:17:20 +0200 Subject: [PATCH 09/23] feat: Flusher --- .../src/github.com/openslides/user_id_header/plugin.go | 6 ++++++ services/client.router | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins-local/src/github.com/openslides/user_id_header/plugin.go b/plugins-local/src/github.com/openslides/user_id_header/plugin.go index f8583ae..225af8a 100644 --- a/plugins-local/src/github.com/openslides/user_id_header/plugin.go +++ b/plugins-local/src/github.com/openslides/user_id_header/plugin.go @@ -91,6 +91,12 @@ func (w *responseWriter) Write(b []byte) (int, error) { return w.ResponseWriter.Write(b) } +func (w *responseWriter) Flush() { + if f, ok := w.ResponseWriter.(http.Flusher); ok { + f.Flush() + } +} + func (p *UserIDHeaderInsert) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Extract User ID user_id := extractUserID(r) diff --git a/services/client.router b/services/client.router index 63c501a..a962d0e 100644 --- a/services/client.router +++ b/services/client.router @@ -7,4 +7,3 @@ priority: 1 middlewares: - oidc-auth - - user-id From 5ed181e2e51cdee705301a74983222d2d2c075e6 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Fri, 5 Jun 2026 12:22:13 +0200 Subject: [PATCH 10/23] feat: Adding additional debugging message --- plugins-local/src/github.com/openslides/user_id_header/plugin.go | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins-local/src/github.com/openslides/user_id_header/plugin.go b/plugins-local/src/github.com/openslides/user_id_header/plugin.go index 225af8a..03fbeea 100644 --- a/plugins-local/src/github.com/openslides/user_id_header/plugin.go +++ b/plugins-local/src/github.com/openslides/user_id_header/plugin.go @@ -37,6 +37,7 @@ func extractUserID(r *http.Request) int { if header == encodedToken { // No token. Handle the request as public access requst. + fmt.Println("No token") return 0 } From 590af32d219ef4ca797e47e24101490802941fda Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 11 Jun 2026 17:12:02 +0200 Subject: [PATCH 11/23] feat: It works --- .../github.com/openslides/user_id_header/plugin.go | 14 ++++++++------ services/auth.router | 3 +++ services/client.router | 1 + 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins-local/src/github.com/openslides/user_id_header/plugin.go b/plugins-local/src/github.com/openslides/user_id_header/plugin.go index 03fbeea..d0f0012 100644 --- a/plugins-local/src/github.com/openslides/user_id_header/plugin.go +++ b/plugins-local/src/github.com/openslides/user_id_header/plugin.go @@ -37,37 +37,37 @@ func extractUserID(r *http.Request) int { if header == encodedToken { // No token. Handle the request as public access requst. - fmt.Println("No token") + fmt.Println("header error: no token") return 0 } parts := strings.Split(encodedToken, ".") if len(parts) != 3 { - fmt.Println("JWT partition not length 3") + fmt.Println("header error: JWT partition not length 3") return 0 } payload, err := base64.RawURLEncoding.DecodeString(parts[1]) if err != nil { - fmt.Println("decoding token payload:", err) + fmt.Println("header error: decoding token payload:", err) return 0 } var claims map[string]interface{} if err := json.Unmarshal(payload, &claims); err != nil { - fmt.Println("parsing token claims:", err) + fmt.Println("header error: parsing token claims:", err) return 0 } rawOsID, exists := claims["os_id"] if !exists { - fmt.Println("missing os_id in token claims") + fmt.Println("header error: missing os_id in token claims") return 0 } osID, err := strconv.Atoi(fmt.Sprintf("%v", rawOsID)) if err != nil { - fmt.Println("invalid os_id value:", rawOsID) + fmt.Println("header error: invalid os_id value:", rawOsID) return 0 } return osID @@ -92,11 +92,13 @@ func (w *responseWriter) Write(b []byte) (int, error) { return w.ResponseWriter.Write(b) } +/* func (w *responseWriter) Flush() { if f, ok := w.ResponseWriter.(http.Flusher); ok { f.Flush() } } +*/ func (p *UserIDHeaderInsert) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Extract User ID diff --git a/services/auth.router b/services/auth.router index 0282964..4b2396d 100644 --- a/services/auth.router +++ b/services/auth.router @@ -3,3 +3,6 @@ service: auth entryPoints: - main + middlewares: + - oidc-auth + - user-id diff --git a/services/client.router b/services/client.router index a962d0e..63c501a 100644 --- a/services/client.router +++ b/services/client.router @@ -7,3 +7,4 @@ priority: 1 middlewares: - oidc-auth + - user-id From 3b2a641074e6c7293500cd34811ab9f06a73c633 Mon Sep 17 00:00:00 2001 From: Bastian Rihm Date: Fri, 12 Jun 2026 19:29:05 +0200 Subject: [PATCH 12/23] Fix http flush in user id middleware --- .../openslides/user_id_header/plugin.go | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/plugins-local/src/github.com/openslides/user_id_header/plugin.go b/plugins-local/src/github.com/openslides/user_id_header/plugin.go index d0f0012..cc3ea95 100644 --- a/plugins-local/src/github.com/openslides/user_id_header/plugin.go +++ b/plugins-local/src/github.com/openslides/user_id_header/plugin.go @@ -53,7 +53,7 @@ func extractUserID(r *http.Request) int { return 0 } - var claims map[string]interface{} + var claims map[string]any if err := json.Unmarshal(payload, &claims); err != nil { fmt.Println("header error: parsing token claims:", err) return 0 @@ -75,6 +75,7 @@ func extractUserID(r *http.Request) int { type responseWriter struct { http.ResponseWriter + flusher http.Flusher userID string headerSet bool } @@ -89,16 +90,20 @@ func (w *responseWriter) Write(b []byte) (int, error) { if !w.headerSet { w.Header().Set(userIDHeader, w.userID) } - return w.ResponseWriter.Write(b) + + n, err := w.ResponseWriter.Write(b) + if w.flusher != nil { + w.flusher.Flush() + } + + return n, err } -/* func (w *responseWriter) Flush() { - if f, ok := w.ResponseWriter.(http.Flusher); ok { - f.Flush() + if w.flusher != nil { + w.flusher.Flush() } } -*/ func (p *UserIDHeaderInsert) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Extract User ID @@ -107,8 +112,14 @@ func (p *UserIDHeaderInsert) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Write it as a new header r.Header.Set(userIDHeader, fmt.Sprint(user_id)) + var flusher http.Flusher + if f, ok := w.(http.Flusher); ok { + flusher = f + } + // Pass p.next.ServeHTTP(&responseWriter{ + flusher: flusher, ResponseWriter: w, userID: fmt.Sprint(user_id), }, r) From d301d9edc49f77fe934dcefd67eabeaa3ff8c96a Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 18 Jun 2026 16:24:05 +0200 Subject: [PATCH 13/23] feat: Access Token Plugin Attempt --- entrypoint.sh | 5 + .../access_token_blocklist/.traefik.yml | 5 + .../openslides/access_token_blocklist/LICENSE | 21 +++ .../openslides/access_token_blocklist/go.mod | 3 + .../access_token_blocklist/plugin.go | 151 ++++++++++++++++++ 5 files changed, 185 insertions(+) create mode 100644 plugins-local/src/github.com/openslides/access_token_blocklist/.traefik.yml create mode 100644 plugins-local/src/github.com/openslides/access_token_blocklist/LICENSE create mode 100644 plugins-local/src/github.com/openslides/access_token_blocklist/go.mod create mode 100644 plugins-local/src/github.com/openslides/access_token_blocklist/plugin.go diff --git a/entrypoint.sh b/entrypoint.sh index b6bb57a..8651c27 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -71,6 +71,8 @@ experimental: localPlugins: user_id_header: moduleName: github.com/openslides/user_id_header + access_token_blocklist: + moduleName: github.com/openslides/access_token_blocklist EOF @@ -226,6 +228,9 @@ echo "Enabling OIDC authentication middleware" user-id: plugin: user_id_header: {} + access-token-blocklist: + plugin: + access_token_blocklist: {} EOF cat $DYNAMIC_CONFIG diff --git a/plugins-local/src/github.com/openslides/access_token_blocklist/.traefik.yml b/plugins-local/src/github.com/openslides/access_token_blocklist/.traefik.yml new file mode 100644 index 0000000..33a0a1a --- /dev/null +++ b/plugins-local/src/github.com/openslides/access_token_blocklist/.traefik.yml @@ -0,0 +1,5 @@ +displayName: Access Token Blocklist +type: middleware +import: github.com/openslides/access_token_blocklist +summary: Invalidates access tokens linked to a logout call +testData: {} diff --git a/plugins-local/src/github.com/openslides/access_token_blocklist/LICENSE b/plugins-local/src/github.com/openslides/access_token_blocklist/LICENSE new file mode 100644 index 0000000..da02d3d --- /dev/null +++ b/plugins-local/src/github.com/openslides/access_token_blocklist/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Since 2011 Authors of OpenSlides, see https://github.com/OpenSlides/OpenSlides/blob/master/AUTHORS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins-local/src/github.com/openslides/access_token_blocklist/go.mod b/plugins-local/src/github.com/openslides/access_token_blocklist/go.mod new file mode 100644 index 0000000..22258c5 --- /dev/null +++ b/plugins-local/src/github.com/openslides/access_token_blocklist/go.mod @@ -0,0 +1,3 @@ +module github.com/openslides/access_token_blocklist + +go 1.23.0 diff --git a/plugins-local/src/github.com/openslides/access_token_blocklist/plugin.go b/plugins-local/src/github.com/openslides/access_token_blocklist/plugin.go new file mode 100644 index 0000000..bd2b2e3 --- /dev/null +++ b/plugins-local/src/github.com/openslides/access_token_blocklist/plugin.go @@ -0,0 +1,151 @@ +package access_token_blocklist + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "strings" +) + +// Mandatory config struct +type Config struct { +} + +func CreateConfig() *Config { + return &Config{} +} + +type AccessTokenBlocklist struct { + next http.Handler + name string + blockedSessionIDs map[string]int + id int +} + +// Create new plugin instance +func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) { + return &AccessTokenBlocklist{ + next: next, + name: name, + blockedSessionIDs: make(map[string]int), + }, nil +} + +type responseWriter struct { + http.ResponseWriter + flusher http.Flusher + userID string + headerSet bool +} + +func (w *responseWriter) WriteHeader(code int) { + w.Header().Set(userIDHeader, w.userID) + w.headerSet = true + w.ResponseWriter.WriteHeader(code) +} + +func (w *responseWriter) Write(b []byte) (int, error) { + if !w.headerSet { + w.Header().Set(userIDHeader, w.userID) + } + + n, err := w.ResponseWriter.Write(b) + if w.flusher != nil { + w.flusher.Flush() + } + + return n, err +} + +func (w *responseWriter) Flush() { + if w.flusher != nil { + w.flusher.Flush() + } +} + +func extractSessionID(r *http.Request) string { + header := r.Header.Get("Authorization") + encodedToken := strings.TrimPrefix(header, "Bearer: ") + + if header == encodedToken { + // No token. Handle the request as public access requst. + fmt.Println("header error: no token") + return "" + } + + parts := strings.Split(encodedToken, ".") + if len(parts) != 3 { + fmt.Println("header error: JWT partition not length 3") + return "" + } + + payload, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + fmt.Println("header error: decoding token payload:", err) + return "" + } + + var claims map[string]any + if err := json.Unmarshal(payload, &claims); err != nil { + fmt.Println("header error: parsing token claims:", err) + return "" + } + + sid, exists := claims["sid"] + if !exists { + fmt.Println("header error: missing sid in token claims") + return "" + } + + return sid.(string) +} + +func (a *AccessTokenBlocklist) blockSessionID(sessionID string) { + a.blockedSessionIDs[sessionID] = 1 +} + +func (a *AccessTokenBlocklist) isSessionIDBlocked(sessionID string) bool { + if _, exists := a.blockedSessionIDs[sessionID]; exists { + return true + } + return false +} + +func (a *AccessTokenBlocklist) ServeHTTP(w http.ResponseWriter, r *http.Request) { + sessionID := extractSessionID(r) + + if strings.HasSuffix(r.URL.Path, logoutRoute) { + a.blockSessionID(sessionID) + } + + var flusher http.Flusher + if f, ok := w.(http.Flusher); ok { + flusher = f + } + if a.isSessionIDBlocked(sessionID) { + // Write it as a new header + r.Header.Set(userIDHeader, "0") + + // Pass + a.next.ServeHTTP(&responseWriter{ + flusher: flusher, + ResponseWriter: w, + userID: "0", + }, r) + return + } + + // Pass + a.next.ServeHTTP(&responseWriter{ + flusher: flusher, + ResponseWriter: w, + }, r) + +} + +const ( + userIDHeader string = "X-User-ID" + logoutRoute string = "system/logout" +) From 5c5af38860f172538f76b566a70d09190b0c5c01 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Mon, 6 Jul 2026 15:48:31 +0200 Subject: [PATCH 14/23] feat: Blocklist Plugin. Identity Service Removal. PKCE enabled, Client Secret disabled --- entrypoint.sh | 6 +----- services/action.router | 1 + services/auth.router | 1 + services/client.router | 1 + services/icc.router | 1 + services/identity.router | 8 -------- services/identity.service | 6 ------ services/media.router | 1 + services/presenter.router | 1 + services/projector.router | 1 + services/search.router | 1 + services/vote.router | 1 + 12 files changed, 10 insertions(+), 19 deletions(-) delete mode 100644 services/identity.router delete mode 100644 services/identity.service diff --git a/entrypoint.sh b/entrypoint.sh index 8651c27..022fb91 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -48,7 +48,6 @@ KEYCLOAK_HOST_PORT="${KEYCLOAK_HOST_PORT:-8080}" OIDC_ISSUER_URL="${OIDC_ISSUER_URL:-http://localhost:8080/realms/openslides}" OIDC_ISSUER_URL_DOCKER="${OIDC_ISSUER_URL_DOCKER:-http://keycloak-server:8080/realms/openslides}" OIDC_CLIENT_ID="${OIDC_CLIENT_ID:-proxy-client}" -OIDC_CLIENT_SECRET="${OIDC_CLIENT_SECRET:-proxy-secret}" OIDC_SECRET="${OIDC_SECRET:-qvAcTGWBIGg7aWKCKRyUsTf33jK3lsmK}" @@ -213,7 +212,7 @@ echo "Enabling OIDC authentication middleware" Provider: Url: "${OIDC_ISSUER_URL_DOCKER}" ClientId: "${OIDC_CLIENT_ID}" - ClientSecret: "${OIDC_CLIENT_SECRET}" + UsePkce: true ValidateIssuer: true ValidIssuer: "${OIDC_ISSUER_URL}" UnauthorizedBehavior: Forward @@ -228,9 +227,6 @@ echo "Enabling OIDC authentication middleware" user-id: plugin: user_id_header: {} - access-token-blocklist: - plugin: - access_token_blocklist: {} EOF cat $DYNAMIC_CONFIG diff --git a/services/action.router b/services/action.router index 4423e3b..8ca65e1 100644 --- a/services/action.router +++ b/services/action.router @@ -6,3 +6,4 @@ middlewares: - oidc-auth - user-id + - access-token-blocklist diff --git a/services/auth.router b/services/auth.router index 4b2396d..57c7a3a 100644 --- a/services/auth.router +++ b/services/auth.router @@ -6,3 +6,4 @@ middlewares: - oidc-auth - user-id + - access-token-blocklist diff --git a/services/client.router b/services/client.router index 63c501a..e1f58bf 100644 --- a/services/client.router +++ b/services/client.router @@ -8,3 +8,4 @@ middlewares: - oidc-auth - user-id + - access-token-blocklist diff --git a/services/icc.router b/services/icc.router index 67cd54e..e197185 100644 --- a/services/icc.router +++ b/services/icc.router @@ -6,3 +6,4 @@ middlewares: - oidc-auth - user-id + - access-token-blocklist diff --git a/services/identity.router b/services/identity.router deleted file mode 100644 index 19a8e13..0000000 --- a/services/identity.router +++ /dev/null @@ -1,8 +0,0 @@ - identity: - rule: "PathPrefix(`/system/identity`)" - service: identity - entryPoints: - - main - middlewares: - - oidc-auth - - user-id diff --git a/services/identity.service b/services/identity.service deleted file mode 100644 index 09f8c08..0000000 --- a/services/identity.service +++ /dev/null @@ -1,6 +0,0 @@ - identity: - loadBalancer: - servers: - - url: "http://${IDENTITY_HOST}:${IDENTITY_PORT}" - # Forward the original Host header to the backend service - passHostHeader: true diff --git a/services/media.router b/services/media.router index 567a417..df7d524 100644 --- a/services/media.router +++ b/services/media.router @@ -6,3 +6,4 @@ middlewares: - oidc-auth - user-id + - access-token-blocklist diff --git a/services/presenter.router b/services/presenter.router index 8610b88..406170c 100644 --- a/services/presenter.router +++ b/services/presenter.router @@ -6,3 +6,4 @@ middlewares: - oidc-auth - user-id + - access-token-blocklist diff --git a/services/projector.router b/services/projector.router index 93dc66b..34f7206 100644 --- a/services/projector.router +++ b/services/projector.router @@ -6,3 +6,4 @@ middlewares: - oidc-auth - user-id + - access-token-blocklist diff --git a/services/search.router b/services/search.router index 99cdf56..9a40035 100644 --- a/services/search.router +++ b/services/search.router @@ -6,3 +6,4 @@ middlewares: - oidc-auth - user-id + - access-token-blocklist diff --git a/services/vote.router b/services/vote.router index b513a40..53b074a 100644 --- a/services/vote.router +++ b/services/vote.router @@ -6,3 +6,4 @@ middlewares: - oidc-auth - user-id + - access-token-blocklist From 749306c384429a3b5e396b34aef7d3aef099c234 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Mon, 6 Jul 2026 15:54:34 +0200 Subject: [PATCH 15/23] feat: Remove blocklist plugin until revisions were made. Change keycloak to zitadel --- entrypoint.sh | 10 +++++++--- services/action.router | 1 - services/auth.router | 1 - services/client.router | 1 - services/icc.router | 1 - services/media.router | 1 - services/presenter.router | 1 - services/projector.router | 1 - services/search.router | 1 - services/vote.router | 1 - 10 files changed, 7 insertions(+), 12 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 022fb91..406ea30 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -43,10 +43,10 @@ VOTE_HOST="${VOTE_HOST:-vote}" VOTE_PORT="${VOTE_PORT:-9013}" CLIENT_HOST="${CLIENT_HOST:-client}" CLIENT_PORT="${CLIENT_PORT:-9001}" -KEYCLOAK_HOST="${KEYCLOAK_HOST:-keycloak-server}" -KEYCLOAK_HOST_PORT="${KEYCLOAK_HOST_PORT:-8080}" +IDP_HOST="${IDP_HOST:-zitadel-api}" +IDP_HOST_PORT="${IDP_HOST_PORT:-8080}" OIDC_ISSUER_URL="${OIDC_ISSUER_URL:-http://localhost:8080/realms/openslides}" -OIDC_ISSUER_URL_DOCKER="${OIDC_ISSUER_URL_DOCKER:-http://keycloak-server:8080/realms/openslides}" +OIDC_ISSUER_URL_DOCKER="${OIDC_ISSUER_URL_DOCKER:-http://zitadel-api:8080/realms/openslides}" OIDC_CLIENT_ID="${OIDC_CLIENT_ID:-proxy-client}" OIDC_SECRET="${OIDC_SECRET:-qvAcTGWBIGg7aWKCKRyUsTf33jK3lsmK}" @@ -224,6 +224,10 @@ echo "Enabling OIDC authentication middleware" Value: "{{\`Bearer: {{ .accessToken }}\`}}" IncludeWhen: "Public" Scopes: ["openid", "profile", "email"] + Authorization: + AssertClaims: + - Name: roles + AnyOf: ["admin", "media"] user-id: plugin: user_id_header: {} diff --git a/services/action.router b/services/action.router index 8ca65e1..4423e3b 100644 --- a/services/action.router +++ b/services/action.router @@ -6,4 +6,3 @@ middlewares: - oidc-auth - user-id - - access-token-blocklist diff --git a/services/auth.router b/services/auth.router index 57c7a3a..4b2396d 100644 --- a/services/auth.router +++ b/services/auth.router @@ -6,4 +6,3 @@ middlewares: - oidc-auth - user-id - - access-token-blocklist diff --git a/services/client.router b/services/client.router index e1f58bf..63c501a 100644 --- a/services/client.router +++ b/services/client.router @@ -8,4 +8,3 @@ middlewares: - oidc-auth - user-id - - access-token-blocklist diff --git a/services/icc.router b/services/icc.router index e197185..67cd54e 100644 --- a/services/icc.router +++ b/services/icc.router @@ -6,4 +6,3 @@ middlewares: - oidc-auth - user-id - - access-token-blocklist diff --git a/services/media.router b/services/media.router index df7d524..567a417 100644 --- a/services/media.router +++ b/services/media.router @@ -6,4 +6,3 @@ middlewares: - oidc-auth - user-id - - access-token-blocklist diff --git a/services/presenter.router b/services/presenter.router index 406170c..8610b88 100644 --- a/services/presenter.router +++ b/services/presenter.router @@ -6,4 +6,3 @@ middlewares: - oidc-auth - user-id - - access-token-blocklist diff --git a/services/projector.router b/services/projector.router index 34f7206..93dc66b 100644 --- a/services/projector.router +++ b/services/projector.router @@ -6,4 +6,3 @@ middlewares: - oidc-auth - user-id - - access-token-blocklist diff --git a/services/search.router b/services/search.router index 9a40035..99cdf56 100644 --- a/services/search.router +++ b/services/search.router @@ -6,4 +6,3 @@ middlewares: - oidc-auth - user-id - - access-token-blocklist diff --git a/services/vote.router b/services/vote.router index 53b074a..b513a40 100644 --- a/services/vote.router +++ b/services/vote.router @@ -6,4 +6,3 @@ middlewares: - oidc-auth - user-id - - access-token-blocklist From 8eb03bd6b4ed8f0143e853e142356f420ca9b3da Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Tue, 7 Jul 2026 15:45:39 +0200 Subject: [PATCH 16/23] feat: Trying to fetch Client ID and Secret from API (unfinsihed) --- Dockerfile | 5 +- entrypoint.sh | 68 ++++++++++++- import-data.json | 257 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+), 6 deletions(-) create mode 100644 import-data.json diff --git a/Dockerfile b/Dockerfile index 6c21ee5..0bc38a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,14 +8,15 @@ WORKDIR /app ENV APP_CONTEXT=${CONTEXT} # curl for healthcheck, gettext for templating (envsubst) -RUN apk add --no-cache curl gettext +RUN apk add --no-cache curl gettext jq # Copy configuration files COPY entrypoint.sh /entrypoint.sh +COPY import-data.json /import-data.json COPY certs /certs COPY services /services COPY templates /templates -COPY plugins-local ./plugins-local +COPY plugins-local /plugins-local # Create dynamic config directory and make entrypoint executable RUN mkdir -p /etc/traefik/dynamic diff --git a/entrypoint.sh b/entrypoint.sh index 406ea30..5ca6218 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -45,16 +45,78 @@ CLIENT_HOST="${CLIENT_HOST:-client}" CLIENT_PORT="${CLIENT_PORT:-9001}" IDP_HOST="${IDP_HOST:-zitadel-api}" IDP_HOST_PORT="${IDP_HOST_PORT:-8080}" +INSTANCE_URL="${INSTANCE_URL:-http://localhost:8080}" OIDC_ISSUER_URL="${OIDC_ISSUER_URL:-http://localhost:8080/realms/openslides}" +OIDC_API_URL="${OIDC_ISSUER_URL_DOCKER:-http://zitadel-api:8080}" OIDC_ISSUER_URL_DOCKER="${OIDC_ISSUER_URL_DOCKER:-http://zitadel-api:8080/realms/openslides}" -OIDC_CLIENT_ID="${OIDC_CLIENT_ID:-proxy-client}" -OIDC_SECRET="${OIDC_SECRET:-qvAcTGWBIGg7aWKCKRyUsTf33jK3lsmK}" # ================================= # = Build static / install config = # ================================= +# Get Zitadel Client ID +OIDC_PAT_PATH="/zitadel/bootstrap/admin.pat" +OIDC_PAT="$(cat ${OIDC_PAT_PATH})" + +# echo $OIDC_PAT + +# Import Data + +#PAYLOAD=$(cat < "$TRAEFIK_CONFIG" @@ -70,8 +132,6 @@ experimental: localPlugins: user_id_header: moduleName: github.com/openslides/user_id_header - access_token_blocklist: - moduleName: github.com/openslides/access_token_blocklist EOF diff --git a/import-data.json b/import-data.json new file mode 100644 index 0000000..1a2a2d9 --- /dev/null +++ b/import-data.json @@ -0,0 +1,257 @@ +{ + "orgs":[ + { + "orgId":"378992503975641091", + "org":{ + "name":"ZITADEL" + }, + "projects":[ + { + "projectId":"378992503975706627", + "project":{ + "name":"ZITADEL" + } + } + ], + "apiApps":[ + { + "appId":"378992503975772163", + "app":{ + "projectId":"378992503975706627", + "name":"Management-API", + "authMethodType":"API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT" + } + }, + { + "appId":"378992503975837699", + "app":{ + "projectId":"378992503975706627", + "name":"Admin-API", + "authMethodType":"API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT" + } + }, + { + "appId":"378992503975903235", + "app":{ + "projectId":"378992503975706627", + "name":"Auth-API", + "authMethodType":"API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT" + } + } + ], + "oidcApps":[ + { + "appId":"378992503975968771", + "app":{ + "projectId":"378992503975706627", + "name":"Management Console", + "redirectUris":[ + "http://zitadel-v2nflc.localhost:8080/ui/console/auth/callback", + "http://localhost:8080/ui/console/auth/callback", + "http://zitadel-v2nflc.localhost:8081/ui/console/auth/callback", + "http://localhost:8081/ui/console/auth/callback" + ], + "responseTypes":[ + "OIDC_RESPONSE_TYPE_CODE" + ], + "grantTypes":[ + "OIDC_GRANT_TYPE_AUTHORIZATION_CODE" + ], + "appType":"OIDC_APP_TYPE_USER_AGENT", + "authMethodType":"OIDC_AUTH_METHOD_TYPE_NONE", + "postLogoutRedirectUris":[ + "http://zitadel-v2nflc.localhost:8080/ui/console/signedout", + "http://localhost:8080/ui/console/signedout", + "http://zitadel-v2nflc.localhost:8081/ui/console/signedout", + "http://localhost:8081/ui/console/signedout" + ], + "devMode":true, + "clockSkew":"0s" + } + } + ], + "humanUsers":[ + { + "userId":"378992503976165379", + "user":{ + "userName":"zitadel-admin@zitadel.localhost", + "profile":{ + "firstName":"ZITADEL", + "lastName":"Admin", + "displayName":"ZITADEL Admin", + "preferredLanguage":"en" + }, + "email":{ + "email":"zitadel-admin@zitadel.localhost", + "isEmailVerified":true + }, + "hashedPassword":{ + "value":"$2a$14$gp4cwqHn8z3/zU28.Qz4jOfU8yGhjpLNRC5qltD20DsYX9Skdyimm" + } + }, + "state":"USER_STATE_ACTIVE" + } + ], + "machineUsers":[ + { + "userId":"378992503976230915", + "user":{ + "userName":"login-client", + "name":"Automatically Initialized IAM_LOGIN_CLIENT" + }, + "state":"USER_STATE_ACTIVE" + } + ], + "orgMembers":[ + { + "userId":"378992503976165379", + "roles":[ + "ORG_OWNER" + ] + } + ], + "projectMembers":[ + { + "projectId":"380002477866483715", + "userId":"380004817734467587", + "roles":[ + "PROJECT_OWNER" + ] + } + ], + "domains":[ + { + "orgId":"378992503975641091", + "domainName":"zitadel.localhost", + "isVerified":true, + "isPrimary":true + } + ], + "orgState":"ORG_STATE_ACTIVE" + }, + { + "orgId":"380002437919932419", + "org":{ + "name":"OpenSlides" + }, + "loginPolicy":{ + "allowUsernamePassword":true, + "allowExternalIdp":true, + "passwordlessType":"PASSWORDLESS_TYPE_ALLOWED", + "passwordCheckLifetime":"864000s", + "externalLoginCheckLifetime":"864000s", + "mfaInitSkipLifetime":"2592000s", + "secondFactorCheckLifetime":"64800s", + "multiFactorCheckLifetime":"43200s", + "secondFactors":[ + "SECOND_FACTOR_TYPE_OTP", + "SECOND_FACTOR_TYPE_U2F" + ], + "multiFactors":[ + "MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION" + ] + }, + "privacyPolicy":{ + "docsLink":"https://zitadel.com/docs" + }, + "projects":[ + { + "projectId":"380002477866483715", + "project":{ + "name":"OpenSlides" + } + } + ], + "oidcApps":[ + { + "appId":"380008909747781635", + "app":{ + "projectId":"380002477866483715", + "name":"OpenSlides", + "redirectUris":[ + "/*" + ], + "responseTypes":[ + "OIDC_RESPONSE_TYPE_CODE" + ], + "grantTypes":[ + "OIDC_GRANT_TYPE_AUTHORIZATION_CODE" + ], + "authMethodType":"OIDC_AUTH_METHOD_TYPE_NONE", + "postLogoutRedirectUris":[ + "/*" + ], + "clockSkew":"0s" + } + } + ], + "humanUsers":[ + { + "userId":"380003472101081091", + "user":{ + "userName":"Hello", + "profile":{ + "firstName":"asa", + "lastName":"asas", + "displayName":"asa asas", + "preferredLanguage":"und" + }, + "email":{ + "email":"asdadda@asfaf.com" + } + }, + "state":"USER_STATE_INITIAL" + } + ], + "machineUsers":[ + { + "userId":"380004817734467587", + "user":{ + "userName":"Admin", + "name":"Admin" + }, + "state":"USER_STATE_ACTIVE" + } + ], + "orgMembers":[ + { + "userId":"378992503976165379", + "roles":[ + "ORG_OWNER" + ] + }, + { + "userId":"380004817734467587", + "roles":[ + "ORG_OWNER" + ] + } + ], + "projectMembers":[ + { + "projectId":"380002477866483715", + "userId":"380004817734467587", + "roles":[ + "PROJECT_OWNER" + ] + } + ], + "userMetadata":[ + { + "id":"380003472101081091", + "key":"os_id", + "value":"MA==" + } + ], + "domains":[ + { + "orgId":"380002437919932419", + "domainName":"openslides.localhost", + "isVerified":true, + "isPrimary":true + } + ], + "orgState":"ORG_STATE_ACTIVE" + } + ] +} From b06bf2836fb53da91f8c43bcc2fad45aab7396ec Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Thu, 9 Jul 2026 10:59:03 +0200 Subject: [PATCH 17/23] feat: Reading client id and secret from volume file --- entrypoint.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 5ca6218..121ab05 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -56,8 +56,9 @@ OIDC_ISSUER_URL_DOCKER="${OIDC_ISSUER_URL_DOCKER:-http://zitadel-api:8080/realms # ================================= # Get Zitadel Client ID -OIDC_PAT_PATH="/zitadel/bootstrap/admin.pat" -OIDC_PAT="$(cat ${OIDC_PAT_PATH})" +OIDC_PAT="$(cat /zitadel/bootstrap/admin.pat)" +OIDC_CLIENT_ID="$(cat /zitadel/bootstrap/client-id)" +OIDC_CLIENT_SECRET="$(cat /zitadel/bootstrap/client-secret)" # echo $OIDC_PAT @@ -267,7 +268,6 @@ echo "Enabling OIDC authentication middleware" oidc-auth: plugin: traefik-oidc-auth: - Secret: "${OIDC_SECRET}" LogLevel: DEBUG Provider: Url: "${OIDC_ISSUER_URL_DOCKER}" From fe6999d022843722e08410b647b1326a60c2db41 Mon Sep 17 00:00:00 2001 From: rrenkert Date: Thu, 9 Jul 2026 17:58:46 +0200 Subject: [PATCH 18/23] Add zitadel router and service --- entrypoint.sh | 11 ++++++----- services/idp.router | 10 ++++++++++ services/idp.service | 11 +++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 services/idp.router create mode 100644 services/idp.service diff --git a/entrypoint.sh b/entrypoint.sh index 121ab05..0bbd104 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -45,10 +45,10 @@ CLIENT_HOST="${CLIENT_HOST:-client}" CLIENT_PORT="${CLIENT_PORT:-9001}" IDP_HOST="${IDP_HOST:-zitadel-api}" IDP_HOST_PORT="${IDP_HOST_PORT:-8080}" -INSTANCE_URL="${INSTANCE_URL:-http://localhost:8080}" -OIDC_ISSUER_URL="${OIDC_ISSUER_URL:-http://localhost:8080/realms/openslides}" +INSTANCE_URL="${INSTANCE_URL:-https://localhost:8000}" +OIDC_ISSUER_URL="${OIDC_ISSUER_URL:-https://localhost:8000}" OIDC_API_URL="${OIDC_ISSUER_URL_DOCKER:-http://zitadel-api:8080}" -OIDC_ISSUER_URL_DOCKER="${OIDC_ISSUER_URL_DOCKER:-http://zitadel-api:8080/realms/openslides}" +OIDC_ISSUER_URL_DOCKER="${OIDC_ISSUER_URL_DOCKER:-http://zitadel-api:8080}" # ================================= @@ -270,14 +270,15 @@ echo "Enabling OIDC authentication middleware" traefik-oidc-auth: LogLevel: DEBUG Provider: - Url: "${OIDC_ISSUER_URL_DOCKER}" + Url: "${OIDC_ISSUER_URL}" ClientId: "${OIDC_CLIENT_ID}" UsePkce: true ValidateIssuer: true ValidIssuer: "${OIDC_ISSUER_URL}" + InsecureSkipVerify: true UnauthorizedBehavior: Forward BypassAuthenticationRule: "PathPrefix(\`/\`)" - LoginUri: "/system/login" + LoginUri: "/ui/v2/login/login" LogoutUri: "/system/logout" Headers: - Name: "Authorization" diff --git a/services/idp.router b/services/idp.router new file mode 100644 index 0000000..ddbfce3 --- /dev/null +++ b/services/idp.router @@ -0,0 +1,10 @@ + zitadel-api: + rule: "!PathPrefix(`/ui/v2/login`)" + service: zitadel-api + entryPoints: + - main + zitadel-login: + rule: "PathPrefix(`/ui/v2/login`)" + service: zitadel-login + entryPoints: + - main diff --git a/services/idp.service b/services/idp.service new file mode 100644 index 0000000..3a52905 --- /dev/null +++ b/services/idp.service @@ -0,0 +1,11 @@ + zitadel-api: + loadBalancer: + servers: + - url: "h2c://zitadel-api:8080" + # Forward the original Host header to the backend service + passHostHeader: true + zitadel-login: + loadBalancer: + servers: + - url: "http://zitadel-login:3000" + passHostHeader: true From 3e12770d820016d021114621e5c44527a8af460a Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Mon, 13 Jul 2026 13:10:05 +0200 Subject: [PATCH 19/23] feat: Environment Variable Update. Removing import data object --- Dockerfile | 1 - entrypoint.sh | 55 +++++----- import-data.json | 257 ----------------------------------------------- 3 files changed, 27 insertions(+), 286 deletions(-) delete mode 100644 import-data.json diff --git a/Dockerfile b/Dockerfile index 0bc38a6..19119e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,6 @@ RUN apk add --no-cache curl gettext jq # Copy configuration files COPY entrypoint.sh /entrypoint.sh -COPY import-data.json /import-data.json COPY certs /certs COPY services /services COPY templates /templates diff --git a/entrypoint.sh b/entrypoint.sh index 0bbd104..ca13b82 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -46,9 +46,8 @@ CLIENT_PORT="${CLIENT_PORT:-9001}" IDP_HOST="${IDP_HOST:-zitadel-api}" IDP_HOST_PORT="${IDP_HOST_PORT:-8080}" INSTANCE_URL="${INSTANCE_URL:-https://localhost:8000}" -OIDC_ISSUER_URL="${OIDC_ISSUER_URL:-https://localhost:8000}" -OIDC_API_URL="${OIDC_ISSUER_URL_DOCKER:-http://zitadel-api:8080}" -OIDC_ISSUER_URL_DOCKER="${OIDC_ISSUER_URL_DOCKER:-http://zitadel-api:8080}" +IDP_URL_EXTERNAL="${IDP_URL_EXTERNAL:-https://localhost:8000}" +IDP_URL_INTERNAL="${IDP_URL_INTERNAL:-http://zitadel-api:8080}" # ================================= @@ -56,11 +55,11 @@ OIDC_ISSUER_URL_DOCKER="${OIDC_ISSUER_URL_DOCKER:-http://zitadel-api:8080}" # ================================= # Get Zitadel Client ID -OIDC_PAT="$(cat /zitadel/bootstrap/admin.pat)" -OIDC_CLIENT_ID="$(cat /zitadel/bootstrap/client-id)" -OIDC_CLIENT_SECRET="$(cat /zitadel/bootstrap/client-secret)" +IDP_PAT="$(cat /zitadel/bootstrap/admin.pat)" +IDP_CLIENT_ID="$(cat /zitadel/bootstrap/client-id)" +IDP_CLIENT_SECRET="$(cat /zitadel/bootstrap/client-secret)" -# echo $OIDC_PAT +# echo $IDP_PAT # Import Data @@ -74,49 +73,49 @@ OIDC_CLIENT_SECRET="$(cat /zitadel/bootstrap/client-secret)" #echo $PAYLOAD -#RESPONSE=$(curl -X POST "$OIDC_API_URL/admin/v1/import" \ -# --header "Authorization: Bearer ${OIDC_PAT}" \ +#RESPONSE=$(curl -X POST "$IDP_API_URL/admin/v1/import" \ +# --header "Authorization: Bearer ${IDP_PAT}" \ # --header "Content-Type: application/json"\ # --header "Host: ${INSTANCE_URL}" \ # --data "${PAYLOAD}") #echo $PAYLOAD -#OIDC_APP_INFORMATION=$(curl -sS -X POST \ -# "${OIDC_API_URL}/zitadel.application.v2.ApplicationService/ListApplications" \ -# -H "Authorization: Bearer $OIDC_PAT" \ +#IDP_APP_INFORMATION=$(curl -sS -X POST \ +# "${IDP_API_URL}/zitadel.application.v2.ApplicationService/ListApplications" \ +# -H "Authorization: Bearer $IDP_PAT" \ # -H "Content-Type: application/json" \ # -H "Host: ${INSTANCE_URL}" \ # -d '{}') -#OIDC_CLIENT_ID="$(echo $OIDC_APP_INFORMATION | jq -r '.applications[0].oidcConfiguration.clientId')" -#OIDC_PROJECT_ID="$(echo $OIDC_APP_INFORMATION | jq -r '.applications[0].projectId')" -#OIDC_APPLICATION_ID="$(echo $OIDC_APP_INFORMATION | jq -r '.applications[0].applicationId')" +#IDP_CLIENT_ID="$(echo $IDP_APP_INFORMATION | jq -r '.applications[0].oidcConfiguration.clientId')" +#IDP_PROJECT_ID="$(echo $IDP_APP_INFORMATION | jq -r '.applications[0].projectId')" +#IDP_APPLICATION_ID="$(echo $IDP_APP_INFORMATION | jq -r '.applications[0].applicationId')" -#echo "CLIENT ID: --- $OIDC_CLIENT_ID" -#echo "Project ID: --- $OIDC_PROJECT_ID" -#echo "App ID: --- $OIDC_APPLICATION_ID" +#echo "CLIENT ID: --- $IDP_CLIENT_ID" +#echo "Project ID: --- $IDP_PROJECT_ID" +#echo "App ID: --- $IDP_APPLICATION_ID" -#if [ "$OIDC_CLIENT_ID" == "null" ] +#if [ "$IDP_CLIENT_ID" == "null" ] #then # echo "No client ID has been returned by zitadel" # echo "Response: $RESPONSE" # sleep infinity #fi -#OIDC_CLIENT_SECRET=$(curl -sS -X POST \ -# "${OIDC_API_URL}/zitadel.application.v2.ApplicationService/GenerateClientSecret" \ +#IDP_CLIENT_SECRET=$(curl -sS -X POST \ +# "${IDP_API_URL}/zitadel.application.v2.ApplicationService/GenerateClientSecret" \ # -H "Host: ${INSTANCE_URL}" \ -# -H "Authorization: Bearer $OIDC_PAT" \ +# -H "Authorization: Bearer $IDP_PAT" \ # -H "Content-Type: application/json" \ # -H "Connect-Protocol-Version: 1" \ # -d '{ -# \"projectId\": \"$OIDC_PROJECT_ID\", -# \"applicationId\": \"$OIDC_APPLICATION_ID\" +# \"projectId\": \"$IDP_PROJECT_ID\", +# \"applicationId\": \"$IDP_APPLICATION_ID\" # }' \ #| jq -r '.clientSecret') -#echo "CLIENT SECRET: --- $OIDC_CLIENT_SECRET" +#echo "CLIENT SECRET: --- $IDP_CLIENT_SECRET" # Generate base config from template envsubst < /templates/traefik.yml > "$TRAEFIK_CONFIG" @@ -270,11 +269,11 @@ echo "Enabling OIDC authentication middleware" traefik-oidc-auth: LogLevel: DEBUG Provider: - Url: "${OIDC_ISSUER_URL}" - ClientId: "${OIDC_CLIENT_ID}" + Url: "${IDP_URL_EXTERNAL}" + ClientId: "${IDP_CLIENT_ID}" UsePkce: true ValidateIssuer: true - ValidIssuer: "${OIDC_ISSUER_URL}" + ValidIssuer: "${IDP_URL_EXTERNAL}" InsecureSkipVerify: true UnauthorizedBehavior: Forward BypassAuthenticationRule: "PathPrefix(\`/\`)" diff --git a/import-data.json b/import-data.json deleted file mode 100644 index 1a2a2d9..0000000 --- a/import-data.json +++ /dev/null @@ -1,257 +0,0 @@ -{ - "orgs":[ - { - "orgId":"378992503975641091", - "org":{ - "name":"ZITADEL" - }, - "projects":[ - { - "projectId":"378992503975706627", - "project":{ - "name":"ZITADEL" - } - } - ], - "apiApps":[ - { - "appId":"378992503975772163", - "app":{ - "projectId":"378992503975706627", - "name":"Management-API", - "authMethodType":"API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT" - } - }, - { - "appId":"378992503975837699", - "app":{ - "projectId":"378992503975706627", - "name":"Admin-API", - "authMethodType":"API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT" - } - }, - { - "appId":"378992503975903235", - "app":{ - "projectId":"378992503975706627", - "name":"Auth-API", - "authMethodType":"API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT" - } - } - ], - "oidcApps":[ - { - "appId":"378992503975968771", - "app":{ - "projectId":"378992503975706627", - "name":"Management Console", - "redirectUris":[ - "http://zitadel-v2nflc.localhost:8080/ui/console/auth/callback", - "http://localhost:8080/ui/console/auth/callback", - "http://zitadel-v2nflc.localhost:8081/ui/console/auth/callback", - "http://localhost:8081/ui/console/auth/callback" - ], - "responseTypes":[ - "OIDC_RESPONSE_TYPE_CODE" - ], - "grantTypes":[ - "OIDC_GRANT_TYPE_AUTHORIZATION_CODE" - ], - "appType":"OIDC_APP_TYPE_USER_AGENT", - "authMethodType":"OIDC_AUTH_METHOD_TYPE_NONE", - "postLogoutRedirectUris":[ - "http://zitadel-v2nflc.localhost:8080/ui/console/signedout", - "http://localhost:8080/ui/console/signedout", - "http://zitadel-v2nflc.localhost:8081/ui/console/signedout", - "http://localhost:8081/ui/console/signedout" - ], - "devMode":true, - "clockSkew":"0s" - } - } - ], - "humanUsers":[ - { - "userId":"378992503976165379", - "user":{ - "userName":"zitadel-admin@zitadel.localhost", - "profile":{ - "firstName":"ZITADEL", - "lastName":"Admin", - "displayName":"ZITADEL Admin", - "preferredLanguage":"en" - }, - "email":{ - "email":"zitadel-admin@zitadel.localhost", - "isEmailVerified":true - }, - "hashedPassword":{ - "value":"$2a$14$gp4cwqHn8z3/zU28.Qz4jOfU8yGhjpLNRC5qltD20DsYX9Skdyimm" - } - }, - "state":"USER_STATE_ACTIVE" - } - ], - "machineUsers":[ - { - "userId":"378992503976230915", - "user":{ - "userName":"login-client", - "name":"Automatically Initialized IAM_LOGIN_CLIENT" - }, - "state":"USER_STATE_ACTIVE" - } - ], - "orgMembers":[ - { - "userId":"378992503976165379", - "roles":[ - "ORG_OWNER" - ] - } - ], - "projectMembers":[ - { - "projectId":"380002477866483715", - "userId":"380004817734467587", - "roles":[ - "PROJECT_OWNER" - ] - } - ], - "domains":[ - { - "orgId":"378992503975641091", - "domainName":"zitadel.localhost", - "isVerified":true, - "isPrimary":true - } - ], - "orgState":"ORG_STATE_ACTIVE" - }, - { - "orgId":"380002437919932419", - "org":{ - "name":"OpenSlides" - }, - "loginPolicy":{ - "allowUsernamePassword":true, - "allowExternalIdp":true, - "passwordlessType":"PASSWORDLESS_TYPE_ALLOWED", - "passwordCheckLifetime":"864000s", - "externalLoginCheckLifetime":"864000s", - "mfaInitSkipLifetime":"2592000s", - "secondFactorCheckLifetime":"64800s", - "multiFactorCheckLifetime":"43200s", - "secondFactors":[ - "SECOND_FACTOR_TYPE_OTP", - "SECOND_FACTOR_TYPE_U2F" - ], - "multiFactors":[ - "MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION" - ] - }, - "privacyPolicy":{ - "docsLink":"https://zitadel.com/docs" - }, - "projects":[ - { - "projectId":"380002477866483715", - "project":{ - "name":"OpenSlides" - } - } - ], - "oidcApps":[ - { - "appId":"380008909747781635", - "app":{ - "projectId":"380002477866483715", - "name":"OpenSlides", - "redirectUris":[ - "/*" - ], - "responseTypes":[ - "OIDC_RESPONSE_TYPE_CODE" - ], - "grantTypes":[ - "OIDC_GRANT_TYPE_AUTHORIZATION_CODE" - ], - "authMethodType":"OIDC_AUTH_METHOD_TYPE_NONE", - "postLogoutRedirectUris":[ - "/*" - ], - "clockSkew":"0s" - } - } - ], - "humanUsers":[ - { - "userId":"380003472101081091", - "user":{ - "userName":"Hello", - "profile":{ - "firstName":"asa", - "lastName":"asas", - "displayName":"asa asas", - "preferredLanguage":"und" - }, - "email":{ - "email":"asdadda@asfaf.com" - } - }, - "state":"USER_STATE_INITIAL" - } - ], - "machineUsers":[ - { - "userId":"380004817734467587", - "user":{ - "userName":"Admin", - "name":"Admin" - }, - "state":"USER_STATE_ACTIVE" - } - ], - "orgMembers":[ - { - "userId":"378992503976165379", - "roles":[ - "ORG_OWNER" - ] - }, - { - "userId":"380004817734467587", - "roles":[ - "ORG_OWNER" - ] - } - ], - "projectMembers":[ - { - "projectId":"380002477866483715", - "userId":"380004817734467587", - "roles":[ - "PROJECT_OWNER" - ] - } - ], - "userMetadata":[ - { - "id":"380003472101081091", - "key":"os_id", - "value":"MA==" - } - ], - "domains":[ - { - "orgId":"380002437919932419", - "domainName":"openslides.localhost", - "isVerified":true, - "isPrimary":true - } - ], - "orgState":"ORG_STATE_ACTIVE" - } - ] -} From 5c4566eee7440e876b65f8cdf7861f91a3836b51 Mon Sep 17 00:00:00 2001 From: rrenkert Date: Tue, 14 Jul 2026 15:24:13 +0200 Subject: [PATCH 20/23] Add config for separate proxy for zitadel --- entrypoint.sh | 25 ++++++++++++++++++++++++- services/auth.router | 8 -------- services/auth.service | 6 ------ services/idp.router | 4 ++-- 4 files changed, 26 insertions(+), 17 deletions(-) delete mode 100644 services/auth.router delete mode 100644 services/auth.service diff --git a/entrypoint.sh b/entrypoint.sh index ca13b82..4345fbf 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -46,7 +46,7 @@ CLIENT_PORT="${CLIENT_PORT:-9001}" IDP_HOST="${IDP_HOST:-zitadel-api}" IDP_HOST_PORT="${IDP_HOST_PORT:-8080}" INSTANCE_URL="${INSTANCE_URL:-https://localhost:8000}" -IDP_URL_EXTERNAL="${IDP_URL_EXTERNAL:-https://localhost:8000}" +IDP_URL_EXTERNAL="${IDP_URL_EXTERNAL:-https://localhost:8800}" IDP_URL_INTERNAL="${IDP_URL_INTERNAL:-http://zitadel-api:8080}" @@ -167,6 +167,29 @@ elif [ -n "$ENABLE_AUTO_HTTPS" ]; then domains: - main: ${EXTERNAL_ADDRESS} certResolver: acmeResolver +EOF +fi + +# Add entryPoints in accordance to HTTPS related variables +cat >> "$TRAEFIK_CONFIG" << 'EOF' + idp: + address: ":8800" + http: +EOF + +if [ -n "$ENABLE_LOCAL_HTTPS" ]; then + # Define tls property, which will cause all routers to terminate TLS and + # foward decrypted traffic. + cat >> "$TRAEFIK_CONFIG" << 'EOF' + tls: {} +EOF +elif [ -n "$ENABLE_AUTO_HTTPS" ]; then + # Also needs tls property, but with additional information for cert retrieval + cat >> "$TRAEFIK_CONFIG" << EOF + tls: + domains: + - idp: ${IDP_EXTERNAL_ADDRESS} + certResolver: acmeResolver EOF # Additionally a plain HTTP endpoint to answer ACME challenges on must be # configured diff --git a/services/auth.router b/services/auth.router deleted file mode 100644 index 4b2396d..0000000 --- a/services/auth.router +++ /dev/null @@ -1,8 +0,0 @@ - auth: - rule: "PathPrefix(`/system/auth`) || PathPrefix(`/system/saml`)" - service: auth - entryPoints: - - main - middlewares: - - oidc-auth - - user-id diff --git a/services/auth.service b/services/auth.service deleted file mode 100644 index 6f9cda6..0000000 --- a/services/auth.service +++ /dev/null @@ -1,6 +0,0 @@ - auth: - loadBalancer: - servers: - - url: "http://${AUTH_HOST}:${AUTH_PORT}" - # Forward the original Host header to the backend service - passHostHeader: true diff --git a/services/idp.router b/services/idp.router index ddbfce3..ffaf64f 100644 --- a/services/idp.router +++ b/services/idp.router @@ -2,9 +2,9 @@ rule: "!PathPrefix(`/ui/v2/login`)" service: zitadel-api entryPoints: - - main + - idp zitadel-login: rule: "PathPrefix(`/ui/v2/login`)" service: zitadel-login entryPoints: - - main + - idp From ffbdf865c2c99f52c427ddc2e697ff4a9215a025 Mon Sep 17 00:00:00 2001 From: rrenkert Date: Fri, 17 Jul 2026 09:24:16 +0200 Subject: [PATCH 21/23] Try an additional middleware to fight against cors in redirects --- entrypoint.sh | 25 +++++++++++++++++++++++-- services/idp.router | 4 ++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 4345fbf..b36ed02 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -287,6 +287,27 @@ echo "Enabling OIDC authentication middleware" cat >> "$DYNAMIC_CONFIG" << EOF middlewares: + zitadel-cors: + headers: + accessControlAllowMethods: + - GET + - POST + - PUT + - DELETE + - OPTIONS + - PATCH + accessControlAllowHeaders: + - Authorization + - Content-Type + - X-CSRF-Token + - Accept + - Origin + - Link + accessControlAllowOriginList: + - "https://localhost:8000" + accessControlMaxAge: 600 + addVaryHeader: true + accessControlAllowCredentials: true oidc-auth: plugin: traefik-oidc-auth: @@ -299,8 +320,8 @@ echo "Enabling OIDC authentication middleware" ValidIssuer: "${IDP_URL_EXTERNAL}" InsecureSkipVerify: true UnauthorizedBehavior: Forward - BypassAuthenticationRule: "PathPrefix(\`/\`)" - LoginUri: "/ui/v2/login/login" + LoginUri: "/login" + CallbackUri: "https://localhost:8000/" LogoutUri: "/system/logout" Headers: - Name: "Authorization" diff --git a/services/idp.router b/services/idp.router index ffaf64f..03a4ec7 100644 --- a/services/idp.router +++ b/services/idp.router @@ -3,8 +3,12 @@ service: zitadel-api entryPoints: - idp + middlewares: + - zitadel-cors zitadel-login: rule: "PathPrefix(`/ui/v2/login`)" service: zitadel-login entryPoints: - idp + middlewares: + - zitadel-cors From 3213645149f5099df8c8de743107914330000016 Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Fri, 17 Jul 2026 16:22:26 +0200 Subject: [PATCH 22/23] fix: Readd auth to prevent client issues. Generalize Login host and port. Update Cert Generation. Fixing some env assignments --- entrypoint.sh | 10 +++++----- make-localhost-cert.sh | 1 + services/auth.router | 8 ++++++++ services/auth.service | 6 ++++++ services/idp.service | 4 ++-- 5 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 services/auth.router create mode 100644 services/auth.service diff --git a/entrypoint.sh b/entrypoint.sh index b36ed02..0e6ef97 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -15,6 +15,7 @@ HTTPS_CERT_FILE="${HTTPS_CERT_FILE:-/certs/cert.pem}" HTTPS_KEY_FILE="${HTTPS_KEY_FILE:-/certs/key.pem}" ENABLE_AUTO_HTTPS="${ENABLE_AUTO_HTTPS:-}" EXTERNAL_ADDRESS="${EXTERNAL_ADDRESS:-openslides.example.com}" +EXTERNAL_ADDRESS_IDP="${EXTERNAL_ADDRESS_IDP:-https://localhost:8800}" ACME_ENDPOINT="${ACME_ENDPOINT:-}" ACME_EMAIL="${ACME_EMAIL:-}" @@ -45,6 +46,8 @@ CLIENT_HOST="${CLIENT_HOST:-client}" CLIENT_PORT="${CLIENT_PORT:-9001}" IDP_HOST="${IDP_HOST:-zitadel-api}" IDP_HOST_PORT="${IDP_HOST_PORT:-8080}" +IDP_LOGIN_HOST="${IDP_HOST:-zitadel-login}" +IDP_LOGIN_HOST_PORT="${IDP_HOST_PORT:-3000}" INSTANCE_URL="${INSTANCE_URL:-https://localhost:8000}" IDP_URL_EXTERNAL="${IDP_URL_EXTERNAL:-https://localhost:8800}" IDP_URL_INTERNAL="${IDP_URL_INTERNAL:-http://zitadel-api:8080}" @@ -188,7 +191,7 @@ elif [ -n "$ENABLE_AUTO_HTTPS" ]; then cat >> "$TRAEFIK_CONFIG" << EOF tls: domains: - - idp: ${IDP_EXTERNAL_ADDRESS} + - idp: ${EXTERNAL_ADDRESS_IDP} certResolver: acmeResolver EOF # Additionally a plain HTTP endpoint to answer ACME challenges on must be @@ -313,15 +316,12 @@ echo "Enabling OIDC authentication middleware" traefik-oidc-auth: LogLevel: DEBUG Provider: - Url: "${IDP_URL_EXTERNAL}" + Url: "${IDP_URL_INTERNAL}" ClientId: "${IDP_CLIENT_ID}" UsePkce: true - ValidateIssuer: true - ValidIssuer: "${IDP_URL_EXTERNAL}" InsecureSkipVerify: true UnauthorizedBehavior: Forward LoginUri: "/login" - CallbackUri: "https://localhost:8000/" LogoutUri: "/system/logout" Headers: - Name: "Authorization" diff --git a/make-localhost-cert.sh b/make-localhost-cert.sh index 54a74e3..1fb0213 100755 --- a/make-localhost-cert.sh +++ b/make-localhost-cert.sh @@ -39,6 +39,7 @@ elif type openssl >/dev/null 2>&1; then openssl req -x509 -newkey rsa:4096 -nodes -days 3650 \ -subj "/C=DE/O=Selfsigned Test/CN=localhost" \ + -addext "subjectAltName=DNS:localhost,DNS:*.localhost,DNS:localhost.localdomain,DNS:*.localhost.localdomain,IP:127.0.0.1,IP:::1" \ -keyout certs/key.pem -out certs/cert.pem echo "Self-signed certificate created with openssl" diff --git a/services/auth.router b/services/auth.router new file mode 100644 index 0000000..4b2396d --- /dev/null +++ b/services/auth.router @@ -0,0 +1,8 @@ + auth: + rule: "PathPrefix(`/system/auth`) || PathPrefix(`/system/saml`)" + service: auth + entryPoints: + - main + middlewares: + - oidc-auth + - user-id diff --git a/services/auth.service b/services/auth.service new file mode 100644 index 0000000..6f9cda6 --- /dev/null +++ b/services/auth.service @@ -0,0 +1,6 @@ + auth: + loadBalancer: + servers: + - url: "http://${AUTH_HOST}:${AUTH_PORT}" + # Forward the original Host header to the backend service + passHostHeader: true diff --git a/services/idp.service b/services/idp.service index 3a52905..e9542b0 100644 --- a/services/idp.service +++ b/services/idp.service @@ -1,11 +1,11 @@ zitadel-api: loadBalancer: servers: - - url: "h2c://zitadel-api:8080" + - url: "h2c://${IDP_HOST}:${IDP_HOST_PORT}" # Forward the original Host header to the backend service passHostHeader: true zitadel-login: loadBalancer: servers: - - url: "http://zitadel-login:3000" + - url: "http://${IDP_LOGIN_HOST}:${IDP_LOGIN_HOST_PORT}" passHostHeader: true From 428fd68fafe0e7138ec3fd304ca46ac8fe90024b Mon Sep 17 00:00:00 2001 From: Jan Malte Behrens Date: Fri, 17 Jul 2026 16:43:52 +0200 Subject: [PATCH 23/23] fix: Switch internal with external url - proxying works now --- entrypoint.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/entrypoint.sh b/entrypoint.sh index 0e6ef97..3c8ef7a 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -316,9 +316,11 @@ echo "Enabling OIDC authentication middleware" traefik-oidc-auth: LogLevel: DEBUG Provider: - Url: "${IDP_URL_INTERNAL}" + Url: "${IDP_URL_EXTERNAL}" ClientId: "${IDP_CLIENT_ID}" UsePkce: true + ValidateIssuer: true + ValidIssuer: "${IDP_URL_INTERNAL}" InsecureSkipVerify: true UnauthorizedBehavior: Forward LoginUri: "/login"