feat(services): multi-domain routing, split-horizon and OIDC hardening
Bundle of cross-role changes for the gymb services deployment: - Traefik routers: OR-combine opnform/homarr/bookstack Host rules with new *_extra_domains (internal *.int.* FQDNs for a DMZ reverseproxy), and emit tls.certresolver only when traefik_cert_mode == acme (drawio, homarr, opnform, send). - Split-horizon: bookstack_extra_hosts / opnform_extra_hosts add container /etc/hosts overrides so containers reach the IdP public FQDN over the LAN. - bookstack: assert the OIDC issuer resolves concretely (reject "//v2.0"), allowing non-Entra IdPs that override bookstack_oidc_issuer. - homarr: derive the bcrypt salt from the password digest so the admin hash is idempotent — no spurious template changes / container restarts. - opnform: PATCH an existing OIDC connection instead of skipping (applies corrected inventory on re-run); add OIDC_FORCE_LOGIN (enabled only after bootstrap) and an optional direct-SSO ingress entrypoint. Docs: READMEs and meta/argument_specs.yml updated for all new variables.
This commit is contained in:
parent
bb64ccf71e
commit
518d80ec71
17 changed files with 309 additions and 37 deletions
|
|
@ -11,9 +11,10 @@ Docker Compose stack behind Traefik.
|
|||
- Integrates the ingress container with an existing Traefik proxy network
|
||||
- Waits for the API container to become healthy before returning
|
||||
|
||||
## What this role does NOT do (stage 1)
|
||||
## What this role does NOT do
|
||||
|
||||
- Does not pre-configure OIDC / identity_connections — set up via Admin UI
|
||||
- Does not migrate existing OpnForm databases — only bootstraps fresh
|
||||
installs (admin registration + OIDC connection are idempotent)
|
||||
|
||||
## Architecture note: why two reverse proxies?
|
||||
|
||||
|
|
@ -91,11 +92,14 @@ Leave `opnform_admin_email` / `opnform_admin_password` empty. Visit
|
|||
|
||||
## OIDC setup
|
||||
|
||||
Set `opnform_oidc_enabled: true` and the role creates an
|
||||
Set `opnform_oidc_enabled: true` and the role provisions an
|
||||
IdentityConnection on the admin's default workspace via
|
||||
`POST /api/open/workspaces/{id}/oidc-connections`. OpnForm enforces a
|
||||
single OIDC connection per workspace, so the task is idempotent (GETs
|
||||
existing connections first and skips if any exist).
|
||||
single OIDC connection per workspace, so the task is idempotent: it GETs
|
||||
existing connections first, then either POSTs a new one or PATCHes the
|
||||
existing one to the desired state. PATCHing (rather than skipping when
|
||||
one exists) keeps inventory changes — e.g. a corrected issuer — applied
|
||||
on re-runs instead of leaving stale values in the DB.
|
||||
|
||||
**Prerequisite**: the admin bootstrap must be configured
|
||||
(`opnform_admin_email` + `opnform_admin_password`). The OIDC API
|
||||
|
|
@ -138,6 +142,44 @@ opnform_oidc_admin_group: "opnform-admins" # mapped to role=admin
|
|||
|
||||
Valid roles: `owner`, `admin`, `editor`, `member`.
|
||||
|
||||
### Force OIDC-only login
|
||||
|
||||
```yaml
|
||||
opnform_oidc_force_login: true # default false
|
||||
```
|
||||
|
||||
Sets `OIDC_FORCE_LOGIN=true` on the API: password login is disabled and
|
||||
every user must authenticate via OIDC. The role keeps force-login **off**
|
||||
during the first deploy (the admin/OIDC bootstrap is password-based) and
|
||||
switches it on only after the OIDC connection is provisioned, recreating
|
||||
the API containers. Ensure all real users have addresses under
|
||||
`opnform_oidc_domain` before enabling — there is no password fallback.
|
||||
|
||||
### Direct-SSO entrypoint
|
||||
|
||||
OpnForm has no native way to skip the email login form and jump straight
|
||||
to the IdP. When enabled, the ingress serves a tiny redirect page that
|
||||
calls `/api/auth/{slug}/redirect` (no domain check) and forwards the
|
||||
browser to the IdP authorize URL.
|
||||
|
||||
```yaml
|
||||
opnform_oidc_sso_entrypoint: true # default false
|
||||
opnform_oidc_sso_path: "/sso" # link users to https://<domain>/sso
|
||||
```
|
||||
|
||||
## Networking / split-horizon
|
||||
|
||||
```yaml
|
||||
opnform_extra_domains: [] # extra Host-rule hostnames (OR-combined)
|
||||
opnform_extra_hosts: [] # API container /etc/hosts overrides ("host:ip")
|
||||
```
|
||||
|
||||
`opnform_extra_domains` adds internal `*.int.*` FQDNs so a DMZ
|
||||
reverseproxy can reach a backend hostname covered by the cert.
|
||||
`opnform_extra_hosts` lets the API containers reach the IdP's public FQDN
|
||||
(used in the OIDC `iss` claim) over the LAN when the DMZ has no NAT
|
||||
loopback.
|
||||
|
||||
## Example playbook
|
||||
|
||||
```yaml
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue