mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-19 00:56:16 +00:00
feat(handlers): proxy-aware RemoteAddr via opt-in TrustForwardedHeaders
Wire up X-Real-IP / X-Forwarded-For / True-Client-IP support for deployments fronted by a reverse proxy, while staying safe on flat-LAN deployments where a malicious speaker could spoof those headers directly. Two new fields on `datastore.Settings`: * TrustForwardedHeaders (bool, default false) — opt-in switch. * TrustedProxyCIDRs ([]string, default `["127.0.0.0/8", "::1/128"]`) — only requests whose immediate TCP peer falls in one of these blocks may have their source IP rewritten from forwarded headers. Loopback default matches the documented same-host nginx layout in docs/guides/HTTPS-SETUP.md. New middleware in `pkg/service/handlers/middleware_realip.go`: * TrustedRealIP wraps `chi/middleware.RealIP` with a trusted-peer gate. When the immediate TCP peer is in the allowlist, chi's parsing handles the actual header → IP rewrite. When it isn't (e.g. a speaker sending forwarded headers itself), we ignore the headers and r.RemoteAddr stays as-is. * ParseTrustedProxyCIDRs converts string CIDRs into *net.IPNet, applying the loopback default on empty input and erroring loudly on invalid entries. Server.TrustedRealIPMiddleware() returns the middleware (or nil) by reading the live settings; the router setup in cmd/soundtouch-service/main.go installs it as the very first middleware so SnapshotMiddleware and downstream handlers see the correct r.RemoteAddr. HandleMargePowerOn now prefers r.RemoteAddr over the body's self-reported `<IPAddress>` for outbound credential push: * The body field is treated as a hint only — a malicious LAN speaker could set it to any value; using it for outbound HTTP requests is the SSRF surface the previous zeroconf hardening was guarding against from the sink side. Fixing it at the source as well closes the gap entirely. * When body IP and TCP source disagree, a log line names both and the device ID so the discrepancy is investigable. * RemoteAddr is unparseable → fall back to the body so we don't silently drop the priming. docs/guides/HTTPS-SETUP.md gains a follow-up note next to the existing nginx snippet explaining the new flag, the loopback-only default, and the explicit warning against enabling the flag on a flat-LAN deployment without a real proxy. Eleven test cases in middleware_realip_test.go lock in the gate behaviour: trusted peers honoured for X-Real-IP / X-Forwarded-For / no-headers / IPv6, untrusted peers' headers ignored, garbage values rejected, ParseTrustedProxyCIDRs covers default / override / invalid. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
dc1f811a81
commit
f951fc92df
@@ -70,6 +70,21 @@ server {
|
||||
}
|
||||
```
|
||||
|
||||
> **Tell the service to honour `X-Real-IP`/`X-Forwarded-For`.** When deploying
|
||||
> behind a reverse proxy on the same host as above, set
|
||||
> `"trust_forwarded_headers": true` in `data/settings.json`. With that flag
|
||||
> on, the service rewrites `r.RemoteAddr` from the proxy-supplied headers,
|
||||
> so handlers that act on the source IP (e.g. the Spotify priming triggered
|
||||
> by `/marge/streaming/support/power_on`) see the speaker's real address
|
||||
> instead of the proxy's loopback peer.
|
||||
>
|
||||
> By default only `127.0.0.0/8` and `::1/128` are trusted to set those
|
||||
> headers. If your reverse proxy lives on a different host, list its CIDR(s)
|
||||
> in `"trusted_proxy_cidrs"` (e.g. `["10.0.0.0/8"]`). Do **not** enable
|
||||
> `trust_forwarded_headers` on a flat LAN deployment without a proxy: a
|
||||
> malicious speaker on the LAN can send the headers itself and spoof its
|
||||
> source IP.
|
||||
|
||||
---
|
||||
|
||||
## Manual CA injection (advanced)
|
||||
|
||||
Reference in New Issue
Block a user