docs: move proxy client-IP guidance to the deployment walkthrough

Reverse-proxy client-IP resolution (trust_forwarded_headers /
trusted_proxy_cidrs) was documented under HTTPS-SETUP because proxies are
commonly used for TLS termination, but it's really a deployment concern.
Relocate it to CLOUD-DEPLOY-WALKTHROUGH as a "Client IP behind a proxy or
load balancer" section with a behavior table (no-proxy default, trusted-proxy
XFF resolution, and the untrusted-peer spoofing gate). HTTPS-SETUP keeps the
TLS-termination example and now cross-links to it; the deployment section
links back for the cert details.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tobias Gesellchen
2026-06-28 16:28:52 +02:00
co-authored by Claude Opus 4.8
parent 1cac9989be
commit 022cafbe23
2 changed files with 39 additions and 14 deletions
@@ -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)
+5 -14
View File
@@ -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).
---