Skip to content

fix(authz): judge the IP allow/deny list by the connection unless a t… - #15038

Merged
diegosouzapw merged 1 commit into
diegosouzapw:release/v3.8.51from
HouMinXi:fix/ip-filter-forged-forwarding-headers
Sep 29, 2026
Merged

diegosouzapw merged 1 commit into
diegosouzapw:release/v3.8.51from
HouMinXi:fix/ip-filter-forged-forwarding-headers

Conversation

@HouMinXi

Copy link
Copy Markdown
Contributor

…rusted proxy fronts it

The server stamps every request with the socket peer and marks it as proxied when X-Forwarded-For or X-Real-IP is present. For a proxied request the IP filter drops the socket peer and takes the client address from the forwarding headers. Any direct client can write those headers, so a blacklisted address could send X-Forwarded-For: <clean address> and be let through, and an address outside the whitelist could claim one that is on it.

Only treat the two headers as a proxy signal when the connection comes from this host, a private-network address or a Cloudflare edge, the same peers the loopback and LAN checks already recognise. From any other address the request is judged by its own peer address. CF-Connecting-IP was already limited to Cloudflare edges as a marker.

Behind a trusted proxy the client address was still whatever came first. The server now works it out where the socket is known and stamps it, token protected like the peer, for the filter to use: a Cloudflare edge's CF-Connecting-IP (a proxy that is not Cloudflare no longer gets to have it believed), else the right-most X-Forwarded-For entry that is not itself a trusted proxy, since a proxy appends to what the client sent and the entries before its own are the client's, else X-Real-IP. A header holding no usable address leaves the peer as the client, instead of judging "unknown", which every list lets through.

A reverse proxy on any other public address would now be judged as the client, which under a whitelist locks everybody out. Such a proxy is named in the new OMNIROUTE_TRUSTED_PROXIES (IPs or CIDR ranges), the same idea as the existing OMNIROUTE_TRUST_PROXY but for the peer address. Loopback, private-network and Cloudflare peers stay trusted without it. A client that sits on a private address itself can still claim another address, which is the price of trusting that whole range without configuration. The peer stamp tests now cover the new marker and client stamp, and a parity test keeps the private ranges in step with the route guard's.

Summary

  • Describe the user-facing or operational change.

Related Issues

  • Closes #
  • Related to #

Validation

Choose the change type and focused loop from the
Contribution Golden Path. The full unit suite,
Vitest, the 60% coverage gate, and the production build all run in CI on this PR (#8329):

  • Change type: provider / routing / UI / i18n / CLI / DB / build-deploy / other
  • Focused tests and category gates from the golden path
  • npm run lint
  • Reconciled with the current active release base; focused checks rerun afterward
  • Production-code changes include a new or updated automated test in this PR
  • SonarQube is temporarily opt-in while the private project has no quota; it is not a PR gate.

Tests Added Or Updated

  • List every changed or added automated test file.
  • If no production code changed, state that here.

Coverage Notes

  • If this PR changes src/, open-sse/, electron/, or bin/, explain which tests cover the change.
  • If coverage moved down in any touched file, explain why and what follow-up task will recover it.

Reviewer Notes

  • Call out any risky areas, migrations, feature flags, or manual validation that reviewers should know about.

…rusted proxy fronts it

The server stamps every request with the socket peer and marks it as proxied
when X-Forwarded-For or X-Real-IP is present. For a proxied request the IP
filter drops the socket peer and takes the client address from the forwarding
headers. Any direct client can write those headers, so a blacklisted address
could send `X-Forwarded-For: <clean address>` and be let through, and an
address outside the whitelist could claim one that is on it.

Only treat the two headers as a proxy signal when the connection comes from
this host, a private-network address or a Cloudflare edge, the same peers the
loopback and LAN checks already recognise. From any other address the request
is judged by its own peer address. CF-Connecting-IP was already limited to
Cloudflare edges as a marker.

Behind a trusted proxy the client address was still whatever came first. The
server now works it out where the socket is known and stamps it, token
protected like the peer, for the filter to use: a Cloudflare edge's
CF-Connecting-IP (a proxy that is not Cloudflare no longer gets to have it
believed), else the right-most X-Forwarded-For entry that is not itself a
trusted proxy, since a proxy appends to what the client sent and the entries
before its own are the client's, else X-Real-IP. A header holding no usable
address leaves the peer as the client, instead of judging "unknown", which
every list lets through.

A reverse proxy on any other public address would now be judged as the client,
which under a whitelist locks everybody out. Such a proxy is named in the new
OMNIROUTE_TRUSTED_PROXIES (IPs or CIDR ranges), the same idea as the existing
OMNIROUTE_TRUST_PROXY but for the peer address. Loopback, private-network and
Cloudflare peers stay trusted without it. A client that sits on a private
address itself can still claim another address, which is the price of trusting
that whole range without configuration. The peer stamp tests now cover the
new marker and client stamp, and a parity test keeps the private ranges in step
with the route guard's.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
@diegosouzapw
diegosouzapw merged commit 1394706 into diegosouzapw:release/v3.8.51 Sep 29, 2026
9 of 16 checks passed
diegosouzapw pushed a commit that referenced this pull request Sep 29, 2026
…quest (#15060)

Maintainer rework: reconciled with #15038/#15040 on the tip — the wider forwarding-header set only sets the via-proxy marker from a peer that may be a proxy (hasProxyHopHeader && isTrustedProxyPeer). Loopback without headers stays local. 35 peer-stamp/route-guard/live-WS test files green. Board-validated with the rest of the batch on release/v3.8.51: typecheck:core, check:open-sse-typecheck, check-file-size, eslint on changed files, i18n:check-keys all green. Thank you @HouMinXi!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants