diff --git a/apisix/init.lua b/apisix/init.lua index 01838da5b0f4..56456c09a692 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -647,8 +647,9 @@ local function handle_x_forwarded_headers(api_ctx) core.request.set_header(api_ctx, "X-Forwarded-Proto", proto) core.request.set_header(api_ctx, "X-Forwarded-Host", host) core.request.set_header(api_ctx, "X-Forwarded-Port", port) - -- later processed in ngx_tpl by `$proxy_add_x_forwarded_for`. - core.request.set_header(api_ctx, "X-Forwarded-For", nil) + -- X-Forwarded-For is kept as-is: ngx_tpl appends the trusted + -- connection IP via `$proxy_add_x_forwarded_for`, so the upstream + -- always sees the real client IP as the last hop of the chain. -- Clear RFC 7239 Forwarded header to prevent forgery. core.request.set_header(api_ctx, "Forwarded", nil) @@ -658,7 +659,6 @@ local function handle_x_forwarded_headers(api_ctx) api_ctx.var.http_x_forwarded_proto = proto api_ctx.var.http_x_forwarded_host = host api_ctx.var.http_x_forwarded_port = port - api_ctx.var.http_x_forwarded_for = nil api_ctx.var.http_forwarded = nil end end diff --git a/conf/config.yaml.example b/conf/config.yaml.example index 0a129a5ac83c..1a12b7dace4f 100644 --- a/conf/config.yaml.example +++ b/conf/config.yaml.example @@ -154,8 +154,11 @@ apisix: # When set to true, it overrides all upstream healthcheck configurations and globally disabling healthchecks. # trusted_addresses: # When configured, APISIX will trust the `X-Forwarded-*` Headers # - 127.0.0.1 # passed in requests from the IP/CIDR in the list. -# - 172.18.0.0/16 # CAUTION: When not configured or the request from an untrusted address, - # APISIX will override `X-Forwarded-*` headers with trusted values. +# - 172.18.0.0/16 # CAUTION: When not configured or the request is from an untrusted + # address, APISIX overrides `X-Forwarded-Proto/Host/Port` with trusted + # values and clears the `Forwarded` header. `X-Forwarded-For` is kept + # and the trusted connection IP is appended, so the upstream still + # receives the real client IP as the last hop of the chain. # fine tune the parameters of LRU cache for some features like secret lru: secret: diff --git a/docs/en/latest/plugins/chaitin-waf.md b/docs/en/latest/plugins/chaitin-waf.md index 16a658791b30..d0f2b87c2ad8 100644 --- a/docs/en/latest/plugins/chaitin-waf.md +++ b/docs/en/latest/plugins/chaitin-waf.md @@ -90,7 +90,7 @@ The examples below demonstrate how you can configure chaitin-waf Plugin for diff Before proceeding, make sure you have installed [Chaitin WAF (SafeLine)](https://docs.waf.chaitin.com/en/GetStarted/Deploy). :::note -Only `X-Forwarded-*` headers sent from addresses in the `apisix.trusted_addresses` configuration (supports IP and CIDR) will be trusted and passed to plugins or upstream. If `apisix.trusted_addresses` is not configured or the IP is not within the configured address range, all `X-Forwarded-*` headers will be overridden with trusted values. +Only `X-Forwarded-*` headers sent from addresses in the `apisix.trusted_addresses` configuration (supports IP and CIDR) will be trusted and passed to plugins or upstream. If `apisix.trusted_addresses` is not configured or the IP is not within the configured address range, the `X-Forwarded-Proto`, `X-Forwarded-Host`, and `X-Forwarded-Port` headers are overridden with trusted values and the `Forwarded` header is cleared. The `X-Forwarded-For` header is kept and the trusted connection IP is appended, so the upstream still receives the real client IP as the last hop of the chain. ::: :::note diff --git a/docs/en/latest/plugins/real-ip.md b/docs/en/latest/plugins/real-ip.md index 1d9977906503..4bf5e2b30b7a 100644 --- a/docs/en/latest/plugins/real-ip.md +++ b/docs/en/latest/plugins/real-ip.md @@ -49,7 +49,7 @@ The Plugin is functionally similar to NGINX's [ngx_http_realip_module](https://n | recursive | boolean | False | False | | If false, replace the original client address that matches one of the trusted addresses by the last address sent in the configured `source`.
If true, replace the original client address that matches one of the trusted addresses by the last non-trusted address sent in the configured `source`. | :::note -Only `X-Forwarded-*` headers sent from addresses in the `apisix.trusted_addresses` configuration (supports IP and CIDR) will be trusted and passed to plugins or upstream. If `apisix.trusted_addresses` is not configured or the IP is not within the configured address range, all `X-Forwarded-*` headers will be overridden with trusted values. +Only `X-Forwarded-*` headers sent from addresses in the `apisix.trusted_addresses` configuration (supports IP and CIDR) will be trusted and passed to plugins or upstream. If `apisix.trusted_addresses` is not configured or the IP is not within the configured address range, the `X-Forwarded-Proto`, `X-Forwarded-Host`, and `X-Forwarded-Port` headers are overridden with trusted values and the `Forwarded` header is cleared. The `X-Forwarded-For` header is kept and the trusted connection IP is appended, so the upstream still receives the real client IP as the last hop of the chain. ::: :::note diff --git a/docs/zh/latest/plugins/chaitin-waf.md b/docs/zh/latest/plugins/chaitin-waf.md index 7a6193f7d818..428481e88cf8 100644 --- a/docs/zh/latest/plugins/chaitin-waf.md +++ b/docs/zh/latest/plugins/chaitin-waf.md @@ -90,7 +90,7 @@ description: chaitin-waf 插件与长亭雷池 WAF 集成,以检测和阻止 继续操作之前,请确保您已安装 [长亭雷池 WAF](https://docs.waf.chaitin.com/en/GetStarted/Deploy)。 :::note -只有发送自 `apisix.trusted_addresses` 配置(支持 IP 和 CIDR)地址的 `X-Forwarded-*` 头才会被信任,并传递给插件或上游。如果未配置 `apisix.trusted_addresses` 或 ip 不在配置地址范围内的,`X-Forwarded-*` 头将全部被可信值覆盖。 +只有发送自 `apisix.trusted_addresses` 配置(支持 IP 和 CIDR)地址的 `X-Forwarded-*` 头才会被信任,并传递给插件或上游。如果未配置 `apisix.trusted_addresses` 或 ip 不在配置地址范围内的,`X-Forwarded-Proto`、`X-Forwarded-Host`、`X-Forwarded-Port` 头将被可信值覆盖,并清除 `Forwarded` 头。`X-Forwarded-For` 头会被保留,并追加可信的连接 IP,因此上游仍可在该头链的最后一跳获取到真实客户端 IP。 ::: :::note diff --git a/docs/zh/latest/plugins/real-ip.md b/docs/zh/latest/plugins/real-ip.md index c8628b719ca1..64b54caf61f8 100644 --- a/docs/zh/latest/plugins/real-ip.md +++ b/docs/zh/latest/plugins/real-ip.md @@ -49,7 +49,7 @@ import TabItem from '@theme/TabItem'; | recursive | boolean | 否 | false | | 如果为 false,则将匹配可信地址之一的原始客户端地址替换为配置的 `source` 中发送的最后一个地址。
如果为 true,则将匹配可信地址之一的原始客户端地址替换为配置的 `source` 中发送的最后一个非可信地址。 | :::note -只有发送自 `apisix.trusted_addresses` 配置 (支持 IP 和 CIDR) 地址的 `X-Forwarded-*` 头才会被信任,并传递给插件或上游。如果未配置 `apisix.trusted_addresses` 或 ip 不在配置地址范围内的,`X-Forwarded-*` 头将全部被可信值覆盖。 +只有发送自 `apisix.trusted_addresses` 配置 (支持 IP 和 CIDR) 地址的 `X-Forwarded-*` 头才会被信任,并传递给插件或上游。如果未配置 `apisix.trusted_addresses` 或 ip 不在配置地址范围内的,`X-Forwarded-Proto`、`X-Forwarded-Host`、`X-Forwarded-Port` 头将被可信值覆盖,并清除 `Forwarded` 头。`X-Forwarded-For` 头会被保留,并追加可信的连接 IP,因此上游仍可在该头链的最后一跳获取到真实客户端 IP。 ::: :::note diff --git a/t/core/trusted-addresses.t b/t/core/trusted-addresses.t index c8a0fc6fafc9..e7ec079aec3d 100644 --- a/t/core/trusted-addresses.t +++ b/t/core/trusted-addresses.t @@ -32,7 +32,7 @@ run_tests(); __DATA__ -=== TEST 1: without trusted_addresses configuration, X-Forwarded headers should be overridden +=== TEST 1: without trusted_addresses, X-Forwarded-For is preserved while others are overridden --- yaml_config apisix: node_listen: 1984 @@ -54,13 +54,14 @@ routes: --- request GET /old_uri --- more_headers +X-Forwarded-For: 1.2.3.4 X-Forwarded-Proto: https X-Forwarded-Host: example.com X-Forwarded-Port: 8443 --- response_body uri: /old_uri host: localhost -x-forwarded-for: 127.0.0.1 +x-forwarded-for: 1.2.3.4, 127.0.0.1 x-forwarded-host: localhost x-forwarded-port: 1984 x-forwarded-proto: http @@ -95,13 +96,14 @@ routes: --- request GET /old_uri --- more_headers +X-Forwarded-For: 1.2.3.4 X-Forwarded-Proto: https X-Forwarded-Host: example.com X-Forwarded-Port: 8443 --- response_body uri: /old_uri host: localhost -x-forwarded-for: 127.0.0.1 +x-forwarded-for: 1.2.3.4, 127.0.0.1 x-forwarded-host: example.com x-forwarded-port: 8443 x-forwarded-proto: https @@ -319,7 +321,7 @@ x-real-ip: 127.0.0.1 -=== TEST 8: with trusted_addresses configuration, but client not in trusted list, X-Forwarded headers should be overridden +=== TEST 8: client not in trusted list, X-Forwarded-For is preserved while others are overridden --- yaml_config apisix: node_listen: 1984 @@ -344,13 +346,14 @@ routes: --- request GET /old_uri --- more_headers +X-Forwarded-For: 1.2.3.4 X-Forwarded-Proto: https X-Forwarded-Host: example.com X-Forwarded-Port: 8443 --- response_body uri: /old_uri host: localhost -x-forwarded-for: 127.0.0.1 +x-forwarded-for: 1.2.3.4, 127.0.0.1 x-forwarded-host: localhost x-forwarded-port: 1984 x-forwarded-proto: http diff --git a/t/plugin/real-ip.t b/t/plugin/real-ip.t index fafe0b4a5842..4af911e705e9 100644 --- a/t/plugin/real-ip.t +++ b/t/plugin/real-ip.t @@ -473,8 +473,7 @@ X-Forwarded-For: 1.1.1.1, 192.128.1.1, 127.0.0.1 -=== TEST 24: trusted in real-ip, but not trusted by `apisix.trusted_addresses` -should be rejected +=== TEST 24: untrusted by `apisix.trusted_addresses`, but real-ip still honors X-Forwarded-For --- yaml_config apisix: node_listen: 1984 @@ -505,4 +504,4 @@ routes: GET /hello --- more_headers X-Forwarded-For: 1.1.1.1 ---- error_code: 403 +--- error_code: 200