CI / verify (pull_request) Successful in 50s
CI / infrastructure-smoke (pull_request) Successful in 21s
CI / migration-smoke (pull_request) Failing after 25s
CI / image-build (pull_request) Successful in 18s
CI / deploy-smoke (same-origin) (pull_request) Successful in 49s
CI / deploy-smoke (split) (pull_request) Successful in 1m17s
An installation can now serve the Dashboard and the API on sibling hostnames (dash./api.) beside the default same-origin topology, from one image and with configuration alone. - The Dashboard learns its API origin at run time: the built page carries one empty <meta name="ekklesia-api-origin">, which the API fills with its identity base URL's origin at startup, before computing the page's ETag. The shell accepts only a canonical http(s) origin and otherwise fails closed without making a request. The build-time EKKLESIA_API_BASE_URL is gone from the bundle; the development server injects the same element. - The display host stays on the API's origin, so its device cookies, requests, and offline cache are unchanged. - The API trusts its own origin (trusted origins plus the identity base URL), as Better Auth already does, so the display page's mutations pass the origin check without operators listing it. - Doctor validates EKKLESIA_DASHBOARD_ORIGIN exactly as the API's startup does (it previously skipped it), reports the topology without printing hostnames, and warns when split hostnames are not siblings. - The module UI contract states the CORS header limit (safelisted headers plus Content-Type, safelisted response headers only). - Reference deployment: Caddyfile.split.example (Dashboard hostname serves / and its script; API hostname serves /api/*, /display/*, and the display script; each refuses the other's paths) and EKKLESIA_DASHBOARD_HOSTNAME in the proxy overlay and env examples. - The deploy smoke test runs either topology (CI runs both). The split run installs the module template as a test-only module, checks per-hostname routing and exact CORS, then drives a digest-pinned headless Chromium over the DevTools protocol, with no added dependency: sign-in on the Dashboard hostname, the session cookie host-only on the API hostname, the module UI imported cross-origin and its credentialed request succeeding, the display and its service worker on the API hostname, and sign-out. ADR 0045 is marked Accepted. Co-Authored-By: Claude Opus 5.5 <[email protected]>
85 lines
3.7 KiB
YAML
85 lines
3.7 KiB
YAML
# The reference TLS proxy overlay (ADR 0044's `proxy` service). Use it on top of the base:
|
|
#
|
|
# docker compose -f compose.yaml -f compose.proxy.yaml up -d
|
|
#
|
|
# Caddy terminates TLS with publicly trusted ACME certificates and publishes the deployment's only
|
|
# ports. It reaches `api` and `object-storage` on the `edge` network, which this overlay creates and
|
|
# attaches them to; `postgres` is never on it.
|
|
#
|
|
# Client addresses. Caddy is the only peer the API trusts to say who the client is: this overlay
|
|
# puts the API in trusted-proxy mode for exactly the proxy's fixed address, and the Caddyfile
|
|
# overwrites X-Forwarded-For with the one address Caddy resolved. Because the address must be
|
|
# fixed for "only the proxy" to be expressible, `edge` has an explicit subnet whose dynamic range is
|
|
# its lower half; the proxy (and the tunnel connector, if used) take addresses from the upper half,
|
|
# which Docker never hands to another container.
|
|
|
|
services:
|
|
proxy:
|
|
image: caddy:2.11.4-alpine@sha256:6aeddd44c3078b0f9a35206472a11420648a79c184603ef95957d0a20044cb2b
|
|
restart: unless-stopped
|
|
read_only: true
|
|
tmpfs:
|
|
- /tmp
|
|
cap_drop:
|
|
- ALL
|
|
cap_add:
|
|
- NET_BIND_SERVICE
|
|
security_opt:
|
|
- no-new-privileges:true
|
|
ports:
|
|
# 80 answers ACME HTTP-01 challenges and redirects everything else to HTTPS. Set
|
|
# EKKLESIA_PUBLISH_ADDRESS to bind one host interface instead of all of them.
|
|
- "${EKKLESIA_PUBLISH_ADDRESS:-0.0.0.0}:${EKKLESIA_HTTP_PORT:-80}:80"
|
|
- "${EKKLESIA_PUBLISH_ADDRESS:-0.0.0.0}:${EKKLESIA_HTTPS_PORT:-443}:443"
|
|
- "${EKKLESIA_PUBLISH_ADDRESS:-0.0.0.0}:${EKKLESIA_HTTPS_PORT:-443}:443/udp"
|
|
environment:
|
|
EKKLESIA_HOSTNAME: ${EKKLESIA_HOSTNAME:?EKKLESIA_HOSTNAME is required, for example ekklesia.example.org}
|
|
EKKLESIA_STORAGE_HOSTNAME: ${EKKLESIA_STORAGE_HOSTNAME:?EKKLESIA_STORAGE_HOSTNAME is required, for example storage.ekklesia.example.org}
|
|
# The split topology only (ADR 0045, Caddyfile.split.example), where EKKLESIA_HOSTNAME is the
|
|
# API's hostname and this is the Dashboard's. Caddyfile.example ignores it; the split
|
|
# configuration refuses to start without it.
|
|
EKKLESIA_DASHBOARD_HOSTNAME: ${EKKLESIA_DASHBOARD_HOSTNAME:-}
|
|
EKKLESIA_ACME_EMAIL: ${EKKLESIA_ACME_EMAIL:?EKKLESIA_ACME_EMAIL is required for ACME account notices}
|
|
EKKLESIA_TUNNEL_ADDRESS: ${EKKLESIA_TUNNEL_ADDRESS:-10.249.250.253}
|
|
volumes:
|
|
- ${EKKLESIA_CADDYFILE:-./Caddyfile}:/etc/caddy/Caddyfile:ro
|
|
# The installation-private CA, so Caddy can verify object-storage's certificate.
|
|
- ${EKKLESIA_TLS_DIR:-/etc/ekklesia/tls}/ca.crt:/etc/caddy/ekklesia-ca.crt:ro
|
|
- proxy-data:/data
|
|
- proxy-config:/config
|
|
networks:
|
|
edge:
|
|
ipv4_address: ${EKKLESIA_PROXY_ADDRESS:-10.249.250.254}
|
|
depends_on:
|
|
api:
|
|
condition: service_healthy
|
|
object-storage:
|
|
condition: service_healthy
|
|
|
|
api:
|
|
networks:
|
|
edge: {}
|
|
environment:
|
|
# Exactly one trusted peer: this proxy. Set here from the same variable that fixes the proxy's
|
|
# address, so the two cannot drift apart. These override any value in ekklesia.env.
|
|
EKKLESIA_CLIENT_ADDRESS_MODE: trusted-proxy
|
|
EKKLESIA_CLIENT_ADDRESS_HEADER: x-forwarded-for
|
|
EKKLESIA_CLIENT_ADDRESS_TRUSTED_PROXIES: ${EKKLESIA_PROXY_ADDRESS:-10.249.250.254}/32
|
|
|
|
object-storage:
|
|
networks:
|
|
edge: {}
|
|
|
|
networks:
|
|
edge:
|
|
ipam:
|
|
config:
|
|
- subnet: ${EKKLESIA_EDGE_SUBNET:-10.249.250.0/24}
|
|
# Docker assigns dynamic addresses only from this range. Keep the fixed proxy and tunnel
|
|
# addresses outside it.
|
|
ip_range: ${EKKLESIA_EDGE_DYNAMIC_RANGE:-10.249.250.0/25}
|
|
|
|
volumes:
|
|
proxy-data:
|
|
proxy-config:
|