feat(traefik-forward-auth): serve several domains and portals from one instance

A session cookie scoped to one registrable domain is never sent to a host under
another, so an app on a second domain could not be protected by this instance at
all. It failed with a 500 naming the cause: "return URL host does not match any
configured cookie domain".

`domains` is now a list of {domain, authHost}, matching the upstream
`server.domains` schema. The chart previously emitted `server.hostname` and
`cookies.domain`, which are not in 4.14.1's documented options — a deprecated
form that still worked. The single-domain values remain as shorthand and fold
into one entry, so an existing release renders the same protection it did before.

`portals` is a list too. A portal is one OAuth2 client, chosen by the middleware
address, so two portals means two Pocket ID applications — which is how access
can be separated per domain rather than merely shared across them. Each portal
gets its own Middleware and its own mounted secret, under
/var/run/secrets/traefik-forward-auth/<portal>/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
sha
2026-08-18 00:10:40 +03:00
co-authored by Claude Opus 5
parent 308b164894
commit cf2aebf298
6 changed files with 130 additions and 25 deletions
+33 -6
View File
@@ -4,18 +4,45 @@ image:
# Overrides the image tag; defaults to Chart.AppVersion
tag: ""
# TFA server hostname (used for OIDC redirects). E.g. "auth.example.com"
hostname: ""
# Domains served by this instance. One entry per registrable domain.
#
# `domain` scopes the session cookie; `authHost` is where this service is
# reachable for that domain, and must be it or a sub-domain of it.
#
# A browser will not send a cookie scoped to one registrable domain to a host
# under another, so an app on a second domain needs its own entry here. A second
# OAuth2 client alone does not solve it.
domains: []
# - domain: example.com
# authHost: auth.example.com
# - domain: example.org
# authHost: auth.example.org
# Cookie domain — scope at which the session cookie is valid.
# Must be a parent of `hostname` (e.g. "example.com" for "auth.example.com").
# Single-domain shorthand, used when `domains` is empty. Equivalent to one
# `domains` entry of {domain: cookieDomain, authHost: hostname}.
hostname: ""
cookieDomain: ""
tokens:
sessionLifetime: 24h
# Pocket ID OIDC configuration. `clientID` is a public identifier;
# `clientSecret` is read from `existingSecret` (key: `client-secret`).
# Portals. Each is one OAuth2 client, selected by the middleware address
# (/portals/<name>) — which is how access is separated: grant one Pocket ID
# application to one group of people and the other to another, and each portal
# admits only its own.
#
# Each portal mounts its secret at
# /var/run/secrets/traefik-forward-auth/<name>/client-secret.
portals: []
# - name: main
# middlewareName: pocket-id-auth # default: <release>-<name>
# existingSecret: tfa-main-secret # must define key `client-secret`
# secretKey: client-secret # optional, if the key differs
# pocketID:
# endpoint: https://id.example.com
# clientID: "..."
# Single-portal shorthand, used when `portals` is empty.
pocketID:
endpoint: ""
clientID: ""