feat(deploy): add the reference deployment Compose project
CI / verify (pull_request) Successful in 54s
CI / infrastructure-smoke (pull_request) Successful in 21s
CI / migration-smoke (pull_request) Successful in 1m48s
CI / image-build (pull_request) Successful in 27s
CI / deploy-smoke (pull_request) Successful in 42s
CI / verify (pull_request) Successful in 54s
CI / infrastructure-smoke (pull_request) Successful in 21s
CI / migration-smoke (pull_request) Successful in 1m48s
CI / image-build (pull_request) Successful in 27s
CI / deploy-smoke (pull_request) Successful in 42s
Implement ADR 0044's reference deployment as a base Compose file plus overlays, so an installation that already runs a proxy can use the base alone. - compose.yaml: api, postgres, object-storage, and the operator-only migrate (profile ops, plan by default). No published port. api and migrate run init, read-only, tmpfs /tmp, cap_drop ALL, no-new-privileges. postgres is only on the internal data network. - TLS everywhere from one installation-private CA: PostgreSQL TLS 1.3 only, with pg_hba rejecting every non-TLS connection, and the API on verify-full via EKKLESIA_DATABASE_CA (PEM text). SeaweedFS serves S3 over HTTPS (ADR 0030 requires https in production), trusted by the API through NODE_EXTRA_CA_CERTS. No Core change. - compose.proxy.yaml + Caddyfile.example: Caddy pinned by digest, ACME HTTP-01 (DNS-01 documented), routes /, /api/*, /display/*, /assets/* to api and refuses everything else; the storage hostname preserves Host. The API trusts exactly the proxy's fixed /32 on an edge network whose dynamic range excludes the fixed addresses; Caddy overwrites X-Forwarded-For and strips caller-supplied forwarding headers. - compose.tunnel.yaml: cloudflared pinned by digest, token from its own env file; Caddy honors CF-Connecting-IP only from its fixed address. - Four per-service root-owned env files, examples with no values; scripts/init-tls.sh (idempotent, never overwrites a key) and scripts/gen-secrets.sh (prints only). - CI: pnpm deploy:config in ci:verify for all three combinations, and a deploy-smoke job (scripts/smoke-deploy.sh) proving readiness, the Dashboard, signed storage delivery, the client-address chain, initial owner sign-in/out, verify-full database TLS, and Doctor through the proxy, against disposable services. tests/deploy.test.ts pins the structural guarantees. - ADR 0044 (still Proposed) amended to match: API-to-storage HTTPS, DNS-01 needs a custom Caddy build and tunnel-only needs DNS-01, the tunnel's origin TLS settings live in the Cloudflare dashboard, port 80 serves HTTP-01, and secrets are one env file per service. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b593abe7a5
commit
167b4c4f14
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
# Non-secret settings for the reference deployment's Compose project. Copy to deploy/.env, which
|
||||
# Compose reads automatically from this directory. Nothing here is a secret; secrets live in the
|
||||
# per-service files under EKKLESIA_ENV_DIR.
|
||||
|
||||
# The image to run, by registry, name, and tag (or digest). Required.
|
||||
EKKLESIA_IMAGE=
|
||||
|
||||
# Where the root-owned, mode-600 env files and the TLS directory live on the host.
|
||||
EKKLESIA_ENV_DIR=/etc/ekklesia
|
||||
EKKLESIA_TLS_DIR=/etc/ekklesia/tls
|
||||
|
||||
# --- compose.proxy.yaml ---------------------------------------------------------------------------
|
||||
# The Ekklesia hostname (Dashboard, API, displays) and the storage hostname (signed object URLs).
|
||||
EKKLESIA_HOSTNAME=
|
||||
EKKLESIA_STORAGE_HOSTNAME=
|
||||
# Contact address for the ACME account (certificate expiry notices).
|
||||
EKKLESIA_ACME_EMAIL=
|
||||
# EKKLESIA_CADDYFILE=./Caddyfile
|
||||
# EKKLESIA_PUBLISH_ADDRESS=0.0.0.0
|
||||
# EKKLESIA_HTTP_PORT=80
|
||||
# EKKLESIA_HTTPS_PORT=443
|
||||
|
||||
# The edge network. Change the subnet only if it collides with one already on the host. Docker
|
||||
# assigns dynamic addresses only from EKKLESIA_EDGE_DYNAMIC_RANGE (the lower half); the proxy and
|
||||
# the tunnel connector take fixed addresses from the upper half, so nothing else can ever hold them.
|
||||
# EKKLESIA_EDGE_SUBNET=10.249.250.0/24
|
||||
# EKKLESIA_EDGE_DYNAMIC_RANGE=10.249.250.0/25
|
||||
# EKKLESIA_PROXY_ADDRESS=10.249.250.254
|
||||
# EKKLESIA_TUNNEL_ADDRESS=10.249.250.253
|
||||
Vendored
+71
@@ -0,0 +1,71 @@
|
||||
# Ekklesia API and operator commands (`api` and `migrate`). Reference deployment, ADR 0044.
|
||||
#
|
||||
# Install as EKKLESIA_ENV_DIR/ekklesia.env (default /etc/ekklesia/ekklesia.env), owned by root with
|
||||
# mode 600:
|
||||
# install -o root -g root -m 600 env/ekklesia.env.example /etc/ekklesia/ekklesia.env
|
||||
# Never commit the filled-in file. Generate secrets with scripts/gen-secrets.sh, and the database CA
|
||||
# line with scripts/init-tls.sh.
|
||||
#
|
||||
# The image already sets NODE_ENV=production, EKKLESIA_API_HOST=0.0.0.0, and EKKLESIA_API_PORT=3000.
|
||||
# Compose sets NODE_EXTRA_CA_CERTS. The proxy overlay sets the three EKKLESIA_CLIENT_ADDRESS_*
|
||||
# variables. None of those belong here.
|
||||
#
|
||||
# Values that are part of the reference topology are filled in; everything installation-specific is
|
||||
# blank. Full reference: docs/development/api.md#required-configuration.
|
||||
|
||||
# --- Database (ADR 0018) ------------------------------------------------------------------------
|
||||
# The host must be exactly `postgres`: the server certificate is issued for that name and
|
||||
# verify-full checks it. User, password, and database must match postgres.env. Percent-encode any
|
||||
# reserved character in the password. Never add sslmode or another TLS parameter to this URL.
|
||||
DATABASE_URL=postgresql://<user>:<password>@postgres:5432/<database>
|
||||
EKKLESIA_DATABASE_TLS_MODE=verify-full
|
||||
# The installation-private CA as PEM text (not a path), on one double-quoted line with \n escapes.
|
||||
# scripts/init-tls.sh prints this exact line. It is the only CA the database connection trusts.
|
||||
EKKLESIA_DATABASE_CA=
|
||||
|
||||
# --- Identity (ADR 0011, ADR 0018, ADR 0034) ----------------------------------------------------
|
||||
# The public https origin of the Ekklesia hostname, with no path. Production rejects http and
|
||||
# loopback origins.
|
||||
EKKLESIA_IDENTITY_BASE_URL=
|
||||
# Comma-separated exact origins allowed to make credentialed requests. Normally just the base URL.
|
||||
EKKLESIA_IDENTITY_TRUSTED_ORIGINS=
|
||||
# At least 32 random characters. Rotating it signs every user out.
|
||||
EKKLESIA_IDENTITY_SECRET=
|
||||
# Leave unset: the Dashboard is served from the base URL (ADR 0044).
|
||||
# EKKLESIA_DASHBOARD_ORIGIN=
|
||||
# Production refuses public sign-up. Leave unset.
|
||||
# EKKLESIA_PUBLIC_SIGNUP_ENABLED=
|
||||
|
||||
# --- Client address (ADR 0017) ------------------------------------------------------------------
|
||||
# Set by compose.proxy.yaml. Only a base-only deployment behind its own proxy sets these here, with
|
||||
# that proxy's fixed address as the only trusted entry (deploy/README.md):
|
||||
# EKKLESIA_CLIENT_ADDRESS_MODE=trusted-proxy
|
||||
# EKKLESIA_CLIENT_ADDRESS_HEADER=x-forwarded-for
|
||||
# EKKLESIA_CLIENT_ADDRESS_TRUSTED_PROXIES=<proxy address>/32
|
||||
|
||||
# --- Object storage (ADR 0030, ADR 0031) --------------------------------------------------------
|
||||
# The API reaches SeaweedFS over HTTPS on the private network; production storage must be https.
|
||||
S3_ENDPOINT=https://object-storage:8443
|
||||
# The https origin browsers fetch signed object URLs from: the storage hostname on the proxy.
|
||||
S3_DELIVERY_ENDPOINT=
|
||||
# Must match object-storage.env.
|
||||
S3_ACCESS_KEY=
|
||||
S3_SECRET_KEY=
|
||||
S3_REGION=us-east-1
|
||||
S3_FORCE_PATH_STYLE=true
|
||||
S3_BUCKET_PREFIX=ekklesia
|
||||
|
||||
# --- Outbound email (ADR 0037), optional --------------------------------------------------------
|
||||
# Without a relay, email verification and password recovery cannot deliver. See
|
||||
# docs/development/email.md.
|
||||
# EKKLESIA_SMTP_HOST=
|
||||
# EKKLESIA_SMTP_PORT=587
|
||||
# EKKLESIA_SMTP_SECURITY=starttls
|
||||
# EKKLESIA_SMTP_USERNAME=
|
||||
# EKKLESIA_SMTP_PASSWORD=
|
||||
# EKKLESIA_SMTP_FROM_ADDRESS=
|
||||
# EKKLESIA_SMTP_FROM_NAME=Ekklesia
|
||||
# EKKLESIA_SMTP_HELO_NAME=
|
||||
|
||||
# --- Module secrets (ADR 0029), as installed modules declare them --------------------------------
|
||||
# EKKLESIA_MODULE_SECRET__<MODULE_ID>__<KEY>=
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
# SeaweedFS S3 credentials (`object-storage` only). Install as EKKLESIA_ENV_DIR/object-storage.env,
|
||||
# owned by root with mode 600. Must match S3_ACCESS_KEY and S3_SECRET_KEY in ekklesia.env.
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
# PostgreSQL superuser for the reference deployment (`postgres` only). Install as
|
||||
# EKKLESIA_ENV_DIR/postgres.env, owned by root with mode 600. Read only on the database's first
|
||||
# start; changing it later does not change the stored password.
|
||||
#
|
||||
# Must match the user, password, and database in ekklesia.env's DATABASE_URL.
|
||||
POSTGRES_USER=ekklesia
|
||||
POSTGRES_DB=ekklesia
|
||||
POSTGRES_PASSWORD=
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
# Cloudflare Tunnel connector token (`tunnel` only, compose.tunnel.yaml). Install as
|
||||
# EKKLESIA_ENV_DIR/tunnel.env, owned by root with mode 600. Copy the token from the tunnel's page in
|
||||
# the Cloudflare dashboard; it is the tunnel's credential.
|
||||
TUNNEL_TOKEN=
|
||||
Reference in New Issue
Block a user