diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dfe43a..447dc9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 2026-06-29 — App-email rollout: gitea + rallly (calcom/nextcloud/immich = findings) +- **gitea 1.23.4** — added `smtp_mapping` (STARTTLS via `GITEA__mailer__PROTOCOL=smtp+starttls` + + `FORCE_TRUST_SERVER_CERT=true` to trust the shim's self-signed cert; single `GITEA__mailer__FROM`). Compose + references the injected `GITEA__mailer__*` keys; env applied every boot. +- **rallly** — added `smtp_mapping` (Nodemailer STARTTLS, `SMTP_SECURE=false` + `SMTP_REJECT_UNAUTHORIZED=false` + to accept the self-signed cert; single `NOREPLY_EMAIL`). **Also fixed a broken image pin:** `3.12.1` does not + exist (3.x stops at 3.11.x) → corrected to `3.11.2` so the template is deployable. +- **NOT wired — reported as findings** (`felhom.eu/documentation/audits/FINDING-app-email-rollout-2026-06-29.md`): + - **cal.com v4.8.7** — hard-codes TLS `rejectUnauthorized:true` with no override; opportunistic STARTTLS + against the self-signed shim fails. Needs a non-STARTTLS-advertising plaintext listener (mechanism change). + - **nextcloud 31** — no cert-skip env (same opportunistic-STARTTLS gap) **and** a split From + (`MAIL_FROM_ADDRESS`+`MAIL_DOMAIN`) the single-`from_var` mapping can't express. + - **immich v2.5.5** — no SMTP env vars at all; config is admin-UI/DB or an `IMMICH_CONFIG_FILE` JSON. Does not + fit env-injection; left for a future config-file-injection mechanism (or manual admin-UI setup). + ## 2026-06-29 — App-email: smtp_mapping for Vaultwarden + Mealie - Added the `smtp_mapping` block to `templates/vaultwarden/.felhom.yml` and `templates/mealie/.felhom.yml`, enabling managed outbound email (app → in-controller shim → hub → Resend) for the two spike-proven apps diff --git a/templates/gitea/.felhom.yml b/templates/gitea/.felhom.yml index bd62e82..0806501 100644 --- a/templates/gitea/.felhom.yml +++ b/templates/gitea/.felhom.yml @@ -61,3 +61,18 @@ healthcheck: path: "/api/healthz" expect: status: 200 + +# --- App-email mapping (apps → in-controller shim → hub → Resend) --- +# Gitea uses STARTTLS to the shim and trusts its self-signed cert via FORCE_TRUST_SERVER_CERT. +# Gitea applies GITEA__* env on every boot, so toggling app-email + redeploy takes effect. The From +# is Gitea's single GITEA__mailer__FROM; USER/PASSWD stay unset (the shim accepts no-auth). +smtp_mapping: + host_var: GITEA__mailer__SMTP_ADDR + port_var: GITEA__mailer__SMTP_PORT + security_var: GITEA__mailer__PROTOCOL + security_value: "smtp+starttls" + from_var: GITEA__mailer__FROM + from_local: gitea + extra: + GITEA__mailer__ENABLED: "true" + GITEA__mailer__FORCE_TRUST_SERVER_CERT: "true" diff --git a/templates/gitea/docker-compose.yml b/templates/gitea/docker-compose.yml index 0777078..d4d8dc9 100644 --- a/templates/gitea/docker-compose.yml +++ b/templates/gitea/docker-compose.yml @@ -16,6 +16,15 @@ services: - GITEA__server__ROOT_URL=https://${SUBDOMAIN}.${DOMAIN} - GITEA__server__SSH_DOMAIN=${SUBDOMAIN}.${DOMAIN} - GITEA__database__DB_TYPE=sqlite3 + # App-email (managed relay). Injected by the controller only when app-email is on (global + + # per-app); empty ENABLED/ADDR keeps Gitea mail disabled. Gitea applies GITEA__* env on every + # boot (environment-to-ini), so the toggle takes effect on redeploy. See .felhom.yml smtp_mapping. + - GITEA__mailer__ENABLED=${GITEA__mailer__ENABLED:-false} + - GITEA__mailer__PROTOCOL=${GITEA__mailer__PROTOCOL:-smtp+starttls} + - GITEA__mailer__SMTP_ADDR=${GITEA__mailer__SMTP_ADDR:-} + - GITEA__mailer__SMTP_PORT=${GITEA__mailer__SMTP_PORT:-2525} + - GITEA__mailer__FROM=${GITEA__mailer__FROM:-} + - GITEA__mailer__FORCE_TRUST_SERVER_CERT=${GITEA__mailer__FORCE_TRUST_SERVER_CERT:-false} volumes: - gitea_data:/data networks: diff --git a/templates/rallly/.felhom.yml b/templates/rallly/.felhom.yml index 75c3ee9..19b8bfb 100644 --- a/templates/rallly/.felhom.yml +++ b/templates/rallly/.felhom.yml @@ -68,3 +68,18 @@ healthcheck: checks: - type: http port: 3000 + +# --- App-email mapping (apps → in-controller shim → hub → Resend) --- +# Rallly (Nodemailer) uses STARTTLS to the shim (SMTP_SECURE=false) and accepts the shim's self-signed +# cert (SMTP_REJECT_UNAUTHORIZED=false on v4; v3.x accepts by default). From = Rallly's single +# NOREPLY_EMAIL; SMTP_USER/SMTP_PWD stay unset (the shim accepts no-auth). +smtp_mapping: + host_var: SMTP_HOST + port_var: SMTP_PORT + security_var: SMTP_SECURE + security_value: "false" + from_var: NOREPLY_EMAIL + from_name_var: NOREPLY_EMAIL_NAME + from_local: rallly + extra: + SMTP_REJECT_UNAUTHORIZED: "false" diff --git a/templates/rallly/docker-compose.yml b/templates/rallly/docker-compose.yml index 0357dc1..c255346 100644 --- a/templates/rallly/docker-compose.yml +++ b/templates/rallly/docker-compose.yml @@ -10,7 +10,7 @@ services: rallly: - image: lukevella/rallly:3.12.1 + image: lukevella/rallly:3.11.2 container_name: rallly restart: unless-stopped depends_on: @@ -21,6 +21,16 @@ services: - DATABASE_URL=postgresql://rallly:${DB_PASSWORD}@rallly-postgres:5432/rallly - SECRET_PASSWORD=${SECRET_PASSWORD} - NEXT_PUBLIC_BASE_URL=https://${SUBDOMAIN}.${DOMAIN} + # App-email (managed relay). Injected by the controller only when app-email is on (global + + # per-app); empty SMTP_HOST keeps Rallly mail disabled. Rallly (Nodemailer) reads these at send + # time. SMTP_SECURE=false → STARTTLS to the shim; SMTP_REJECT_UNAUTHORIZED=false accepts the + # shim's self-signed cert (v4 flag; v3.x accepts self-signed by default). See .felhom.yml smtp_mapping. + - SMTP_HOST=${SMTP_HOST:-} + - SMTP_PORT=${SMTP_PORT:-587} + - SMTP_SECURE=${SMTP_SECURE:-false} + - SMTP_REJECT_UNAUTHORIZED=${SMTP_REJECT_UNAUTHORIZED:-true} + - NOREPLY_EMAIL=${NOREPLY_EMAIL:-} + - NOREPLY_EMAIL_NAME=${NOREPLY_EMAIL_NAME:-Felhom} networks: - traefik-public - rallly-internal