Adds a built-in Nextcloud to the ansipa stack and wires every service's
logins back to FreeIPA, either through Keycloak or directly.
Nextcloud:
- nextcloud + redis services, sharing the existing postgres server via a
second database. The initdb hook only fires on an empty data directory,
so run.sh also creates the role/DB idempotently for stacks that predate
Nextcloud.
- served at the /nextcloud subpath of the gateway (OVERWRITEWEBROOT), with
the /.well-known/* DAV redirects at the server root that sync clients and
Nextcloud's own setup checks expect.
Identity:
- keycloak-configure.sh now provisions confidential OIDC clients per relying
party (nextcloud, proxmox-ve, proxmox-bs, opnsense) plus a realm-wide
"groups" claim, writing secrets to .oidc-secrets (0600, gitignored).
- nextcloud-configure.sh binds the IPA LDAP backend as the account source
and user_oidc for login. The internal username is pinned to the IPA uid
and unique-uid is disabled, otherwise every SSO login creates a second,
empty account beside the LDAP one.
- checkmk-ldap-configure.sh binds CheckMK straight to FreeIPA. CheckMK
cannot go through Keycloak: SAML is commercial-edition only and no
edition supports OIDC. The LDAP REST endpoints landed in 2.4 (werk
#16527) but the stack pins 2.3, so it writes user_connections.mk —
generated via Python repr() so quotes in a password cannot produce a
SyntaxError that takes the GUI down, backed up and rolled back if the
site fails to load it. Directory type is 389directoryserver, which is
what gives FreeIPA the correct uid/member attribute defaults.
Monitoring:
- OPNsense local check (gateways, pf state table vs its hard limit,
firmware, CARP) plus an installer. POSIX sh, since OPNsense is FreeBSD
with no bash or GNU grep. Transport is agent-over-SSH rather than inetd
on 6556, because OPNsense regenerates inetd.conf/services/hosts.allow
from config.xml and silently reverts those edits on reboot.
Fixes found along the way:
- keycloak-configure.sh used KC_URL without the /auth relative path and
probed /health/ready, which lives on management port 9000 and is not
under that path — so it 404'd and timed out on this deployment.
- .env.example had an unquoted `cn=Directory Manager`, which the shell
scripts sourcing it parsed as a command named `Manager`.
- summary heredocs printed raw escape codes; colours now use ANSI-C quoting.
docs/md/ansipa-sso.md covers the identity chain, the per-service support
matrix, Proxmox VE/PBS OpenID realm setup, and why packet captures should
not be tunnelled through the CheckMK API.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BWLuyyrydC1u2FjM7naPcz
Adds a deliberate extra sign-in layer covering the entire nginx gateway
(portal page + every proxied web UI) via a shared htpasswd credential
generated on the FreeIPA container and published to nginx through a new
portal-auth volume. FreeIPA, CheckMK and Keycloak still each enforce their
own login behind it — two prompts beats one attacker who only had to beat
one lock. Healthcheck updated to treat 401 as healthy accordingly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add an nginx service that fronts all three web UIs on a single hostname/port
and serves a portal landing page, designed to sit behind the operator's own
TLS-terminating reverse proxy.
- Each backend is proxied at the path it already serves natively — FreeIPA
/ipa, CheckMK /cmk, Keycloak under a configured /auth relative path — so no
fragile path rewriting is needed and one upstream proxy line exposes
everything. FreeIPA's strict Host/Referer anti-CSRF checks are satisfied by
pinning Host to the IPA hostname and rewriting Referer.
- Honors incoming X-Forwarded-Proto/For so it works behind a second reverse
proxy (double-proxy verified: portal, FreeIPA, CheckMK and Keycloak all
reachable through two layers, and a CheckMK login POST completes to an
authenticated session through the gateway).
- Keycloak: KC_HTTP_RELATIVE_PATH=/auth + KC_PROXY_HEADERS=xforwarded so it
builds correct URLs behind the gateway; deliberately not forwarding
X-Forwarded-Port (this gateway is :80 internally; the public port is the
upstream proxy's) to avoid https://host:80 redirects.
- .env.example documents ANSIPA_HTTP_PORT and gives the exact upstream
reverse-proxy snippet (nginx location block + Caddy one-liner).
Portal is a static cyberqueer-themed page linking to /ipa/ui/, /cmk/, /auth/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUhrcFU8J1Hnf7vNqNxZNi