Skip to content

fix(wolf-rbac): use trusted client IP source for access_check#13329

Merged
shreemaan-abhishek merged 3 commits into
apache:masterfrom
shreemaan-abhishek:fix/wolf-rbac-x-real-ip-spoofing
May 11, 2026
Merged

fix(wolf-rbac): use trusted client IP source for access_check#13329
shreemaan-abhishek merged 3 commits into
apache:masterfrom
shreemaan-abhishek:fix/wolf-rbac-x-real-ip-spoofing

Conversation

@shreemaan-abhishek

Copy link
Copy Markdown
Contributor

Description

The wolf-rbac plugin populates the clientIP parameter sent to wolf-server's /wolf/rbac/access_check endpoint by reading ctx.var.http_x_real_ip, the raw X-Real-IP request header. This bypasses nginx's real_ip module: even though APISIX configures real_ip_from = { "127.0.0.1", "unix:" } by default — meaning nginx correctly rejects X-Real-IP from untrusted sources when computing $remote_addr — the plugin reads the original header directly, so any external client can supply an arbitrary clientIP value.

This change replaces the raw-header read with core.request.get_remote_client_ip(ctx), which returns $remote_addr after real_ip processing. The new behavior is:

  • Behind a trusted proxy (listed in real_ip_from): forwards the real client IP from the trusted header — same behavior the previous code intended.
  • From untrusted sources: forwards the actual TCP peer address; the spoofed header is ignored, matching nginx's standard handling.

Which issue(s) this PR fixes:

Fixes #

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change
  • I have verified that this change is backward compatible (If not, please discuss on the APISIX mailing list first)

The plugin previously read the raw `X-Real-IP` request header to populate
the `clientIP` parameter sent to wolf-server, falling back to
`core.request.get_ip` only if the header was absent. Reading the raw
header bypasses nginx's `real_ip` module, so any client could supply an
arbitrary value regardless of the configured `real_ip_from` trusted
proxy chain.

Switch to `core.request.get_remote_client_ip`, which returns
`$remote_addr` after `real_ip` processing — both correct when APISIX is
behind a trusted proxy and unspoofable from untrusted sources.
@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. bug Something isn't working labels May 5, 2026
Add regression coverage for the IP source change in wolf-rbac:

- Mock wolf-server now logs the `clientIP` query arg it receives, so
  tests can assert what the plugin forwarded.
- TEST 41: when the source is in `set_real_ip_from`, an `X-Real-IP`
  header is honored and forwarded to wolf-server.
- TEST 42: when the source is NOT in `set_real_ip_from`, a spoofed
  `X-Real-IP` is ignored and the actual peer address is forwarded.
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels May 5, 2026
The same plugin still called `core.request.get_ip` for the missing-token
log line in `get_wolf_token`. It was non-spoofable but inaccurate behind
a trusted proxy, so two paths in one file resolved client IP differently.

Switch to `core.request.get_remote_client_ip` here as well so the whole
plugin uses one consistent source.
@shreemaan-abhishek
shreemaan-abhishek merged commit 1b1ce5a into apache:master May 11, 2026
22 of 23 checks passed
shreemaan-abhishek added a commit that referenced this pull request May 13, 2026
* fix(chaitin-waf): use trusted client IP source for WAF backend

The plugin populated `client_ip` sent to the Chaitin WAF backend by
reading `ctx.var.http_x_forwarded_for` directly when `real_client_ip`
is enabled, bypassing nginx's `real_ip` module. Any external client
could supply an arbitrary `X-Forwarded-For` header and have it
forwarded to the WAF unchecked.

Replace the raw-header read with `core.request.get_remote_client_ip`
(realip-aware) when `real_client_ip = true`, and `core.request.get_ip`
(direct TCP peer) when false. This applies the same fix shape used
for wolf-rbac in #13329.

* fix(chaitin-waf): preserve real_client_ip=false in get_conf merge

`get_conf` merged `real_client_ip` with `or`, which silently fell
back to the default `true` whenever the user explicitly set `false`,
making the toggle ineffective. Replace with an explicit nil check so
both `true` and `false` overrides are honored.

Also tighten the existing `real_client_ip = false` test to assert the
chosen `client_ip` (not just the response), and add a regression case
that verifies the `false` branch wins over `set_real_ip_from`-trusted
`X-Forwarded-For` rewrites.

* docs(chaitin-waf): clarify how real_client_ip selects the client IP

Describe the two `config.real_client_ip` modes in terms of the value
the plugin sends to the WAF service: APISIX's resolved client IP
(true) versus the IP of the directly connected peer (false). The
previous wording referenced only `X-Forwarded-For`, which no longer
matches the resolution path.
wistefan pushed a commit to wistefan/apisix that referenced this pull request Jun 16, 2026
…#13329)

* fix(wolf-rbac): use trusted client IP source for access_check

The plugin previously read the raw `X-Real-IP` request header to populate
the `clientIP` parameter sent to wolf-server, falling back to
`core.request.get_ip` only if the header was absent. Reading the raw
header bypasses nginx's `real_ip` module, so any client could supply an
arbitrary value regardless of the configured `real_ip_from` trusted
proxy chain.

Switch to `core.request.get_remote_client_ip`, which returns
`$remote_addr` after `real_ip` processing — both correct when APISIX is
behind a trusted proxy and unspoofable from untrusted sources.

* test(wolf-rbac): assert clientIP respects trusted-proxy chain

Add regression coverage for the IP source change in wolf-rbac:

- Mock wolf-server now logs the `clientIP` query arg it receives, so
  tests can assert what the plugin forwarded.
- TEST 41: when the source is in `set_real_ip_from`, an `X-Real-IP`
  header is honored and forwarded to wolf-server.
- TEST 42: when the source is NOT in `set_real_ip_from`, a spoofed
  `X-Real-IP` is ignored and the actual peer address is forwarded.

* fix(wolf-rbac): also use trusted client IP in change_pwd log path

The same plugin still called `core.request.get_ip` for the missing-token
log line in `get_wolf_token`. It was non-spoofable but inaccurate behind
a trusted proxy, so two paths in one file resolved client IP differently.

Switch to `core.request.get_remote_client_ip` here as well so the whole
plugin uses one consistent source.
wistefan pushed a commit to wistefan/apisix that referenced this pull request Jun 16, 2026
…e#13339)

* fix(chaitin-waf): use trusted client IP source for WAF backend

The plugin populated `client_ip` sent to the Chaitin WAF backend by
reading `ctx.var.http_x_forwarded_for` directly when `real_client_ip`
is enabled, bypassing nginx's `real_ip` module. Any external client
could supply an arbitrary `X-Forwarded-For` header and have it
forwarded to the WAF unchecked.

Replace the raw-header read with `core.request.get_remote_client_ip`
(realip-aware) when `real_client_ip = true`, and `core.request.get_ip`
(direct TCP peer) when false. This applies the same fix shape used
for wolf-rbac in apache#13329.

* fix(chaitin-waf): preserve real_client_ip=false in get_conf merge

`get_conf` merged `real_client_ip` with `or`, which silently fell
back to the default `true` whenever the user explicitly set `false`,
making the toggle ineffective. Replace with an explicit nil check so
both `true` and `false` overrides are honored.

Also tighten the existing `real_client_ip = false` test to assert the
chosen `client_ip` (not just the response), and add a regression case
that verifies the `false` branch wins over `set_real_ip_from`-trusted
`X-Forwarded-For` rewrites.

* docs(chaitin-waf): clarify how real_client_ip selects the client IP

Describe the two `config.real_client_ip` modes in terms of the value
the plugin sends to the WAF service: APISIX's resolved client IP
(true) versus the IP of the directly connected peer (false). The
previous wording referenced only `X-Forwarded-For`, which no longer
matches the resolution path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants