CI / verify (pull_request) Successful in 1m51s
CI / infrastructure-smoke (pull_request) Successful in 20s
CI / migration-smoke (pull_request) Successful in 1m48s
CI / image-build (pull_request) Successful in 35s
CI / deploy-smoke (same-origin) (pull_request) Successful in 50s
CI / deploy-smoke (split) (pull_request) Successful in 1m16s
Doctor checks the client-address configuration of the process it runs in, and operators run it as `migrate`. The proxy overlay set trusted-proxy mode on `api` only, so Doctor reported "Direct client-address mode" for an installation that trusts its proxy. - compose.proxy.yaml defines the three variables once, as a YAML anchor, and applies it to both `api` and `migrate`. Rendered with `docker compose config`, `api` is unchanged and `migrate` now matches it, keeping NODE_EXTRA_CA_CERTS from the base. - The example deploy/.env sets COMPOSE_FILE=compose.yaml:compose.proxy.yaml, as the instance already does. Plain `docker compose run --rm migrate ...` in the README then runs with the overlay; it used to read compose.yaml alone. - Every client-address check in Doctor now says it checked the environment Doctor runs with, and how to read the running API's own value (`docker compose exec api printenv EKKLESIA_CLIENT_ADDRESS_MODE`, confirmed to work in the image). - The deploy README explains why operator commands need the same files as the stack. Co-Authored-By: Claude Opus 5.5 <[email protected]>
92 lines
4.0 KiB
YAML
92 lines
4.0 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.
|
|
|
|
# Exactly one trusted peer: this proxy. Set from the same variable that fixes the proxy's address,
|
|
# so the two cannot drift apart. These override any value in ekklesia.env.
|
|
x-ekklesia-client-address: &ekklesia-client-address
|
|
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
|
|
|
|
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: *ekklesia-client-address
|
|
|
|
# Operator commands (migrations, Doctor) run as `migrate`. It gets the API's client-address
|
|
# configuration too, so that Doctor checks what the API actually runs rather than an empty one.
|
|
migrate:
|
|
environment: *ekklesia-client-address
|
|
|
|
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:
|