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
1dcff92240
commit
19864d79b2
17 changed files with 309 additions and 37 deletions
|
|
@ -22,9 +22,14 @@ The role asserts these are set; the play fails fast if any is empty:
|
|||
| `bookstack_db_root_password` | MariaDB root password |
|
||||
| `bookstack_db_password` | MariaDB user password |
|
||||
| `bookstack_admin_password` | Initial local admin password |
|
||||
| `bookstack_oidc_client_id` | Entra ID App Registration ID (if OIDC on) |
|
||||
| `bookstack_oidc_client_secret` | Entra ID client secret (if OIDC on) |
|
||||
| `bookstack_entra_tenant_id` | Entra tenant UUID (if OIDC on) |
|
||||
| `bookstack_oidc_client_id` | OIDC client ID (if OIDC on) |
|
||||
| `bookstack_oidc_client_secret` | OIDC client secret (if OIDC on) |
|
||||
|
||||
When OIDC is on, the role also asserts that `bookstack_oidc_issuer`
|
||||
resolves to a concrete URL. For Entra ID this means setting
|
||||
`bookstack_entra_tenant_id` (the default issuer interpolates it; an unset
|
||||
tenant leaves `//v2.0` and fails the assert). For other IdPs (Authentik,
|
||||
Keycloak) set `bookstack_oidc_issuer` directly instead.
|
||||
|
||||
Provide via OpenBao lookup, Ansible Vault or `--extra-vars`. Never commit
|
||||
real secrets.
|
||||
|
|
@ -34,6 +39,10 @@ real secrets.
|
|||
See `defaults/main.yml`. Frequently overridden:
|
||||
|
||||
- `bookstack_domain`, `bookstack_base_url`
|
||||
- `bookstack_extra_domains` (extra Host-rule hostnames, e.g. an internal
|
||||
`*.int.*` FQDN for a DMZ reverseproxy)
|
||||
- `bookstack_extra_hosts` (container `/etc/hosts` overrides for
|
||||
split-horizon IdP access; entries as `host:ip`)
|
||||
- `bookstack_image`, `bookstack_db_image` (pin in production)
|
||||
- `bookstack_oidc_enabled` (set `false` to disable OIDC entirely)
|
||||
- `bookstack_oidc_auto_initiate` (`true` redirects straight to IdP)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,14 @@ bookstack_backup_dir: "{{ bookstack_docker_volume_dir }}/backup"
|
|||
|
||||
# Service configuration
|
||||
bookstack_domain: "wiki.local.test"
|
||||
# Additional hostnames the bookstack router answers on (e.g. an internal
|
||||
# *.int.* FQDN so a DMZ reverseproxy can hit a backend hostname covered
|
||||
# by the cert).
|
||||
bookstack_extra_domains: []
|
||||
# Container-level /etc/hosts overrides — useful in split-horizon setups
|
||||
# where the BookStack container needs to reach an IdP's public FQDN
|
||||
# (used in the OIDC `iss` claim) over the LAN rather than via the DMZ.
|
||||
bookstack_extra_hosts: []
|
||||
bookstack_base_url: "https://{{ bookstack_domain }}"
|
||||
|
||||
# Images — pin via inventory in production
|
||||
|
|
|
|||
|
|
@ -37,6 +37,24 @@ argument_specs:
|
|||
type: str
|
||||
default: wiki.local.test
|
||||
description: Hostname used in the Traefik Host rule.
|
||||
bookstack_extra_domains:
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
description:
|
||||
- Additional hostnames the Traefik router answers on, OR-combined
|
||||
with C(bookstack_domain). Useful for an internal C(*.int.*) FQDN
|
||||
so a DMZ reverseproxy can reach a backend hostname covered by the
|
||||
cert.
|
||||
bookstack_extra_hosts:
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
description:
|
||||
- Container-level C(/etc/hosts) overrides (Compose C(extra_hosts)
|
||||
entries, C("host:ip")). Useful in split-horizon setups where the
|
||||
BookStack container must reach an IdP's public FQDN (used in the
|
||||
OIDC C(iss) claim) over the LAN rather than via the DMZ.
|
||||
bookstack_base_url:
|
||||
type: str
|
||||
description: Defaults to C("https://{{ bookstack_domain }}").
|
||||
|
|
|
|||
|
|
@ -14,7 +14,13 @@
|
|||
- bookstack_admin_password | length > 0
|
||||
- (not bookstack_oidc_enabled) or (bookstack_oidc_client_id | length > 0)
|
||||
- (not bookstack_oidc_enabled) or (bookstack_oidc_client_secret | length > 0)
|
||||
- (not bookstack_oidc_enabled) or (bookstack_entra_tenant_id | length > 0)
|
||||
# Issuer URL must resolve to something concrete. The Entra default
|
||||
# interpolates bookstack_entra_tenant_id; an unset tenant leaves
|
||||
# "//v2.0" in the URL. Allow non-Entra IdPs (Authentik, Keycloak)
|
||||
# that override bookstack_oidc_issuer directly.
|
||||
- (not bookstack_oidc_enabled) or
|
||||
(bookstack_oidc_issuer | length > 0 and
|
||||
'//v2.0' not in bookstack_oidc_issuer)
|
||||
fail_msg: >-
|
||||
One or more required secrets are unset. Provide them via OpenBao
|
||||
lookup, Ansible Vault or --extra-vars. See README for the full list.
|
||||
|
|
|
|||
|
|
@ -45,13 +45,19 @@ services:
|
|||
networks:
|
||||
- {{ bookstack_traefik_network }}
|
||||
- internal
|
||||
{% if bookstack_extra_hosts | length > 0 %}
|
||||
extra_hosts:
|
||||
{% for host in bookstack_extra_hosts %}
|
||||
- "{{ host }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
depends_on:
|
||||
{{ bookstack_service_name }}-db:
|
||||
condition: service_healthy
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network={{ bookstack_traefik_network }}"
|
||||
- "traefik.http.routers.{{ bookstack_service_name }}.rule=Host(`{{ bookstack_domain }}`)"
|
||||
- "traefik.http.routers.{{ bookstack_service_name }}.rule={% set _all_domains = [bookstack_domain] + (bookstack_extra_domains | default([])) %}{% for d in _all_domains %}Host(`{{ d }}`){% if not loop.last %} || {% endif %}{% endfor +%}"
|
||||
- "traefik.http.routers.{{ bookstack_service_name }}.entrypoints=websecure"
|
||||
- "traefik.http.routers.{{ bookstack_service_name }}.tls=true"
|
||||
- "traefik.http.routers.{{ bookstack_service_name }}.tls.certresolver={{ bookstack_traefik_certresolver }}"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue