Files
pad/internal/server
xarmian fc5a54dff7 fix(server): read raw TCP peer for loopback check (TASK-662) (#175)
* fix(server): read raw TCP peer for loopback check (TASK-662)

TrustedProxyRealIP rewrites r.RemoteAddr when the peer is a trusted
proxy. Without additional defense, an attacker reaching a trusted
reverse proxy could set X-Forwarded-For: 127.0.0.1 and trick the
bootstrap loopback check into accepting them as a local caller —
reopening the full-instance-takeover path that TASK-660 closed at the
spoof layer.

Add CapturePeerAddr middleware that runs BEFORE TrustedProxyRealIP and
stashes the untampered r.RemoteAddr in request context. Change
requestIsLoopback to read via rawPeerAddr(r) (context-first, with a
safe fallback for test paths that skip the middleware). r.RemoteAddr
stays the rewritten value for the rate-limiter / audit-log paths that
actually want the client's IP.

Tests cover: direct loopback → true; direct LAN → false; trusted
proxy forwarding spoofed 127.0.0.1 → false; untrusted peer with
spoofed XFF=127.0.0.1 → false; and that rawPeerAddr falls back to
r.RemoteAddr when CapturePeerAddr is absent.

Parent: PLAN-643 (OSS Security Hardening).

* fix(server): require loopback peer AND no proxy headers for bootstrap (Codex P1)

Codex caught a regression in the initial PR: reading rawPeerAddr(r) made
every request through a same-host reverse proxy look loopback, so a Caddy
or nginx on 127.0.0.1 forwarding public traffic would let attackers reach
the bootstrap endpoint from the internet.

Tighten the rule to two independent conditions:
 1. The untampered TCP peer is a loopback address.
 2. Neither X-Forwarded-For nor X-Real-IP is set.

A legitimate local CLI calling Pad directly satisfies both. A reverse
proxy forwarding public traffic always sets the forwarding headers, so
the presence of either disqualifies the request. The raw-peer check
still defeats X-Forwarded-For spoofing from non-loopback attackers, and
now also handles the Codex-flagged scenario where a local proxy is
trusted or left misconfigured.

Tests updated to cover: direct loopback no-headers allowed; loopback
peer + XFF rejected; loopback peer + X-Real-IP rejected; IPv6 loopback
allowed.
2026-04-21 19:33:47 -04:00
..
2026-03-26 01:52:36 +00:00
2026-03-26 01:52:36 +00:00