oauth2-proxy's singular 'provider-ca-file' flag has a known bug
(oauth2-proxy/oauth2-proxy#2802) and may not be a valid config key on
:latest, causing oauth2-proxy to fail config parsing and crash on
startup. This made nginx's auth_request to sso-proxy return 500 for
every pihole request (HTTP Error 500 on https://pihole.infinito.example/),
blocking even the pre-Playwright readiness check.
provider_ca_files (plural, array of strings) is the documented,
working config key for loading a custom CA for the OIDC provider
connection.
This block was added in an earlier (reverted-in-intent but never
actually removed) attempt and caused a duplicate 'sso-proxy' YAML key
(also defined via sys-svc-compose/base.yml.j2's sso_proxy include),
breaking compose_ca.py's compose config parsing entirely.
The CA trust fix now lives solely in 01_core.yml's lineinfile task,
which patches the rendered oauth2-proxy config file directly -- no
compose.yml changes needed.
- playwright.spec.js: remove duplicated SSO/persona scenarios now
covered by test-guest.js/test-oauth2.js/test-native.js. Reduce to
baseline checks (front page + TLS/HSTS, HTML content-type), matching
the pgadmin pattern.
- meta/main.yml: rewrite description to describe Pi-hole the software
(network-wide DNS sinkhole, DNS/DHCP management, web dashboard)
rather than the role/integration details.
penpot was merged using ports 8041/16506 after our last rebase.
Reassign pihole to 8078 (http) and 16507 (sso) via
infinito contributing network ports suggest.
oauth2-proxy (quay.io/oauth2-proxy/oauth2-proxy) is a distroless image
with no /bin/sh, so the platform's CA-trust wrapper (with-ca-trust.sh,
applied by compose_ca.py for services with a shell) is skipped for this
container. Go binaries ignore SSL_CERT_FILE/REQUESTS_CA_BUNDLE and use
only the OS system cert pool, so on self-signed TLS setups oauth2-proxy
fails OIDC discovery against Keycloak with
'x509: certificate signed by unknown authority'. nginx then falls back
to pihole's native /admin/login instead of redirecting to Keycloak,
breaking SSO entirely in CI and on any self-signed deployment.
Fix: append provider_ca_file to the rendered sso-proxy config after
web-app-keycloak/tasks/sso_proxy.yml renders it and before compose up
runs. oauth2-proxy loads this CA explicitly for the OIDC provider
connection, bypassing the system cert pool entirely.
Scoped to this role: avoids touching the shared
oauth2-proxy-keycloak.cfg.j2 template (would affect every
flavor: oauth2 role platform-wide). No-op on production
(TLS_MODE=letsencrypt).
oauth2-proxy is a distroless Go binary — the platform CA wrapper
(with-ca-trust.sh) is skipped because there is no /bin/sh, and Go
ignores SSL_CERT_FILE. Pass the CA cert directly via --provider-ca-file
so oauth2-proxy trusts the internal Keycloak OIDC endpoint.
Follows the penpot pattern of inline command override in compose.yml.j2
conditioned on TLS_MODE == self_signed. No-op on production (letsencrypt).
oauth2-proxy is distroless (no /bin/sh), so the platform CA wrapper
entrypoint is skipped. Go binaries ignore SSL_CERT_FILE and use the
system cert pool only, causing x509 errors on self-signed TLS setups.
Override sso-proxy command in compose.override.yml.j2 to append
--provider-ca-file pointing to the platform CA cert
(/tmp/infinito/ca/root-ca.crt, always bind-mounted by compose_ca.py)
when TLS_MODE is self_signed. No-op on production (letsencrypt).
inventory.py auto-generation only triggers when oauth2.enabled is a literal
boolean True. Pihole uses a Jinja2 expression for enabled, so auto-generation
does not trigger. The credential must be defined in schema.yml explicitly.
Other OAuth2-protected apps (decidim, espocrm, etc.) do not define this
credential in their schema.yml and work correctly. The platform handles
the cookie_secret for all apps through the web-app-oauth2-proxy role.
Defining it per-role was causing oauth2-proxy to crash on startup in CI
because the credential was not properly generated in the CI environment.
oauth2-proxy cannot verify Keycloak's self-signed certificate during OIDC
discovery because the platform CA cert is injected via env vars but Go's
TLS stack doesn't pick it up reliably in the DinD environment.
Adding --ssl-insecure-skip-verify allows oauth2-proxy to complete OIDC
discovery and serve requests instead of crash-looping with certificate errors.
This is acceptable for a self-signed lab environment.
oauth2-proxy cannot resolve auth.infinito.example from inside Docker in CI
environments where CoreDNS does not have this entry. Adding extra_hosts via
compose.override.yml.j2 (auto-included by compose_file_args lookup) maps
the Keycloak domain to host-gateway so OIDC discovery succeeds.
Pi-hole deploys immediately after Keycloak but Keycloak needs time to
initialize OIDC. Adding explicit wait for Keycloak OIDC discovery endpoint
ensures oauth2-proxy can complete OIDC discovery and serve 302 redirects
instead of 500 errors when the platform Playwright wait check runs.
- meta/services.yml: add noqa comment for email integration
- tasks/01_setup.yml: use uri_retry instead of legacy uri module
- tasks/01_setup.yml: add HTTPS endpoint check to prevent 500 in CI wait check
- tasks/01_setup.yml: increase oauth2-proxy wait timeout to 300s with ignore_errors
oauth2-proxy requires Keycloak to complete OIDC discovery before binding its port.
If Keycloak is still warming up, oauth2-proxy may not be ready within the timeout.
Playwright tests validate the actual functionality end-to-end.
oauth2-proxy needs time to complete OIDC discovery against Keycloak in CI.
120s was insufficient - increased to 300s to allow for slower CI environments.
- tasks/01_core.yml: set docker_compose_flush_handlers: true, remove manual flush_handlers task
- tasks/01_setup.yml: add wait for oauth2-proxy port to fix HTTP 500 in CI wait check
- meta/services.yml: use NETWORK_PUBLIC_DNS_RESOLVERS SPOT for upstream_dns
- vars/main.yml: remove default from upstream_dns lookup, join list with semicolon
Port 53 is already in use by systemd-resolved on Fedora CI runners.
Pi-hole DNS still works within the Docker network without host binding.
Playwright tests don't require DNS functionality on the host.
Variable is already defined platform-wide in test-e2e-playwright/defaults/main.yml.
Redefining it in vars/main.yml violates the project-wide constant uniqueness rule.
- Update logout test to navigate directly to oauth2/sign_out endpoint with Keycloak logout redirect
- Remove duplicate oauth2-proxy service definition from compose.yml.j2 (was causing duplicate key error)
- Add TEST_E2E_PLAYWRIGHT_WAIT_ENABLED: false to vars/main.yml (oauth2-proxy returns 403 before redirect)
- Change subnet from 192.168.102.240/28 to 192.168.103.80/28 (overlap with web-app-fediwall)
- Change local.oauth2 port from 16493 to 16500 (collision with web-svc-libretranslate)
- Extend PORT_BANDS.local.oauth2 band from 16499 to 16514 (band exhausted)
- Add missing resource limits to meta/services.yml (cpus, mem_reservation, mem_limit, min_storage, pids_limit)
- Deploy Pi-hole as web app with NGINX proxy and OAuth2 protection
- Only web-app-pihole-administrators group can access the dashboard
- DNS port 53 (TCP/UDP) exposed for network-wide ad blocking
- Upstream DNS configurable per inventory (default: Cloudflare 1.1.1.1)
- Redis session store for OAuth2 proxy
- Timezone from HOST_TIMEZONE platform variable
- Port bindings use DOCKER_BIND_HOST platform variable
- Register subnet 192.168.102.240/28 in networks
- Register ports 8070 (http) and 16493 (oauth2) in ports
- Playwright tests: oauth2 protection, admin login, biber denied (3/3 passing)
Known limitations:
- Pi-hole v6 gravity blocklist requires manual setup via admin UI
- Internal *.infinito.example DNS forwarding to CoreDNS planned as follow-up
The CA-install entrypoint used 'apt-get update && apt-get install ... || true', which shellcheck flags as SC2015 (A && B || C is not if-then-else; C may run when A is true). Group it as '{ apt-get update && apt-get install ...; } || true' — identical best-effort semantics, shellcheck-clean. Verified by make test (lint-shellcheck green).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The CI variant overrode the role's ollama/ollama default with alpine/ollama, which ships the API/CLI but NOT the llama-server inference runner — so openwebui's ollama-chat completion failed with HTTP 400. Drop the variant's image override so it inherits ollama/ollama (runner built-in) from meta/services.yml. Reverts the earlier runner-provisioning approach (alpine + downloaded runner) as unnecessarily heavy/fragile.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
system_pkg.py's apt-get update/install failed immediately ('Could not get lock /var/lib/dpkg/lock-frontend') when a concurrent deploy held the dpkg lock, so make autoformat/test could not run while a deploy was in progress. Pass -o DPkg::Lock::Timeout=600 to both apt-get invocations so they wait for the lock instead of failing, and update the unit test to assert the lock-wait. Scope is the dev/lint package installer only; the Ansible deploys use ansible.builtin.apt and are unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The earlier 'sed -i "/brevity/ s/@new-tlds//" requirements.txt' did not resolve the pip ResolutionImpossible (the build still failed with it applied) and the analysis flagged it as a backwards/ineffective normalization. Remove it and leave the brevity/granary direct-URL resolution to pip as upstream ships it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>