fix(security): stop trusting client-supplied forwarded headers in rate limiting
extract_client_ip took X-Real-IP/X-Forwarded-For from any request, so a client talking to the backend directly (the FIPS peer listener, or any non-proxy path) could rotate a fake IP per request and never trip the login rate limiter. The accept loop now records the TCP peer address in request extensions, and forwarded headers are honored only when the connection itself is from loopback — where nginx overwrites X-Real-IP with the real client address. Direct connections bucket under their socket IP. §C of the 1.8.0 hardening plan; 3 new unit tests cover the loopback/direct/no-header matrix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
bd7edb4376
commit
9020b8526c
@@ -131,14 +131,25 @@ modules; production request/boot paths are essentially panic-free. The real risk
|
||||
revalidate) instead of blocking on systemctl. `image_verifier.rs` cosign sites have no
|
||||
callers yet — handled with the §A cosign item. Tests: container 155 / transport 29 /
|
||||
config 29 / package 46 all green.
|
||||
- [ ] 🟡 **Restrict Bitcoin RPC exposure.** `bootstrap.rs:409` writes
|
||||
`rpcallowip=0.0.0.0/0`. Scope to the container subnet / `127.0.0.1`.
|
||||
- [ ] 🟡 **Restrict Bitcoin RPC exposure.** INVESTIGATED 2026-07-03 — the fix is NOT
|
||||
`rpcallowip`: under rootless-podman NAT every forwarded connection reaches bitcoind
|
||||
with an in-subnet source IP, so scoping `rpcallowip` blocks nothing (and container
|
||||
consumers use archy-net DNS anyway). The actual exposure is the host-side publish
|
||||
`8332:8332` (binds 0.0.0.0 → LAN can hit RPC, auth-only barrier; 4 write sites:
|
||||
`bootstrap.rs:424`, `package/config.rs:694`, `package/install.rs:1333/2450`, plus
|
||||
the knots manifest). Real fix = `127.0.0.1:8332:8332` host bind (P2P 8333 stays
|
||||
public; zmq 28332/28333 should get the same look — unauthenticated). ⚠ May break
|
||||
external wallets pointed straight at nodeIP:8332 — needs a user call + on-node gate
|
||||
re-run, so NOT changed drive-by from the dev box.
|
||||
- [ ] 🟡 **Move generated secrets from env to file mounts.** `manifest.rs:1208-1226`
|
||||
injects secrets as `-e KEY=value`, readable via `podman inspect` / `/proc/<pid>/environ`.
|
||||
Prefer bind-mounting the existing `0600` secret file or `podman --secret`.
|
||||
- [ ] 🟡 **Harden rate-limit IP extraction.** `middleware.rs:120-128` trusts
|
||||
client-spoofable `X-Real-IP`/`X-Forwarded-For` → per-request bucket rotation defeats the
|
||||
login limiter. Trust forwarded headers only from a configured proxy; have nginx set them.
|
||||
- [x] 🟡 **Harden rate-limit IP extraction.** DONE 2026-07-03: the accept loop injects the
|
||||
TCP `PeerAddr` into request extensions; `extract_client_ip` honors
|
||||
`X-Real-IP`/`X-Forwarded-For` ONLY when the connection is from loopback (our nginx,
|
||||
which sets `X-Real-IP $remote_addr`) — direct connections (e.g. the FIPS peer
|
||||
listener) bucket under their socket IP, so per-request header rotation no longer
|
||||
defeats the login limiter. 3 unit tests.
|
||||
- [ ] 🟢 **Include `seq` in the mesh signed preimage.** `message_types.rs:245-288` signs
|
||||
`(t,v,ts)` but sets the anti-replay `seq` after signing → a radio MITM can alter ordering
|
||||
without breaking the signature.
|
||||
|
||||
Reference in New Issue
Block a user