diff --git a/docs/content/docs/guides/CLOUD-DEPLOY-WALKTHROUGH.md b/docs/content/docs/guides/CLOUD-DEPLOY-WALKTHROUGH.md index 613fa8b..e9d7c04 100644 --- a/docs/content/docs/guides/CLOUD-DEPLOY-WALKTHROUGH.md +++ b/docs/content/docs/guides/CLOUD-DEPLOY-WALKTHROUGH.md @@ -46,6 +46,40 @@ Minimum mitigations before going live: --- +## Client IP behind a proxy or load balancer + +Behind a reverse proxy or load balancer, the connection AfterTouch sees comes +from the proxy, not from the speaker. A few handlers act on the source IP (for +example the Spotify priming triggered by `/marge/streaming/support/power_on`, +and the device IP AfterTouch records), so in a proxied setup you usually want +it to recover the real speaker IP from the `X-Forwarded-For` header. + +Enable it in `data/settings.json`: + +- Set `"trust_forwarded_headers": true`. +- Set `"trusted_proxy_cidrs"` to your proxy's own IP range(s), for example + `["10.0.0.0/8"]`. It defaults to loopback (`127.0.0.0/8`, `::1/128`), which + already covers a proxy running on the same host. + +Make sure the proxy sets the header (nginx: +`proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`). Only +`X-Forwarded-For` is consulted (not `X-Real-IP` or `True-Client-IP`). + +| Deployment | `trust_forwarded_headers` | Client IP AfterTouch uses | +|---------------------------------------------------|---------------------------|----------------------------------------------------------------------------------------| +| Direct LAN / on-device (no proxy) | `false` (default) | the connecting socket's IP; `X-Forwarded-For` is ignored | +| Behind a proxy listed in `trusted_proxy_cidrs` | `true` | the rightmost `X-Forwarded-For` entry outside `trusted_proxy_cidrs` (the real speaker) | +| A request that did not arrive via a trusted proxy | `true` | the socket IP; its `X-Forwarded-For` is ignored (spoofing protection) | + +> **Do not enable `trust_forwarded_headers` on a flat LAN with no proxy.** A +> malicious speaker could then send `X-Forwarded-For` itself and spoof its +> source IP. A missing or unparseable header always falls back to the socket IP. + +For terminating TLS at the proxy (serving the certificate on `:443`), see the +[reverse proxy section of the HTTPS guide](HTTPS-SETUP.md#reverse-proxy-optional). + +--- + ## Step 1 — Deploy AfterTouch on your server ### Docker / Docker Compose (any VPS) diff --git a/docs/content/docs/guides/HTTPS-SETUP.md b/docs/content/docs/guides/HTTPS-SETUP.md index 6aeeeee..977ca20 100644 --- a/docs/content/docs/guides/HTTPS-SETUP.md +++ b/docs/content/docs/guides/HTTPS-SETUP.md @@ -125,20 +125,11 @@ server { } ``` -> **Tell the service to resolve the client IP from `X-Forwarded-For`.** When -> deploying behind a reverse proxy, set `"trust_forwarded_headers": true` in -> `data/settings.json`. With that flag on, the service reads the real client -> IP from `X-Forwarded-For` (chi walks the chain right-to-left, skipping your -> trusted-proxy IPs), so handlers that act on the source IP (e.g. the Spotify -> priming from `/marge/streaming/support/power_on`) see the speaker's real -> address instead of the proxy's. -> -> By default only `127.0.0.0/8` and `::1/128` are trusted proxy ranges. If -> your reverse proxy lives on a different host, list its CIDR(s) in -> `"trusted_proxy_cidrs"` (the proxy's own IP ranges, e.g. -> `["10.0.0.0/8"]`). Do **not** enable `trust_forwarded_headers` on a flat -> LAN with no proxy: a malicious speaker on the LAN could send -> `X-Forwarded-For` itself and spoof its source IP. +> **Client IP behind a proxy.** A reverse proxy changes the source IP the +> service sees, which matters for the handlers that act on it. Configuring +> AfterTouch to recover the real speaker IP from `X-Forwarded-For` +> (`trust_forwarded_headers` / `trusted_proxy_cidrs`) is covered under +> [Client IP behind a proxy or load balancer](CLOUD-DEPLOY-WALKTHROUGH.md#client-ip-behind-a-proxy-or-load-balancer). ---