docs(env): document TRUST_CF_CONNECTING_IP security implications

The existing .env.example block warned that an attacker could forge
the geo lookup if the flag was enabled on a non-Cloudflare deployment,
but did not name the two additional downstream consumers of
`getClientIp`: the IP-rate-limit bucket key for login/chat/coach and
the audit_logs.ipAddress column for every privileged write. An
operator who deploys behind nginx / Caddy / direct and copy-pastes
the flag from a forum thread would let any caller defeat the
rate-limiter AND forge the IP recorded against their audit trail.

Rewrite the block to list every consumer explicitly and re-emphasise
that the flag is fail-closed (default off, strict "=== '1'" gate, no
server-side topology detection — the operator owns the decision).
This commit is contained in:
Marc-André Bombeck committed 2026-05-17 15:30:34 +02:00
1 parent bd841aee99
commit 10c3f8e667
1 file changed
+21 -4
+21 -4
View File
@@ -118,10 +118,27 @@ API_TOKEN_HMAC_KEY="REPLACE-WITH-OUTPUT-OF-openssl-rand-hex-32"
# `X-Forwarded-For` chain stops at the Coolify / Caddy loopback and the
# admin sign-in geo lookup has no signal to resolve from.
#
# Set this to "1" ONLY when every public request goes through Cloudflare.
# A deployment without Cloudflare in front MUST leave this off — an
# attacker can set the header directly on the public internet and the geo
# resolver would happily report a forged location.
# Set this to "1" ONLY when every public request to HealthLog terminates
# at Cloudflare (Tunnel, Workers, classic proxy) before it reaches your
# origin. Deployments behind nginx, Caddy, Traefik, an AWS ALB, or any
# direct-to-Next.js exposure MUST leave this off.
#
# Security implications when the flag is mis-set:
# - `cf-connecting-ip` becomes the IP-rate-limit bucket key for
# login / chat / coach. An attacker who can set the header rotates
# it per request and defeats every IP-keyed throttle.
# - The same value lands in the `audit_logs.ipAddress` column for
# every `measurement.create`, `medication.intake`, sign-in, and
# admin action. A misconfigured flag lets any caller forge the
# IP recorded against their actions across the audit trail.
# - The admin sign-in overview's "Standort" column reads the same
# value via geo-backfill; a malicious caller can poison the column
# with a chosen IP per request.
#
# Code-level validation is strict (only "=== '1'" enables, value is
# regex- and length-bounded), but the gating decision is up to the
# operator — there is no way for the server to detect whether the flag
# matches the real deployment topology. When in doubt, leave it off.
# TRUST_CF_CONNECTING_IP=""