From b67dc6753fe4d16823b579765a7a2c584fa74a37 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Wed, 27 Aug 2025 15:34:12 +0800 Subject: [PATCH 01/37] feat: use `trusted_addresses` to handle `X-Forwarded-*` headers --- apisix/init.lua | 18 +++++++++ apisix/utils/trusted-addresses.lua | 59 ++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 apisix/utils/trusted-addresses.lua diff --git a/apisix/init.lua b/apisix/init.lua index d56fbc3e1860..1b3c98d93ff9 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -45,6 +45,7 @@ local xrpc = require("apisix.stream.xrpc") local ctxdump = require("resty.ctxdump") local debug = require("apisix.debug") local pubsub_kafka = require("apisix.pubsub.kafka") +local trusted_addresses_util = require("apisix.utils.trusted-addresses") local ngx = ngx local get_method = ngx.req.get_method local ngx_exit = ngx.exit @@ -159,6 +160,8 @@ function _M.http_init_worker() -- To ensure that all workers related to Prometheus metrics are initialized, -- we need to put the initialization of the Prometheus plugin here. plugin.init_prometheus() + + trusted_addresses_util.init_worker() end @@ -599,6 +602,19 @@ function _M.handle_upstream(api_ctx, route, enable_websocket) end +local function handle_x_forwarded_headers(api_ctx) + if trusted_addresses_util.is_trusted(api_ctx.var.remote_addr) then + api_ctx.var.x_forwarded_proto = core.request.header(api_ctx, "X-Forwarded-Proto") or api_ctx.var.scheme + api_ctx.var.x_forwarded_host = core.request.header(api_ctx, "X-Forwarded-Host") or api_ctx.var.host + api_ctx.var.x_forwarded_port = core.request.header(api_ctx, "X-Forwarded-Port") or api_ctx.var.server_port + else + api_ctx.var.x_forwarded_host = api_ctx.var.host + api_ctx.var.x_forwarded_proto = api_ctx.var.scheme + api_ctx.var.x_forwarded_port = api_ctx.var.server_port + end +end + + function _M.http_access_phase() -- from HTTP/3 to HTTP/1.1 we need to convert :authority pesudo-header -- to Host header, so we set upstream_host variable here. @@ -648,6 +664,8 @@ function _M.http_access_phase() api_ctx.var.real_request_uri = api_ctx.var.request_uri api_ctx.var.request_uri = api_ctx.var.uri .. api_ctx.var.is_args .. (api_ctx.var.args or "") + handle_x_forwarded_headers(api_ctx) + router.router_http.match(api_ctx) local route = api_ctx.matched_route diff --git a/apisix/utils/trusted-addresses.lua b/apisix/utils/trusted-addresses.lua new file mode 100644 index 000000000000..470401e32581 --- /dev/null +++ b/apisix/utils/trusted-addresses.lua @@ -0,0 +1,59 @@ +local require = require +local core = require("apisix.core") +local next = next +local ipairs = ipairs + +local trusted_addresses_matcher + +local _M = {} + + +local function validate_trusted_addresses(trusted_addresses) + if trusted_addresses and next(trusted_addresses) then + for _, cidr in ipairs(trusted_addresses) do + if not core.ip.validate_cidr_or_ip(cidr) then + core.log.error("Invalid IP/CIDR '", cidr, "' exists in trusted_addresses") + return false + end + end + end + return true +end + + +function _M.init_worker() + local local_conf = core.config.local_conf() + local trusted_addresses = core.table.try_read_attr(local_conf, "apisix", "trusted_addresses") + + if not trusted_addresses then + return + end + + if not core.table.isarray(trusted_addresses) then + core.log.error("trusted_addresses '", trusted_addresses, "' is not an array, please check your configuration") + return + end + + if not validate_trusted_addresses(trusted_addresses) then + return + end + + local matcher, err = core.ip.create_ip_matcher(trusted_addresses) + if not matcher then + core.log.error("failed to create ip matcher for trusted_addresses: ", err) + return + end + + trusted_addresses_matcher = matcher +end + + +function _M.is_trusted(address) + if not trusted_addresses_matcher then + core.log.info("trusted_addresses_matcher is not initialized, skipping subsequent parsing.") + return false + end + return trusted_addresses_matcher(address) +end + +return _M From 34b5953d52782ab0b3392b4da37e6d824b8bda27 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Wed, 27 Aug 2025 15:41:33 +0800 Subject: [PATCH 02/37] chore --- apisix/utils/trusted-addresses.lua | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apisix/utils/trusted-addresses.lua b/apisix/utils/trusted-addresses.lua index 470401e32581..3267599ee1f0 100644 --- a/apisix/utils/trusted-addresses.lua +++ b/apisix/utils/trusted-addresses.lua @@ -9,12 +9,10 @@ local _M = {} local function validate_trusted_addresses(trusted_addresses) - if trusted_addresses and next(trusted_addresses) then - for _, cidr in ipairs(trusted_addresses) do - if not core.ip.validate_cidr_or_ip(cidr) then - core.log.error("Invalid IP/CIDR '", cidr, "' exists in trusted_addresses") - return false - end + for _, cidr in ipairs(trusted_addresses) do + if not core.ip.validate_cidr_or_ip(cidr) then + core.log.error("Invalid IP/CIDR '", cidr, "' exists in trusted_addresses") + return false end end return true From 5bcc9ffcb8b21f40750d618939fd10178a945511 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Wed, 27 Aug 2025 15:45:08 +0800 Subject: [PATCH 03/37] fix --- apisix/utils/trusted-addresses.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/utils/trusted-addresses.lua b/apisix/utils/trusted-addresses.lua index 3267599ee1f0..8c40fa1c25c7 100644 --- a/apisix/utils/trusted-addresses.lua +++ b/apisix/utils/trusted-addresses.lua @@ -51,7 +51,7 @@ function _M.is_trusted(address) core.log.info("trusted_addresses_matcher is not initialized, skipping subsequent parsing.") return false end - return trusted_addresses_matcher(address) + return trusted_addresses_matcher:match(address) end return _M From a9b3e4c6317934118cb5c7d2451417d827bc4004 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Wed, 27 Aug 2025 17:47:16 +0800 Subject: [PATCH 04/37] feat: add to config.yaml.example --- conf/config.yaml.example | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conf/config.yaml.example b/conf/config.yaml.example index 408fb51389fe..94b016207668 100644 --- a/conf/config.yaml.example +++ b/conf/config.yaml.example @@ -141,6 +141,8 @@ apisix: # or (standalone mode) the config isn't loaded yet either via file or Admin API. # disable_upstream_healthcheck: false # A global switch for healthcheck. Defaults to false. # When set to true, it overrides all upstream healthcheck configurations and globally disabling healthchecks. +# trusted_addresses: # When enabled, APISIX will trust the `X-Forwarded-* Headers` +# - 127.0.0.1 # passed in requests from the IP/CIDR in the list. nginx_config: # Config for render the template to generate nginx.conf # user: root # Set the execution user of the worker process. This is only # effective if the master process runs with super-user privileges. From 1ab46cddad60d2c563234fd901989a29a00f9da3 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Wed, 27 Aug 2025 17:48:32 +0800 Subject: [PATCH 05/37] fix: handle xf headers in untrusted addresses --- apisix/init.lua | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/apisix/init.lua b/apisix/init.lua index 1b3c98d93ff9..dc00ef8cb601 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -61,6 +61,7 @@ local type = type local pairs = pairs local tostring = tostring local ngx_re_match = ngx.re.match +local ngx_req_clear_header = ngx.req.clear_header local control_api_router local is_http = false @@ -608,9 +609,26 @@ local function handle_x_forwarded_headers(api_ctx) api_ctx.var.x_forwarded_host = core.request.header(api_ctx, "X-Forwarded-Host") or api_ctx.var.host api_ctx.var.x_forwarded_port = core.request.header(api_ctx, "X-Forwarded-Port") or api_ctx.var.server_port else - api_ctx.var.x_forwarded_host = api_ctx.var.host - api_ctx.var.x_forwarded_proto = api_ctx.var.scheme - api_ctx.var.x_forwarded_port = api_ctx.var.server_port + if core.request.header(api_ctx, "X-Forwarded-Proto") then + -- Set the trusted value for the subsequent stages + api_ctx.var.x_forwarded_proto = api_ctx.var.scheme + -- Store the untrusted value for later use + api_ctx.untrusted_x_forwarded_proto = core.request.header(api_ctx, "X-Forwarded-Proto") + -- Clear headers to ensure that the subsequent stages get the correct info + ngx_req_clear_header("X-Forwarded-Proto") + end + + if core.request.header(api_ctx, "X-Forwarded-Host") then + api_ctx.var.x_forwarded_host = api_ctx.var.host + api_ctx.untrusted_x_forwarded_host = core.request.header(api_ctx, "X-Forwarded-Host") + ngx_req_clear_header("X-Forwarded-Host") + end + + if core.request.header(api_ctx, "X-Forwarded-Port") then + api_ctx.var.x_forwarded_port = api_ctx.var.server_port + api_ctx.untrusted_x_forwarded_port = core.request.header(api_ctx, "X-Forwarded-Port") + ngx_req_clear_header("X-Forwarded-Port") + end end end From b9cf720f88a451e87eccbabd1480be88747ecf0b Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Thu, 28 Aug 2025 08:56:29 +0800 Subject: [PATCH 06/37] fix: logic --- apisix/init.lua | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/apisix/init.lua b/apisix/init.lua index dc00ef8cb601..9055eec29829 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -604,31 +604,23 @@ end local function handle_x_forwarded_headers(api_ctx) - if trusted_addresses_util.is_trusted(api_ctx.var.remote_addr) then - api_ctx.var.x_forwarded_proto = core.request.header(api_ctx, "X-Forwarded-Proto") or api_ctx.var.scheme - api_ctx.var.x_forwarded_host = core.request.header(api_ctx, "X-Forwarded-Host") or api_ctx.var.host - api_ctx.var.x_forwarded_port = core.request.header(api_ctx, "X-Forwarded-Port") or api_ctx.var.server_port + local is_trusted = trusted_addresses_util.is_trusted(api_ctx.var.realip_remote_addr) + if is_trusted then + api_ctx.var.http_x_forwarded_proto = api_ctx.var.http_x_forwarded_proto or api_ctx.var.scheme + api_ctx.var.http_x_forwarded_host = api_ctx.var.http_x_forwarded_host or api_ctx.var.host + api_ctx.var.http_x_forwarded_port = api_ctx.var.http_x_forwarded_port or api_ctx.var.server_port else - if core.request.header(api_ctx, "X-Forwarded-Proto") then - -- Set the trusted value for the subsequent stages - api_ctx.var.x_forwarded_proto = api_ctx.var.scheme - -- Store the untrusted value for later use - api_ctx.untrusted_x_forwarded_proto = core.request.header(api_ctx, "X-Forwarded-Proto") - -- Clear headers to ensure that the subsequent stages get the correct info - ngx_req_clear_header("X-Forwarded-Proto") - end - - if core.request.header(api_ctx, "X-Forwarded-Host") then - api_ctx.var.x_forwarded_host = api_ctx.var.host - api_ctx.untrusted_x_forwarded_host = core.request.header(api_ctx, "X-Forwarded-Host") - ngx_req_clear_header("X-Forwarded-Host") - end - - if core.request.header(api_ctx, "X-Forwarded-Port") then - api_ctx.var.x_forwarded_port = api_ctx.var.server_port - api_ctx.untrusted_x_forwarded_port = core.request.header(api_ctx, "X-Forwarded-Port") - ngx_req_clear_header("X-Forwarded-Port") - end + -- store the original x-forwarded-* headers for later process + api_ctx.var.original_x_forwarded_proto = api_ctx.var.http_x_forwarded_proto + api_ctx.var.original_x_forwarded_host = api_ctx.var.http_x_forwarded_host + api_ctx.var.original_x_forwarded_port = api_ctx.var.http_x_forwarded_port + api_ctx.var.original_x_forwarded_for = api_ctx.var.http_x_forwarded_for + + -- override the x-forwarded-* headers to the trusted ones + core.request.set_header(api_ctx, "X-Forwarded-Proto", api_ctx.var.scheme) + core.request.set_header(api_ctx, "X-Forwarded-Host", api_ctx.var.host) + core.request.set_header(api_ctx, "X-Forwarded-Port", api_ctx.var.server_port) + core.request.set_header(api_ctx, "X-Forwarded-For", nil) end end From 4f2ed0c446d84f0eb29ff7c11277ed5007d5beb0 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Thu, 28 Aug 2025 10:08:02 +0800 Subject: [PATCH 07/37] chore: format --- apisix/utils/trusted-addresses.lua | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apisix/utils/trusted-addresses.lua b/apisix/utils/trusted-addresses.lua index 8c40fa1c25c7..680fb037a482 100644 --- a/apisix/utils/trusted-addresses.lua +++ b/apisix/utils/trusted-addresses.lua @@ -1,7 +1,7 @@ -local require = require -local core = require("apisix.core") -local next = next -local ipairs = ipairs +local require = require +local core = require("apisix.core") +local next = next +local ipairs = ipairs local trusted_addresses_matcher @@ -26,12 +26,17 @@ function _M.init_worker() if not trusted_addresses then return end - + if not core.table.isarray(trusted_addresses) then core.log.error("trusted_addresses '", trusted_addresses, "' is not an array, please check your configuration") return end + if not next(trusted_addresses) then + core.log.info("trusted_addresses is an empty array") + return + end + if not validate_trusted_addresses(trusted_addresses) then return end @@ -41,7 +46,7 @@ function _M.init_worker() core.log.error("failed to create ip matcher for trusted_addresses: ", err) return end - + trusted_addresses_matcher = matcher end From 54113248df64b57e4a1beb502cb9397a91a208cd Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Thu, 28 Aug 2025 14:07:06 +0800 Subject: [PATCH 08/37] fix: lint --- apisix/init.lua | 11 ++++------- apisix/utils/trusted-addresses.lua | 16 ++++++++++++++++ conf/config.yaml.example | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/apisix/init.lua b/apisix/init.lua index 9055eec29829..b69352af87a7 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -61,7 +61,6 @@ local type = type local pairs = pairs local tostring = tostring local ngx_re_match = ngx.re.match -local ngx_req_clear_header = ngx.req.clear_header local control_api_router local is_http = false @@ -604,12 +603,8 @@ end local function handle_x_forwarded_headers(api_ctx) - local is_trusted = trusted_addresses_util.is_trusted(api_ctx.var.realip_remote_addr) - if is_trusted then - api_ctx.var.http_x_forwarded_proto = api_ctx.var.http_x_forwarded_proto or api_ctx.var.scheme - api_ctx.var.http_x_forwarded_host = api_ctx.var.http_x_forwarded_host or api_ctx.var.host - api_ctx.var.http_x_forwarded_port = api_ctx.var.http_x_forwarded_port or api_ctx.var.server_port - else + local addr_is_trusted = trusted_addresses_util.is_trusted(api_ctx.var.realip_remote_addr) + if not addr_is_trusted then -- store the original x-forwarded-* headers for later process api_ctx.var.original_x_forwarded_proto = api_ctx.var.http_x_forwarded_proto api_ctx.var.original_x_forwarded_host = api_ctx.var.http_x_forwarded_host @@ -620,6 +615,8 @@ local function handle_x_forwarded_headers(api_ctx) core.request.set_header(api_ctx, "X-Forwarded-Proto", api_ctx.var.scheme) core.request.set_header(api_ctx, "X-Forwarded-Host", api_ctx.var.host) core.request.set_header(api_ctx, "X-Forwarded-Port", api_ctx.var.server_port) + -- clear untrusted values, + -- later processed in ngx_tpl by `$proxy_add_x_forwarded_for` core.request.set_header(api_ctx, "X-Forwarded-For", nil) end end diff --git a/apisix/utils/trusted-addresses.lua b/apisix/utils/trusted-addresses.lua index 680fb037a482..7e6780c548d1 100644 --- a/apisix/utils/trusted-addresses.lua +++ b/apisix/utils/trusted-addresses.lua @@ -1,3 +1,19 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- local require = require local core = require("apisix.core") local next = next diff --git a/conf/config.yaml.example b/conf/config.yaml.example index 94b016207668..968ed78594fa 100644 --- a/conf/config.yaml.example +++ b/conf/config.yaml.example @@ -141,7 +141,7 @@ apisix: # or (standalone mode) the config isn't loaded yet either via file or Admin API. # disable_upstream_healthcheck: false # A global switch for healthcheck. Defaults to false. # When set to true, it overrides all upstream healthcheck configurations and globally disabling healthchecks. -# trusted_addresses: # When enabled, APISIX will trust the `X-Forwarded-* Headers` +# trusted_addresses: # When enabled, APISIX will trust the `X-Forwarded-*` Headers # - 127.0.0.1 # passed in requests from the IP/CIDR in the list. nginx_config: # Config for render the template to generate nginx.conf # user: root # Set the execution user of the worker process. This is only From 5e128192b1c4585788c0cd7eca9e85168e8cdc0f Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Thu, 28 Aug 2025 14:23:16 +0800 Subject: [PATCH 09/37] fix: lint --- apisix/utils/trusted-addresses.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apisix/utils/trusted-addresses.lua b/apisix/utils/trusted-addresses.lua index 7e6780c548d1..cbdd89b2a69d 100644 --- a/apisix/utils/trusted-addresses.lua +++ b/apisix/utils/trusted-addresses.lua @@ -44,7 +44,8 @@ function _M.init_worker() end if not core.table.isarray(trusted_addresses) then - core.log.error("trusted_addresses '", trusted_addresses, "' is not an array, please check your configuration") + core.log.error("trusted_addresses '", trusted_addresses, + "' is not an array, please check your configuration") return end @@ -69,7 +70,7 @@ end function _M.is_trusted(address) if not trusted_addresses_matcher then - core.log.info("trusted_addresses_matcher is not initialized, skipping subsequent parsing.") + core.log.info("trusted_addresses_matcher is not initialized") return false end return trusted_addresses_matcher:match(address) From 197ce75259b729717f4a5f7cbca898e4f73bc4fc Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Thu, 28 Aug 2025 15:04:39 +0800 Subject: [PATCH 10/37] fix: old tests --- t/APISIX.pm | 2 ++ t/plugin/proxy-rewrite2.t | 2 ++ 2 files changed, 4 insertions(+) diff --git a/t/APISIX.pm b/t/APISIX.pm index 3c82110a3844..6c75ba0ce236 100644 --- a/t/APISIX.pm +++ b/t/APISIX.pm @@ -105,6 +105,8 @@ apisix: tcp: - 9100 enable_resolv_search_opt: false + trusted_addresses: + - "127.0.0.1" _EOC_ my $etcd_enable_auth = $ENV{"ETCD_ENABLE_AUTH"} || "false"; diff --git a/t/plugin/proxy-rewrite2.t b/t/plugin/proxy-rewrite2.t index 7096e4aad249..893683321235 100644 --- a/t/plugin/proxy-rewrite2.t +++ b/t/plugin/proxy-rewrite2.t @@ -27,6 +27,8 @@ add_block_preprocessor(sub { my $yaml_config = $block->yaml_config // <<_EOC_; apisix: node_listen: 1984 + trusted_addresses: + - "127.0.0.1" deployment: role: data_plane role_data_plane: From 8a271a5909630ec97017dba917b082400bf2b055 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Thu, 28 Aug 2025 15:56:55 +0800 Subject: [PATCH 11/37] fix: old tests --- t/plugin/dubbo-proxy/upstream.t | 2 ++ t/plugin/sls-logger.t | 2 ++ 2 files changed, 4 insertions(+) diff --git a/t/plugin/dubbo-proxy/upstream.t b/t/plugin/dubbo-proxy/upstream.t index c54c091160eb..627c9566b12c 100644 --- a/t/plugin/dubbo-proxy/upstream.t +++ b/t/plugin/dubbo-proxy/upstream.t @@ -51,6 +51,8 @@ _EOC_ apisix: node_listen: 1984 enable_admin: false + trusted_addresses: + - "127.0.0.1" deployment: role: data_plane role_data_plane: diff --git a/t/plugin/sls-logger.t b/t/plugin/sls-logger.t index 45dd3f8964d7..91586251832b 100644 --- a/t/plugin/sls-logger.t +++ b/t/plugin/sls-logger.t @@ -267,6 +267,8 @@ apisix: enable_encrypt_fields: true keyring: - edd1c9f0985e76a2 + trusted_addresses: + - "127.0.0.1" --- config location /t { content_by_lua_block { From cd98ba8e2fb4ea87a81610e107b7003f9863a13c Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Fri, 29 Aug 2025 10:06:14 +0800 Subject: [PATCH 12/37] test: add trusted_accesses cases --- apisix/init.lua | 10 ++- t/core/trusted-addresses.t | 135 +++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 t/core/trusted-addresses.t diff --git a/apisix/init.lua b/apisix/init.lua index b69352af87a7..0f16360749f7 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -604,7 +604,15 @@ end local function handle_x_forwarded_headers(api_ctx) local addr_is_trusted = trusted_addresses_util.is_trusted(api_ctx.var.realip_remote_addr) - if not addr_is_trusted then + if addr_is_trusted then + -- later send to upstream in `ngx_tpl` by `var_x_forwarded_*` + api_ctx.var.var_x_forwarded_proto = api_ctx.var.http_x_forwarded_proto + or api_ctx.var.scheme + api_ctx.var.var_x_forwarded_host = api_ctx.var.http_x_forwarded_host + or api_ctx.var.host + api_ctx.var.var_x_forwarded_port = api_ctx.var.http_x_forwarded_port + or api_ctx.var.server_port + else -- store the original x-forwarded-* headers for later process api_ctx.var.original_x_forwarded_proto = api_ctx.var.http_x_forwarded_proto api_ctx.var.original_x_forwarded_host = api_ctx.var.http_x_forwarded_host diff --git a/t/core/trusted-addresses.t b/t/core/trusted-addresses.t new file mode 100644 index 000000000000..197962990308 --- /dev/null +++ b/t/core/trusted-addresses.t @@ -0,0 +1,135 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_root_location(); +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: without trusted_addresses configuration, X-Forwarded headers should be overridden +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: false +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /old_uri + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +#END +--- request +GET /old_uri +--- more_headers +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-host: localhost +x-forwarded-port: 1984 +x-forwarded-proto: http +x-real-ip: 127.0.0.1 + + + +=== TEST 2: with trusted_addresses configuration, X-Forwarded headers should be preserved from trusted client +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: false + trusted_addresses: + - "127.0.0.1" +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /old_uri + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +#END +--- request +GET /old_uri +--- more_headers +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-host: example.com +x-forwarded-port: 8443 +x-forwarded-proto: https +x-real-ip: 127.0.0.1 + + + +=== TEST 3: with trusted_addresses configuration, but client not in trusted list, X-Forwarded headers should be overridden +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: false + trusted_addresses: + - "10.0.0.0/8" +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /old_uri + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +#END +--- request +GET /old_uri +--- more_headers +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-host: localhost +x-forwarded-port: 1984 +x-forwarded-proto: http +x-real-ip: 127.0.0.1 From 809b1e9740b64fedc9cf1cdf709246f953d9de64 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Fri, 29 Aug 2025 13:42:14 +0800 Subject: [PATCH 13/37] try fix --- apisix/init.lua | 16 +++++++--------- t/core/trusted-addresses.t | 8 ++++++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/apisix/init.lua b/apisix/init.lua index 0f16360749f7..9ee8ada55bba 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -604,15 +604,7 @@ end local function handle_x_forwarded_headers(api_ctx) local addr_is_trusted = trusted_addresses_util.is_trusted(api_ctx.var.realip_remote_addr) - if addr_is_trusted then - -- later send to upstream in `ngx_tpl` by `var_x_forwarded_*` - api_ctx.var.var_x_forwarded_proto = api_ctx.var.http_x_forwarded_proto - or api_ctx.var.scheme - api_ctx.var.var_x_forwarded_host = api_ctx.var.http_x_forwarded_host - or api_ctx.var.host - api_ctx.var.var_x_forwarded_port = api_ctx.var.http_x_forwarded_port - or api_ctx.var.server_port - else + if not addr_is_trusted then -- store the original x-forwarded-* headers for later process api_ctx.var.original_x_forwarded_proto = api_ctx.var.http_x_forwarded_proto api_ctx.var.original_x_forwarded_host = api_ctx.var.http_x_forwarded_host @@ -626,6 +618,12 @@ local function handle_x_forwarded_headers(api_ctx) -- clear untrusted values, -- later processed in ngx_tpl by `$proxy_add_x_forwarded_for` core.request.set_header(api_ctx, "X-Forwarded-For", nil) + + --- need to manually update the values in `var` + api_ctx.var.http_x_forwarded_proto = api_ctx.var.scheme + api_ctx.var.http_x_forwarded_host = api_ctx.var.host + api_ctx.var.http_x_forwarded_port = api_ctx.var.server_port + api_ctx.var.http_x_forwarded_for = nil end end diff --git a/t/core/trusted-addresses.t b/t/core/trusted-addresses.t index 197962990308..a64540283576 100644 --- a/t/core/trusted-addresses.t +++ b/t/core/trusted-addresses.t @@ -20,6 +20,14 @@ repeat_each(1); no_root_location(); no_shuffle(); +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + run_tests(); __DATA__ From 1a072b2cbc5508d4fb5b522dcd7cea1b575fb53a Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Fri, 29 Aug 2025 15:13:28 +0800 Subject: [PATCH 14/37] fix: update vars logic --- apisix/init.lua | 55 ++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/apisix/init.lua b/apisix/init.lua index 9ee8ada55bba..0e17783ba03e 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -293,21 +293,6 @@ end local function set_upstream_headers(api_ctx, picked_server) set_upstream_host(api_ctx, picked_server) - - local proto = api_ctx.var.http_x_forwarded_proto - if proto then - api_ctx.var.var_x_forwarded_proto = proto - end - - local x_forwarded_host = api_ctx.var.http_x_forwarded_host - if x_forwarded_host then - api_ctx.var.var_x_forwarded_host = x_forwarded_host - end - - local port = api_ctx.var.http_x_forwarded_port - if port then - api_ctx.var.var_x_forwarded_port = port - end end @@ -604,6 +589,7 @@ end local function handle_x_forwarded_headers(api_ctx) local addr_is_trusted = trusted_addresses_util.is_trusted(api_ctx.var.realip_remote_addr) + if not addr_is_trusted then -- store the original x-forwarded-* headers for later process api_ctx.var.original_x_forwarded_proto = api_ctx.var.http_x_forwarded_proto @@ -611,19 +597,38 @@ local function handle_x_forwarded_headers(api_ctx) api_ctx.var.original_x_forwarded_port = api_ctx.var.http_x_forwarded_port api_ctx.var.original_x_forwarded_for = api_ctx.var.http_x_forwarded_for + local proto = api_ctx.var.scheme + local host = api_ctx.var.host + local port = api_ctx.var.server_port + + api_ctx.var.http_x_forwarded_proto = proto + api_ctx.var.http_x_forwarded_host = host + api_ctx.var.http_x_forwarded_port = port + -- override the x-forwarded-* headers to the trusted ones - core.request.set_header(api_ctx, "X-Forwarded-Proto", api_ctx.var.scheme) - core.request.set_header(api_ctx, "X-Forwarded-Host", api_ctx.var.host) - core.request.set_header(api_ctx, "X-Forwarded-Port", api_ctx.var.server_port) - -- clear untrusted values, + 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) + end +end + + +local function update_var_x_forwarded_headers(api_ctx) + local proto = api_ctx.var.http_x_forwarded_proto + if proto then + api_ctx.var.var_x_forwarded_proto = proto + end - --- need to manually update the values in `var` - api_ctx.var.http_x_forwarded_proto = api_ctx.var.scheme - api_ctx.var.http_x_forwarded_host = api_ctx.var.host - api_ctx.var.http_x_forwarded_port = api_ctx.var.server_port - api_ctx.var.http_x_forwarded_for = nil + local port = api_ctx.var.http_x_forwarded_port + if port then + api_ctx.var.var_x_forwarded_port = port + end + + local host = api_ctx.var.http_x_forwarded_host + if host then + api_ctx.var.var_x_forwarded_host = host end end @@ -785,6 +790,8 @@ function _M.http_access_phase() end _M.handle_upstream(api_ctx, route, enable_websocket) + + update_var_x_forwarded_headers(api_ctx) end From 6614dc0e6bd711bd6485bf6a0d247b02834af176 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Fri, 29 Aug 2025 15:25:04 +0800 Subject: [PATCH 15/37] chore --- apisix/init.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/apisix/init.lua b/apisix/init.lua index 0e17783ba03e..bb7dd32c92dd 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -604,6 +604,7 @@ 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 -- override the x-forwarded-* headers to the trusted ones core.request.set_header(api_ctx, "X-Forwarded-Proto", proto) From 510b2b3e6596033a88340c52355c9933717f006c Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Fri, 29 Aug 2025 16:21:42 +0800 Subject: [PATCH 16/37] test: add cases --- t/core/trusted-addresses.t | 80 +++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/t/core/trusted-addresses.t b/t/core/trusted-addresses.t index a64540283576..5da6e5849594 100644 --- a/t/core/trusted-addresses.t +++ b/t/core/trusted-addresses.t @@ -68,7 +68,7 @@ x-real-ip: 127.0.0.1 -=== TEST 2: with trusted_addresses configuration, X-Forwarded headers should be preserved from trusted client +=== TEST 2: with IP, X-Forwarded headers should be preserved from trusted client --- yaml_config apisix: node_listen: 1984 @@ -106,7 +106,83 @@ x-real-ip: 127.0.0.1 -=== TEST 3: with trusted_addresses configuration, but client not in trusted list, X-Forwarded headers should be overridden +=== TEST 3: with CIDR, X-Forwarded headers should be preserved from trusted client +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: false + trusted_addresses: + - "127.0.0.0/24" +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /old_uri + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +#END +--- request +GET /old_uri +--- more_headers +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-host: example.com +x-forwarded-port: 8443 +x-forwarded-proto: https +x-real-ip: 127.0.0.1 + + + +=== TEST 4: with `0.0.0.0/0`, X-Forwarded headers should be preserved from trusted client +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: false + trusted_addresses: + - "0.0.0.0/0" +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /old_uri + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +#END +--- request +GET /old_uri +--- more_headers +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-host: example.com +x-forwarded-port: 8443 +x-forwarded-proto: https +x-real-ip: 127.0.0.1 + + + +=== TEST 5: with trusted_addresses configuration, but client not in trusted list, X-Forwarded headers should be overridden --- yaml_config apisix: node_listen: 1984 From 16ecd734970924d88ac8a2c9c484aee3aa4c75c2 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Fri, 29 Aug 2025 16:42:50 +0800 Subject: [PATCH 17/37] docs: add notes to related docs --- docs/en/latest/plugins/chaitin-waf.md | 4 ++++ docs/en/latest/plugins/real-ip.md | 4 ++++ docs/zh/latest/plugins/chaitin-waf.md | 5 +++++ docs/zh/latest/plugins/real-ip.md | 4 ++++ 4 files changed, 17 insertions(+) diff --git a/docs/en/latest/plugins/chaitin-waf.md b/docs/en/latest/plugins/chaitin-waf.md index c5ae8f4e91ab..6e16267dd097 100644 --- a/docs/en/latest/plugins/chaitin-waf.md +++ b/docs/en/latest/plugins/chaitin-waf.md @@ -70,6 +70,10 @@ The response headers are listed below: | config.keepalive_timeout | integer | false | 60000 | Idle connection timeout, in milliseconds. | | config.real_client_ip | boolean | false | true | Specifies whether to use the `X-Forwarded-For` as the client IP (if present). If `false`, uses the direct client IP from the connection. | +:::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. +::: + :::note You can fetch the `admin_key` from `config.yaml` and save to an environment variable with the following command: diff --git a/docs/en/latest/plugins/real-ip.md b/docs/en/latest/plugins/real-ip.md index c607046ccbf2..3cb4b70a285d 100644 --- a/docs/en/latest/plugins/real-ip.md +++ b/docs/en/latest/plugins/real-ip.md @@ -45,6 +45,10 @@ The Plugin is functionally similar to NGINX's [ngx_http_realip_module](https://n | trusted_addresses | array[string] | False | | array of IPv4 or IPv6 addresses (CIDR notation acceptable) | Trusted addresses that are known to send correct replacement addresses. This configuration sets the [`set_real_ip_from`](https://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from) directive. | | 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. +::: + :::note If the address specified in `source` is missing or invalid, the Plugin would not change the client address. ::: diff --git a/docs/zh/latest/plugins/chaitin-waf.md b/docs/zh/latest/plugins/chaitin-waf.md index e090c9213b69..4823939e991a 100644 --- a/docs/zh/latest/plugins/chaitin-waf.md +++ b/docs/zh/latest/plugins/chaitin-waf.md @@ -106,6 +106,11 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/chaitin-waf -H "X-API-KE | config.req_body_size | integer | 否 | | 请求体大小,单位为 KB | | config.keepalive_size | integer | 否 | | 长亭 WAF 服务的最大并发空闲连接数 | | config.keepalive_timeout | integer | 否 | | 空闲链接超时,毫秒 | +| config.real_client_ip | boolean | 否 | true | 指定是否使用 `X-Forwarded-For` 作为客户端 IP 地址(如果存在)。如果设置为 `false`,则使用连接的直接客户端 IP 地址。 | + +:::note +只有来自 `apisix.trusted_addresses` 配置(支持 IP 和 CIDR)中的地址发送的 `X-Forwarded-*` 头才会被信任,并传递给插件或上游。 +::: 一个典型的示例配置如下,这里使用 `httpbun.org` 作为示例后端,可以按需替换: diff --git a/docs/zh/latest/plugins/real-ip.md b/docs/zh/latest/plugins/real-ip.md index 1b85b358b834..06876e4cb1b9 100644 --- a/docs/zh/latest/plugins/real-ip.md +++ b/docs/zh/latest/plugins/real-ip.md @@ -45,6 +45,10 @@ description: real-ip 插件允许 Apache APISIX 通过 HTTP 请求头或 HTTP | trusted_addresses | array[string] | 否 | | IPv4 或 IPv6 地址数组(接受 CIDR 表示法) | 已知会发送正确替代地址的可信地址。此配置设置 [`set_real_ip_from`](https://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from) 指令。 | | recursive | boolean | 否 | false | | 如果为 false,则将匹配可信地址之一的原始客户端地址替换为配置的 `source` 中发送的最后一个地址。
如果为 true,则将匹配可信地址之一的原始客户端地址替换为配置的 `source` 中发送的最后一个非可信地址。 | +:::note +只有来自 `apisix.trusted_addresses` 配置(支持 IP 和 CIDR)中的地址发送的 `X-Forwarded-*` 头才会被信任,并传递给插件或上游。 +::: + :::note 如果 `source` 属性中设置的地址丢失或者无效,该插件将不会更改客户端地址。 ::: From 1e3950e43168097d09227da79dc69fb044f4a1ee Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Mon, 1 Sep 2025 12:45:27 +0800 Subject: [PATCH 18/37] chore: add more explain about the default process way --- conf/config.yaml.example | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conf/config.yaml.example b/conf/config.yaml.example index 968ed78594fa..b7b81b4440f1 100644 --- a/conf/config.yaml.example +++ b/conf/config.yaml.example @@ -143,6 +143,8 @@ apisix: # When set to true, it overrides all upstream healthcheck configurations and globally disabling healthchecks. # trusted_addresses: # When enabled, APISIX will trust the `X-Forwarded-*` Headers # - 127.0.0.1 # passed in requests from the IP/CIDR in the list. +# - 0.0.0.0/0 # CAUTION: When not configured, APISIX will remove `X-Forwarded-*` headers + # from requests originating from any IP/CIDR. nginx_config: # Config for render the template to generate nginx.conf # user: root # Set the execution user of the worker process. This is only # effective if the master process runs with super-user privileges. From 1b6e5e658c021732d92400f1c9444c8522b691ca Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Tue, 2 Sep 2025 10:01:52 +0800 Subject: [PATCH 19/37] chore: adjust config example --- conf/config.yaml.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/config.yaml.example b/conf/config.yaml.example index b7b81b4440f1..689ba83fdeff 100644 --- a/conf/config.yaml.example +++ b/conf/config.yaml.example @@ -141,9 +141,9 @@ apisix: # or (standalone mode) the config isn't loaded yet either via file or Admin API. # disable_upstream_healthcheck: false # A global switch for healthcheck. Defaults to false. # When set to true, it overrides all upstream healthcheck configurations and globally disabling healthchecks. -# trusted_addresses: # When enabled, APISIX will trust the `X-Forwarded-*` Headers +# 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. -# - 0.0.0.0/0 # CAUTION: When not configured, APISIX will remove `X-Forwarded-*` headers +# - 172.18.0.0/16 # CAUTION: When not configured, APISIX will remove `X-Forwarded-*` headers # from requests originating from any IP/CIDR. nginx_config: # Config for render the template to generate nginx.conf # user: root # Set the execution user of the worker process. This is only From f8c3aaaf380c3a4b3a92ee370eb68c5ceca55592 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Tue, 2 Sep 2025 10:10:13 +0800 Subject: [PATCH 20/37] chore: rm usless config --- t/plugin/dubbo-proxy/upstream.t | 2 -- t/plugin/sls-logger.t | 2 -- 2 files changed, 4 deletions(-) diff --git a/t/plugin/dubbo-proxy/upstream.t b/t/plugin/dubbo-proxy/upstream.t index 627c9566b12c..c54c091160eb 100644 --- a/t/plugin/dubbo-proxy/upstream.t +++ b/t/plugin/dubbo-proxy/upstream.t @@ -51,8 +51,6 @@ _EOC_ apisix: node_listen: 1984 enable_admin: false - trusted_addresses: - - "127.0.0.1" deployment: role: data_plane role_data_plane: diff --git a/t/plugin/sls-logger.t b/t/plugin/sls-logger.t index 91586251832b..45dd3f8964d7 100644 --- a/t/plugin/sls-logger.t +++ b/t/plugin/sls-logger.t @@ -267,8 +267,6 @@ apisix: enable_encrypt_fields: true keyring: - edd1c9f0985e76a2 - trusted_addresses: - - "127.0.0.1" --- config location /t { content_by_lua_block { From 247c208de55dbc176dc5525499779e151f0e883a Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Tue, 2 Sep 2025 10:23:51 +0800 Subject: [PATCH 21/37] test: add multi ips cases --- t/core/trusted-addresses.t | 126 ++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 3 deletions(-) diff --git a/t/core/trusted-addresses.t b/t/core/trusted-addresses.t index 5da6e5849594..95d93203b853 100644 --- a/t/core/trusted-addresses.t +++ b/t/core/trusted-addresses.t @@ -106,13 +106,132 @@ x-real-ip: 127.0.0.1 -=== TEST 3: with CIDR, X-Forwarded headers should be preserved from trusted client +=== TEST 3: with multiple IPs, X-Forwarded headers should be preserved from trusted client +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: false + trusted_addresses: + - "127.0.0.1" + - "127.0.0.2" +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /old_uri + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +#END +--- request +GET /old_uri +--- more_headers +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-host: example.com +x-forwarded-port: 8443 +x-forwarded-proto: https +x-real-ip: 127.0.0.1 + + + +=== TEST 4: with CIDR, X-Forwarded headers should be preserved from trusted client +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: false + trusted_addresses: + - "127.0.0.0/24" +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /old_uri + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +#END +--- request +GET /old_uri +--- more_headers +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-host: example.com +x-forwarded-port: 8443 +x-forwarded-proto: https +x-real-ip: 127.0.0.1 + + + +=== TEST 5: with multiple CIDRs, X-Forwarded headers should be preserved from trusted client +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: false + trusted_addresses: + - "127.0.0.0/24" + - "1.1.1.0/24" +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /old_uri + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +#END +--- request +GET /old_uri +--- more_headers +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-host: example.com +x-forwarded-port: 8443 +x-forwarded-proto: https +x-real-ip: 127.0.0.1 + + + +=== TEST 6: with multiple IPs and CIDRs, X-Forwarded headers should be preserved from trusted client --- yaml_config apisix: node_listen: 1984 enable_admin: false trusted_addresses: - "127.0.0.0/24" + - "1.1.1.0/24" + - "127.0.0.1" + - "1.1.1.1" deployment: role: data_plane role_data_plane: @@ -144,7 +263,7 @@ x-real-ip: 127.0.0.1 -=== TEST 4: with `0.0.0.0/0`, X-Forwarded headers should be preserved from trusted client +=== TEST 7: with `0.0.0.0/0`, X-Forwarded headers should be preserved from trusted client --- yaml_config apisix: node_listen: 1984 @@ -182,12 +301,13 @@ x-real-ip: 127.0.0.1 -=== TEST 5: with trusted_addresses configuration, but client not in trusted list, X-Forwarded headers should be overridden +=== TEST 8: with trusted_addresses configuration, but client not in trusted list, X-Forwarded headers should be overridden --- yaml_config apisix: node_listen: 1984 enable_admin: false trusted_addresses: + - "1.0.0.1" - "10.0.0.0/8" deployment: role: data_plane From 1d744519aab78f6d93254b0b0d41bf1715e85a42 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Tue, 2 Sep 2025 14:40:31 +0800 Subject: [PATCH 22/37] test: add plugin cases --- t/plugin/proxy-rewrite2.t | 31 +++++++++++++++++++++++++++++++ t/plugin/real-ip.t | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/t/plugin/proxy-rewrite2.t b/t/plugin/proxy-rewrite2.t index 893683321235..a882ffd5af02 100644 --- a/t/plugin/proxy-rewrite2.t +++ b/t/plugin/proxy-rewrite2.t @@ -232,3 +232,34 @@ GET /echo X-Forwarded-Port: 8080 --- response_headers X-Forwarded-Port: 10080 + + + +=== TEST 8: pass duplicate X-Forwarded-Proto, but not configured trusted_addresses +--- yaml_config +apisix: + node_listen: 1984 +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /echo + upstream_id: 1 +upstreams: + - + id: 1 + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +#END +--- request +GET /echo +--- more_headers +X-Forwarded-Proto: http +X-Forwarded-Proto: grpc +--- response_headers +X-Forwarded-Proto: http diff --git a/t/plugin/real-ip.t b/t/plugin/real-ip.t index e6e044730bb4..fafe0b4a5842 100644 --- a/t/plugin/real-ip.t +++ b/t/plugin/real-ip.t @@ -470,3 +470,39 @@ passed GET /hello --- more_headers 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 +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: true + trusted_addresses: + - "192.128.0.0/16" +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /hello + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin + plugins: + real-ip: + trusted_addresses: ["192.128.0.0/16", "127.0.0.0/24"] + source: http_x_forwarded_for + ip-restriction: + whitelist: ["1.1.1.1"] +#END +--- request +GET /hello +--- more_headers +X-Forwarded-For: 1.1.1.1 +--- error_code: 403 From 31947b8d29141d5f48f3566443a39c296c294676 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Tue, 2 Sep 2025 15:44:39 +0800 Subject: [PATCH 23/37] docs: adjust descs --- docs/en/latest/plugins/chaitin-waf.md | 2 +- docs/en/latest/plugins/real-ip.md | 2 +- docs/zh/latest/plugins/chaitin-waf.md | 2 +- docs/zh/latest/plugins/real-ip.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/latest/plugins/chaitin-waf.md b/docs/en/latest/plugins/chaitin-waf.md index 6e16267dd097..5fdad5ac87e1 100644 --- a/docs/en/latest/plugins/chaitin-waf.md +++ b/docs/en/latest/plugins/chaitin-waf.md @@ -71,7 +71,7 @@ The response headers are listed below: | config.real_client_ip | boolean | false | true | Specifies whether to use the `X-Forwarded-For` as the client IP (if present). If `false`, uses the direct client IP from the connection. | :::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. +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 discarded. ::: :::note diff --git a/docs/en/latest/plugins/real-ip.md b/docs/en/latest/plugins/real-ip.md index 3cb4b70a285d..a76ae25559ad 100644 --- a/docs/en/latest/plugins/real-ip.md +++ b/docs/en/latest/plugins/real-ip.md @@ -46,7 +46,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. +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 discarded. ::: :::note diff --git a/docs/zh/latest/plugins/chaitin-waf.md b/docs/zh/latest/plugins/chaitin-waf.md index 4823939e991a..e4daba5842de 100644 --- a/docs/zh/latest/plugins/chaitin-waf.md +++ b/docs/zh/latest/plugins/chaitin-waf.md @@ -109,7 +109,7 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/chaitin-waf -H "X-API-KE | config.real_client_ip | boolean | 否 | true | 指定是否使用 `X-Forwarded-For` 作为客户端 IP 地址(如果存在)。如果设置为 `false`,则使用连接的直接客户端 IP 地址。 | :::note -只有来自 `apisix.trusted_addresses` 配置(支持 IP 和 CIDR)中的地址发送的 `X-Forwarded-*` 头才会被信任,并传递给插件或上游。 +只有发送自 `apisix.trusted_addresses` 配置(支持 IP 和 CIDR)地址的 `X-Forwarded-*` 头才会被信任,并传递给插件或上游。如果未配置 `apisix.trusted_addresses` 或 ip 不在配置地址范围内的,`X-Forwarded-*` 头将全部丢弃。 ::: 一个典型的示例配置如下,这里使用 `httpbun.org` 作为示例后端,可以按需替换: diff --git a/docs/zh/latest/plugins/real-ip.md b/docs/zh/latest/plugins/real-ip.md index 06876e4cb1b9..548dd8f1535c 100644 --- a/docs/zh/latest/plugins/real-ip.md +++ b/docs/zh/latest/plugins/real-ip.md @@ -46,7 +46,7 @@ description: real-ip 插件允许 Apache APISIX 通过 HTTP 请求头或 HTTP | recursive | boolean | 否 | false | | 如果为 false,则将匹配可信地址之一的原始客户端地址替换为配置的 `source` 中发送的最后一个地址。
如果为 true,则将匹配可信地址之一的原始客户端地址替换为配置的 `source` 中发送的最后一个非可信地址。 | :::note -只有来自 `apisix.trusted_addresses` 配置(支持 IP 和 CIDR)中的地址发送的 `X-Forwarded-*` 头才会被信任,并传递给插件或上游。 +只有发送自 `apisix.trusted_addresses` 配置(支持 IP 和 CIDR)地址的 `X-Forwarded-*` 头才会被信任,并传递给插件或上游。如果未配置 `apisix.trusted_addresses` 或 ip 不在配置地址范围内的,`X-Forwarded-*` 头将全部丢弃。 ::: :::note From 818cb0aab6ed5733e3e43612400fba793be30f75 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Fri, 5 Sep 2025 14:55:30 +0800 Subject: [PATCH 24/37] docs: adjust desc based on @moonming comments --- apisix/init.lua | 3 ++- conf/config.yaml.example | 4 ++-- docs/en/latest/plugins/chaitin-waf.md | 2 +- docs/en/latest/plugins/real-ip.md | 2 +- docs/zh/latest/plugins/chaitin-waf.md | 2 +- docs/zh/latest/plugins/real-ip.md | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/apisix/init.lua b/apisix/init.lua index bb7dd32c92dd..cbda1c8fb48f 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -591,7 +591,8 @@ local function handle_x_forwarded_headers(api_ctx) local addr_is_trusted = trusted_addresses_util.is_trusted(api_ctx.var.realip_remote_addr) if not addr_is_trusted then - -- store the original x-forwarded-* headers for later process + -- store the original x-forwarded-* headers + -- to allow future use by other plugins or processes api_ctx.var.original_x_forwarded_proto = api_ctx.var.http_x_forwarded_proto api_ctx.var.original_x_forwarded_host = api_ctx.var.http_x_forwarded_host api_ctx.var.original_x_forwarded_port = api_ctx.var.http_x_forwarded_port diff --git a/conf/config.yaml.example b/conf/config.yaml.example index 689ba83fdeff..2999a65920ba 100644 --- a/conf/config.yaml.example +++ b/conf/config.yaml.example @@ -143,8 +143,8 @@ 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, APISIX will remove `X-Forwarded-*` headers - # from requests originating from any IP/CIDR. +# - 172.18.0.0/16 # CAUTION: When not configured or the request from an untrusted address, + # APISIX will override `X-Forwarded-*` headers with values from the request context. nginx_config: # Config for render the template to generate nginx.conf # user: root # Set the execution user of the worker process. This is only # effective if the master process runs with super-user privileges. diff --git a/docs/en/latest/plugins/chaitin-waf.md b/docs/en/latest/plugins/chaitin-waf.md index 5fdad5ac87e1..7f3adc318120 100644 --- a/docs/en/latest/plugins/chaitin-waf.md +++ b/docs/en/latest/plugins/chaitin-waf.md @@ -71,7 +71,7 @@ The response headers are listed below: | config.real_client_ip | boolean | false | true | Specifies whether to use the `X-Forwarded-For` as the client IP (if present). If `false`, uses the direct client IP from the connection. | :::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 discarded. +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. ::: :::note diff --git a/docs/en/latest/plugins/real-ip.md b/docs/en/latest/plugins/real-ip.md index a76ae25559ad..147ad73d6689 100644 --- a/docs/en/latest/plugins/real-ip.md +++ b/docs/en/latest/plugins/real-ip.md @@ -46,7 +46,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 discarded. +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. ::: :::note diff --git a/docs/zh/latest/plugins/chaitin-waf.md b/docs/zh/latest/plugins/chaitin-waf.md index e4daba5842de..e685bc505335 100644 --- a/docs/zh/latest/plugins/chaitin-waf.md +++ b/docs/zh/latest/plugins/chaitin-waf.md @@ -109,7 +109,7 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/chaitin-waf -H "X-API-KE | config.real_client_ip | boolean | 否 | true | 指定是否使用 `X-Forwarded-For` 作为客户端 IP 地址(如果存在)。如果设置为 `false`,则使用连接的直接客户端 IP 地址。 | :::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-*` 头将全部被可信值覆盖。 ::: 一个典型的示例配置如下,这里使用 `httpbun.org` 作为示例后端,可以按需替换: diff --git a/docs/zh/latest/plugins/real-ip.md b/docs/zh/latest/plugins/real-ip.md index 548dd8f1535c..da1fa4b08eb1 100644 --- a/docs/zh/latest/plugins/real-ip.md +++ b/docs/zh/latest/plugins/real-ip.md @@ -46,7 +46,7 @@ description: real-ip 插件允许 Apache APISIX 通过 HTTP 请求头或 HTTP | 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-*` 头将全部被可信值覆盖。 ::: :::note From 80d561f1202b46a95cda1f9835ba84c1d29c9132 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Fri, 5 Sep 2025 15:54:16 +0800 Subject: [PATCH 25/37] fix: logic and cases --- apisix/cli/schema.lua | 8 ++++ apisix/utils/trusted-addresses.lua | 22 +-------- t/cli/test_validate_config.sh | 46 ++++++++++++++++++ t/core/trusted-addresses.t | 77 ++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 21 deletions(-) diff --git a/apisix/cli/schema.lua b/apisix/cli/schema.lua index b7a8b0fc0708..3b65880312cd 100644 --- a/apisix/cli/schema.lua +++ b/apisix/cli/schema.lua @@ -259,6 +259,14 @@ local config_schema = { default = false, description = "a global switch to disable upstream health checks", }, + trusted_addresses = { + type = "array", + minItems = 0, + items = { + type = "string", + }, + uniqueItems = true + }, } }, nginx_config = { diff --git a/apisix/utils/trusted-addresses.lua b/apisix/utils/trusted-addresses.lua index cbdd89b2a69d..883780ec3d59 100644 --- a/apisix/utils/trusted-addresses.lua +++ b/apisix/utils/trusted-addresses.lua @@ -24,28 +24,12 @@ local trusted_addresses_matcher local _M = {} -local function validate_trusted_addresses(trusted_addresses) - for _, cidr in ipairs(trusted_addresses) do - if not core.ip.validate_cidr_or_ip(cidr) then - core.log.error("Invalid IP/CIDR '", cidr, "' exists in trusted_addresses") - return false - end - end - return true -end - - function _M.init_worker() local local_conf = core.config.local_conf() local trusted_addresses = core.table.try_read_attr(local_conf, "apisix", "trusted_addresses") if not trusted_addresses then - return - end - - if not core.table.isarray(trusted_addresses) then - core.log.error("trusted_addresses '", trusted_addresses, - "' is not an array, please check your configuration") + core.log.info("trusted_addresses is not configured") return end @@ -54,10 +38,6 @@ function _M.init_worker() return end - if not validate_trusted_addresses(trusted_addresses) then - return - end - local matcher, err = core.ip.create_ip_matcher(trusted_addresses) if not matcher then core.log.error("failed to create ip matcher for trusted_addresses: ", err) diff --git a/t/cli/test_validate_config.sh b/t/cli/test_validate_config.sh index 0f8a09a43c2d..0008d7a01d92 100755 --- a/t/cli/test_validate_config.sh +++ b/t/cli/test_validate_config.sh @@ -204,3 +204,49 @@ if ! echo "$out" | grep 'property "host" validation failed'; then fi echo "passed: check etcd schema during init" + +# Test trusted_addresses with non-array value (string instead of array) +echo ' +apisix: + trusted_addresses: "127.0.0.1" +deployment: + role: traditional + role_traditional: + config_provider: etcd +etcd: + host: + - "http://127.0.0.1:2379" + prefix: "/apisix" +' > conf/config.yaml + +out=$(make init 2>&1 || true) +if ! echo "$out" | grep 'property "trusted_addresses" validation failed: wrong type: expected array, got string'; then + echo "failed: trusted_addresses should reject string value" + exit 1 +fi + +echo "passed: trusted_addresses rejects non-array value" + +# Test trusted_addresses with non-string items in array +echo ' +apisix: + trusted_addresses: + - "127.0.0.1" + - 123 +deployment: + role: traditional + role_traditional: + config_provider: etcd +etcd: + host: + - "http://127.0.0.1:2379" + prefix: "/apisix" +' > conf/config.yaml + +out=$(make init 2>&1 || true) +if ! echo "$out" | grep 'property "trusted_addresses" validation failed: failed to validate item 2: wrong type: expected string, got number'; then + echo "failed: trusted_addresses should reject non-string items" + exit 1 +fi + +echo "passed: trusted_addresses rejects non-string array items" diff --git a/t/core/trusted-addresses.t b/t/core/trusted-addresses.t index 95d93203b853..e11e10e5c6c0 100644 --- a/t/core/trusted-addresses.t +++ b/t/core/trusted-addresses.t @@ -65,6 +65,9 @@ x-forwarded-host: localhost x-forwarded-port: 1984 x-forwarded-proto: http x-real-ip: 127.0.0.1 +--- error_log +trusted_addresses is not configured +trusted_addresses_matcher is not initialized @@ -337,3 +340,77 @@ x-forwarded-host: localhost x-forwarded-port: 1984 x-forwarded-proto: http x-real-ip: 127.0.0.1 + + + +=== TEST 9: with empty trusted_addresses configuration, X-Forwarded headers should be overridden +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: false + trusted_addresses: [] +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /old_uri + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +#END +--- request +GET /old_uri +--- more_headers +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-host: localhost +x-forwarded-port: 1984 +x-forwarded-proto: http +x-real-ip: 127.0.0.1 +--- no_error_log +trusted_addresses is not configured +--- error_log +trusted_addresses is an empty array +trusted_addresses_matcher is not initialized + + + +=== TEST 9: invalid trusted_addresses configuration: IP is invalid +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: false + trusted_addresses: + - "1.0.0" +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- error_log +failed to create ip matcher for trusted_addresses: invalid ip address: 1.0.0 + + + +=== TEST 10: invalid trusted_addresses configuration: CIDR is invalid +--- yaml_config +apisix: + node_listen: 1984 + enable_admin: false + trusted_addresses: + - "1.0.0.0/33" +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +--- error_log +failed to create ip matcher for trusted_addresses: invalid ip address: 1.0.0.0/33 From 3bad749cf792ac53981ec4c2c922f58e579ded9f Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Fri, 5 Sep 2025 16:16:17 +0800 Subject: [PATCH 26/37] chore: add more cases --- apisix/cli/schema.lua | 2 +- apisix/utils/trusted-addresses.lua | 5 ----- t/cli/test_validate_config.sh | 25 +++++++++++++++++++++++++ t/core/trusted-addresses.t | 4 ++-- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/apisix/cli/schema.lua b/apisix/cli/schema.lua index 3b65880312cd..ea9188680b13 100644 --- a/apisix/cli/schema.lua +++ b/apisix/cli/schema.lua @@ -261,7 +261,7 @@ local config_schema = { }, trusted_addresses = { type = "array", - minItems = 0, + minItems = 1, items = { type = "string", }, diff --git a/apisix/utils/trusted-addresses.lua b/apisix/utils/trusted-addresses.lua index 883780ec3d59..9bf8fe605a9d 100644 --- a/apisix/utils/trusted-addresses.lua +++ b/apisix/utils/trusted-addresses.lua @@ -33,11 +33,6 @@ function _M.init_worker() return end - if not next(trusted_addresses) then - core.log.info("trusted_addresses is an empty array") - return - end - local matcher, err = core.ip.create_ip_matcher(trusted_addresses) if not matcher then core.log.error("failed to create ip matcher for trusted_addresses: ", err) diff --git a/t/cli/test_validate_config.sh b/t/cli/test_validate_config.sh index 0008d7a01d92..83c3a2c52847 100755 --- a/t/cli/test_validate_config.sh +++ b/t/cli/test_validate_config.sh @@ -250,3 +250,28 @@ if ! echo "$out" | grep 'property "trusted_addresses" validation failed: failed fi echo "passed: trusted_addresses rejects non-string array items" + +# Test trusted_addresses with duplicate items (should be rejected) +echo ' +apisix: + trusted_addresses: + - "127.0.0.1" + - "192.168.1.1" + - "127.0.0.1" +deployment: + role: traditional + role_traditional: + config_provider: etcd +etcd: + host: + - "http://127.0.0.1:2379" + prefix: "/apisix" +' > conf/config.yaml + +out=$(make init 2>&1 || true) +if ! echo "$out" | grep 'property "trusted_addresses" validation failed.*duplicate'; then + echo "failed: trusted_addresses should reject duplicate items" + exit 1 +fi + +echo "passed: trusted_addresses rejects duplicate items" diff --git a/t/core/trusted-addresses.t b/t/core/trusted-addresses.t index e11e10e5c6c0..147fda105e7c 100644 --- a/t/core/trusted-addresses.t +++ b/t/core/trusted-addresses.t @@ -385,7 +385,7 @@ trusted_addresses_matcher is not initialized -=== TEST 9: invalid trusted_addresses configuration: IP is invalid +=== TEST 10: invalid trusted_addresses configuration: IP is invalid --- yaml_config apisix: node_listen: 1984 @@ -401,7 +401,7 @@ failed to create ip matcher for trusted_addresses: invalid ip address: 1.0.0 -=== TEST 10: invalid trusted_addresses configuration: CIDR is invalid +=== TEST 11: invalid trusted_addresses configuration: CIDR is invalid --- yaml_config apisix: node_listen: 1984 From 0d8e1b82669931acf64b1395a81a286511345bd0 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Fri, 5 Sep 2025 16:55:10 +0800 Subject: [PATCH 27/37] chore --- conf/config.yaml.example | 2 +- t/cli/test_validate_config.sh | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/conf/config.yaml.example b/conf/config.yaml.example index 2999a65920ba..903e72065ceb 100644 --- a/conf/config.yaml.example +++ b/conf/config.yaml.example @@ -144,7 +144,7 @@ apisix: # 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 values from the request context. + # APISIX will override `X-Forwarded-*` headers with trusted values. nginx_config: # Config for render the template to generate nginx.conf # user: root # Set the execution user of the worker process. This is only # effective if the master process runs with super-user privileges. diff --git a/t/cli/test_validate_config.sh b/t/cli/test_validate_config.sh index 83c3a2c52847..516f158c375e 100755 --- a/t/cli/test_validate_config.sh +++ b/t/cli/test_validate_config.sh @@ -227,6 +227,28 @@ fi echo "passed: trusted_addresses rejects non-array value" +# Test trusted_addresses with empty array (should be rejected) +echo ' +apisix: + trusted_addresses: [] +deployment: + role: traditional + role_traditional: + config_provider: etcd +etcd: + host: + - "http://127.0.0.1:2379" + prefix: "/apisix" +' > conf/config.yaml + +out=$(make init 2>&1 || true) +if ! echo "$out" | grep 'property "trusted_addresses" validation failed: expect array to have at least 1 items'; then + echo "failed: trusted_addresses should reject empty array" + exit 1 +fi + +echo "passed: trusted_addresses rejects empty array" + # Test trusted_addresses with non-string items in array echo ' apisix: From 395cf61e266597be7e18499ba0a234391055dbe4 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Fri, 5 Sep 2025 17:00:12 +0800 Subject: [PATCH 28/37] fix: cases --- apisix/utils/trusted-addresses.lua | 2 -- t/cli/test_validate_config.sh | 2 +- t/core/trusted-addresses.t | 46 ++---------------------------- 3 files changed, 3 insertions(+), 47 deletions(-) diff --git a/apisix/utils/trusted-addresses.lua b/apisix/utils/trusted-addresses.lua index 9bf8fe605a9d..df2a314a2a91 100644 --- a/apisix/utils/trusted-addresses.lua +++ b/apisix/utils/trusted-addresses.lua @@ -16,8 +16,6 @@ -- local require = require local core = require("apisix.core") -local next = next -local ipairs = ipairs local trusted_addresses_matcher diff --git a/t/cli/test_validate_config.sh b/t/cli/test_validate_config.sh index 516f158c375e..fe1573996313 100755 --- a/t/cli/test_validate_config.sh +++ b/t/cli/test_validate_config.sh @@ -291,7 +291,7 @@ etcd: ' > conf/config.yaml out=$(make init 2>&1 || true) -if ! echo "$out" | grep 'property "trusted_addresses" validation failed.*duplicate'; then +if ! echo "$out" | grep 'property "trusted_addresses" validation failed.*equal'; then echo "failed: trusted_addresses should reject duplicate items" exit 1 fi diff --git a/t/core/trusted-addresses.t b/t/core/trusted-addresses.t index 147fda105e7c..6154858179c2 100644 --- a/t/core/trusted-addresses.t +++ b/t/core/trusted-addresses.t @@ -343,49 +343,7 @@ x-real-ip: 127.0.0.1 -=== TEST 9: with empty trusted_addresses configuration, X-Forwarded headers should be overridden ---- yaml_config -apisix: - node_listen: 1984 - enable_admin: false - trusted_addresses: [] -deployment: - role: data_plane - role_data_plane: - config_provider: yaml ---- apisix_yaml -routes: - - - id: 1 - uri: /old_uri - upstream: - nodes: - "127.0.0.1:1980": 1 - type: roundrobin -#END ---- request -GET /old_uri ---- more_headers -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-host: localhost -x-forwarded-port: 1984 -x-forwarded-proto: http -x-real-ip: 127.0.0.1 ---- no_error_log -trusted_addresses is not configured ---- error_log -trusted_addresses is an empty array -trusted_addresses_matcher is not initialized - - - -=== TEST 10: invalid trusted_addresses configuration: IP is invalid +=== TEST 9: invalid trusted_addresses configuration: IP is invalid --- yaml_config apisix: node_listen: 1984 @@ -401,7 +359,7 @@ failed to create ip matcher for trusted_addresses: invalid ip address: 1.0.0 -=== TEST 11: invalid trusted_addresses configuration: CIDR is invalid +=== TEST 10: invalid trusted_addresses configuration: CIDR is invalid --- yaml_config apisix: node_listen: 1984 From 9457206c93bc89ee2a8b7b820d48186f319ad2b7 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Fri, 5 Sep 2025 17:53:00 +0800 Subject: [PATCH 29/37] fix: validate ip/cidr func --- apisix/utils/trusted-addresses.lua | 15 ++++++++++ t/core/trusted-addresses.t | 46 ++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/apisix/utils/trusted-addresses.lua b/apisix/utils/trusted-addresses.lua index df2a314a2a91..de40eaec3a39 100644 --- a/apisix/utils/trusted-addresses.lua +++ b/apisix/utils/trusted-addresses.lua @@ -16,12 +16,23 @@ -- local require = require local core = require("apisix.core") +local ipairs = ipairs local trusted_addresses_matcher local _M = {} +local function validate_trusted_addresses(trusted_addresses) + for _, cidr in ipairs(trusted_addresses) do + if not core.ip.validate_cidr_or_ip(cidr) then + core.log.error("invalid IP/CIDR '", cidr, "' exists in trusted_addresses") + return false + end + end + return true +end + function _M.init_worker() local local_conf = core.config.local_conf() local trusted_addresses = core.table.try_read_attr(local_conf, "apisix", "trusted_addresses") @@ -31,6 +42,10 @@ function _M.init_worker() return end + if not validate_trusted_addresses(trusted_addresses) then + return + end + local matcher, err = core.ip.create_ip_matcher(trusted_addresses) if not matcher then core.log.error("failed to create ip matcher for trusted_addresses: ", err) diff --git a/t/core/trusted-addresses.t b/t/core/trusted-addresses.t index 6154858179c2..1a3646141c97 100644 --- a/t/core/trusted-addresses.t +++ b/t/core/trusted-addresses.t @@ -106,6 +106,9 @@ x-forwarded-host: example.com x-forwarded-port: 8443 x-forwarded-proto: https x-real-ip: 127.0.0.1 +--- no_error_log +trusted_addresses is not configured +trusted_addresses_matcher is not initialized @@ -145,6 +148,9 @@ x-forwarded-host: example.com x-forwarded-port: 8443 x-forwarded-proto: https x-real-ip: 127.0.0.1 +--- no_error_log +trusted_addresses is not configured +trusted_addresses_matcher is not initialized @@ -183,6 +189,9 @@ x-forwarded-host: example.com x-forwarded-port: 8443 x-forwarded-proto: https x-real-ip: 127.0.0.1 +--- no_error_log +trusted_addresses is not configured +trusted_addresses_matcher is not initialized @@ -222,6 +231,9 @@ x-forwarded-host: example.com x-forwarded-port: 8443 x-forwarded-proto: https x-real-ip: 127.0.0.1 +--- no_error_log +trusted_addresses is not configured +trusted_addresses_matcher is not initialized @@ -263,6 +275,9 @@ x-forwarded-host: example.com x-forwarded-port: 8443 x-forwarded-proto: https x-real-ip: 127.0.0.1 +--- no_error_log +trusted_addresses is not configured +trusted_addresses_matcher is not initialized @@ -340,6 +355,9 @@ x-forwarded-host: localhost x-forwarded-port: 1984 x-forwarded-proto: http x-real-ip: 127.0.0.1 +--- no_error_log +trusted_addresses is not configured +trusted_addresses_matcher is not initialized @@ -354,8 +372,20 @@ deployment: role: data_plane role_data_plane: config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /old_uri + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +#END +--- request +GET /old_uri --- error_log -failed to create ip matcher for trusted_addresses: invalid ip address: 1.0.0 +invalid IP/CIDR '1.0.0' exists in trusted_addresses @@ -370,5 +400,17 @@ deployment: role: data_plane role_data_plane: config_provider: yaml +--- apisix_yaml +routes: + - + id: 1 + uri: /old_uri + upstream: + nodes: + "127.0.0.1:1980": 1 + type: roundrobin +#END +--- request +GET /old_uri --- error_log -failed to create ip matcher for trusted_addresses: invalid ip address: 1.0.0.0/33 +invalid IP/CIDR '1.0.0.0/33' exists in trusted_addresses From 5fd40fc939aca92049e66d10311bc0e8f5442042 Mon Sep 17 00:00:00 2001 From: YYYoung Date: Mon, 15 Sep 2025 09:25:31 +0800 Subject: [PATCH 30/37] Update docs/zh/latest/plugins/chaitin-waf.md --- docs/zh/latest/plugins/chaitin-waf.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/zh/latest/plugins/chaitin-waf.md b/docs/zh/latest/plugins/chaitin-waf.md index 14a2a9983cb1..a90cae2454c9 100644 --- a/docs/zh/latest/plugins/chaitin-waf.md +++ b/docs/zh/latest/plugins/chaitin-waf.md @@ -89,7 +89,6 @@ 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-*` 头将全部被可信值覆盖。 ::: From 528116c496c9c5ea3ef2b3030e1da6596bac67cb Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Mon, 15 Sep 2025 13:44:05 +0800 Subject: [PATCH 31/37] chore: rename `update_var_x_forwarded_headers` to `set_upstream_x_forwarded_headers` --- apisix/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apisix/init.lua b/apisix/init.lua index 8a2bd1480a5d..9c44156a6295 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -617,7 +617,7 @@ local function handle_x_forwarded_headers(api_ctx) end -local function update_var_x_forwarded_headers(api_ctx) +local function set_upstream_x_forwarded_headers(api_ctx) local proto = api_ctx.var.http_x_forwarded_proto if proto then api_ctx.var.var_x_forwarded_proto = proto @@ -793,7 +793,7 @@ function _M.http_access_phase() _M.handle_upstream(api_ctx, route, enable_websocket) - update_var_x_forwarded_headers(api_ctx) + set_upstream_x_forwarded_headers(api_ctx) end From c06b7c26623cee533483eac4aa33b88ac2e5b738 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Tue, 16 Sep 2025 15:40:16 +0800 Subject: [PATCH 32/37] fix: solve comments by @moonming --- apisix/cli/schema.lua | 22 +++++++- t/cli/test_validate_config.sh | 94 ++++++++++++++++++++++++++++++++++- t/core/trusted-addresses.t | 56 --------------------- 3 files changed, 114 insertions(+), 58 deletions(-) diff --git a/apisix/cli/schema.lua b/apisix/cli/schema.lua index ea9188680b13..f180eecfa2d8 100644 --- a/apisix/cli/schema.lua +++ b/apisix/cli/schema.lua @@ -19,6 +19,8 @@ local jsonschema = require("jsonschema") local pairs = pairs local pcall = pcall local require = require +local table_insert = table.insert +local table_concat = table.concat local _M = {} @@ -66,6 +68,24 @@ local etcd_schema = { required = {"prefix", "host"} } +-- copy from apisix/schema_def.lua +local ipv4_seg = "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])" +local ipv4_def_buf = {} +for i = 1, 4 do + table_insert(ipv4_def_buf, ipv4_seg) +end +local ipv4_def = table_concat(ipv4_def_buf, [[\.]]) +-- There is false negative for ipv6/cidr. For instance, `:/8` will be valid. +-- It is fine as the correct regex will be too complex. +local ipv6_def = "([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}" + .. "([a-fA-F0-9]{0,4})?" +local ip_def = { + {title = "IPv4", type = "string", format = "ipv4"}, + {title = "IPv4/CIDR", type = "string", pattern = "^" .. ipv4_def .. "/([12]?[0-9]|3[0-2])$"}, + {title = "IPv6", type = "string", format = "ipv6"}, + {title = "IPv6/CIDR", type = "string", pattern = "^" .. ipv6_def .. "/[0-9]{1,3}$"}, +} + local config_schema = { type = "object", properties = { @@ -263,7 +283,7 @@ local config_schema = { type = "array", minItems = 1, items = { - type = "string", + anyOf = ip_def, }, uniqueItems = true }, diff --git a/t/cli/test_validate_config.sh b/t/cli/test_validate_config.sh index fe1573996313..d779dbe0d9ab 100755 --- a/t/cli/test_validate_config.sh +++ b/t/cli/test_validate_config.sh @@ -266,7 +266,7 @@ etcd: ' > conf/config.yaml out=$(make init 2>&1 || true) -if ! echo "$out" | grep 'property "trusted_addresses" validation failed: failed to validate item 2: wrong type: expected string, got number'; then +if ! echo "$out" | grep 'property "trusted_addresses" validation failed: failed to validate item 2: object matches none of the required'; then echo "failed: trusted_addresses should reject non-string items" exit 1 fi @@ -297,3 +297,95 @@ if ! echo "$out" | grep 'property "trusted_addresses" validation failed.*equal'; fi echo "passed: trusted_addresses rejects duplicate items" + +# Test trusted_addresses with invalid IP +echo ' +apisix: + trusted_addresses: + - "127.0.0" +deployment: + role: traditional + role_traditional: + config_provider: etcd +etcd: + host: + - "http://127.0.0.1:2379" + prefix: "/apisix" +' > conf/config.yaml + +out=$(make init 2>&1 || true) +if ! echo "$out" | grep 'property "trusted_addresses" validation failed: failed to validate item 1: object matches none of the required'; then + echo "failed: trusted_addresses should reject invalid IP" + exit 1 +fi + +echo "passed: trusted_addresses rejects invalid IP" + +# Test trusted_addresses with invalid CIDR +echo ' +apisix: + trusted_addresses: + - "127.0.0.0/33" +deployment: + role: traditional + role_traditional: + config_provider: etcd +etcd: + host: + - "http://127.0.0.1:2379" + prefix: "/apisix" +' > conf/config.yaml + +out=$(make init 2>&1 || true) +if ! echo "$out" | grep 'property "trusted_addresses" validation failed: failed to validate item 1: object matches none of the required'; then + echo "failed: trusted_addresses should reject invalid CIDR" + exit 1 +fi + +echo "passed: trusted_addresses rejects invalid CIDR" + +# Test trusted_addresses with valid IP +echo ' +apisix: + trusted_addresses: + - "127.0.0.1" +deployment: + role: traditional + role_traditional: + config_provider: etcd +etcd: + host: + - "http://127.0.0.1:2379" + prefix: "/apisix" +' > conf/config.yaml + +out=$(make init 2>&1 || true) +if echo "$out" | grep 'property "trusted_addresses" validation failed: failed to validate item 1: object matches none of the required'; then + echo "failed: trusted_addresses should accept valid IP" + exit 1 +fi + +echo "passed: trusted_addresses accepts valid IP" + +# Test trusted_addresses with valid CIDR +echo ' +apisix: + trusted_addresses: + - "127.0.0.0/24" +deployment: + role: traditional + role_traditional: + config_provider: etcd +etcd: + host: + - "http://127.0.0.1:2379" + prefix: "/apisix" +' > conf/config.yaml + +out=$(make init 2>&1 || true) +if echo "$out" | grep 'property "trusted_addresses" validation failed: failed to validate item 1: object matches none of the required'; then + echo "failed: trusted_addresses should accept valid CIDR" + exit 1 +fi + +echo "passed: trusted_addresses accepts valid CIDR" diff --git a/t/core/trusted-addresses.t b/t/core/trusted-addresses.t index 1a3646141c97..c8a0fc6fafc9 100644 --- a/t/core/trusted-addresses.t +++ b/t/core/trusted-addresses.t @@ -358,59 +358,3 @@ x-real-ip: 127.0.0.1 --- no_error_log trusted_addresses is not configured trusted_addresses_matcher is not initialized - - - -=== TEST 9: invalid trusted_addresses configuration: IP is invalid ---- yaml_config -apisix: - node_listen: 1984 - enable_admin: false - trusted_addresses: - - "1.0.0" -deployment: - role: data_plane - role_data_plane: - config_provider: yaml ---- apisix_yaml -routes: - - - id: 1 - uri: /old_uri - upstream: - nodes: - "127.0.0.1:1980": 1 - type: roundrobin -#END ---- request -GET /old_uri ---- error_log -invalid IP/CIDR '1.0.0' exists in trusted_addresses - - - -=== TEST 10: invalid trusted_addresses configuration: CIDR is invalid ---- yaml_config -apisix: - node_listen: 1984 - enable_admin: false - trusted_addresses: - - "1.0.0.0/33" -deployment: - role: data_plane - role_data_plane: - config_provider: yaml ---- apisix_yaml -routes: - - - id: 1 - uri: /old_uri - upstream: - nodes: - "127.0.0.1:1980": 1 - type: roundrobin -#END ---- request -GET /old_uri ---- error_log -invalid IP/CIDR '1.0.0.0/33' exists in trusted_addresses From 30ce9c26e65f9e8ecfc063c08abe9b94b75bd9ba Mon Sep 17 00:00:00 2001 From: Nic Date: Tue, 16 Sep 2025 17:16:51 +0800 Subject: [PATCH 33/37] reverse dependecy between schema_def.lua and schema.lua Signed-off-by: Nic --- apisix/cli/schema.lua | 23 ++--------------------- apisix/core.lua | 2 +- apisix/core/schema.lua | 6 ++++++ apisix/schema_def.lua | 9 --------- 4 files changed, 9 insertions(+), 31 deletions(-) diff --git a/apisix/cli/schema.lua b/apisix/cli/schema.lua index f180eecfa2d8..8a70bda8bca2 100644 --- a/apisix/cli/schema.lua +++ b/apisix/cli/schema.lua @@ -19,8 +19,7 @@ local jsonschema = require("jsonschema") local pairs = pairs local pcall = pcall local require = require -local table_insert = table.insert -local table_concat = table.concat +local schema_def = require("apisix.schema_def") local _M = {} @@ -68,24 +67,6 @@ local etcd_schema = { required = {"prefix", "host"} } --- copy from apisix/schema_def.lua -local ipv4_seg = "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])" -local ipv4_def_buf = {} -for i = 1, 4 do - table_insert(ipv4_def_buf, ipv4_seg) -end -local ipv4_def = table_concat(ipv4_def_buf, [[\.]]) --- There is false negative for ipv6/cidr. For instance, `:/8` will be valid. --- It is fine as the correct regex will be too complex. -local ipv6_def = "([a-fA-F0-9]{0,4}:){1,8}(:[a-fA-F0-9]{0,4}){0,8}" - .. "([a-fA-F0-9]{0,4})?" -local ip_def = { - {title = "IPv4", type = "string", format = "ipv4"}, - {title = "IPv4/CIDR", type = "string", pattern = "^" .. ipv4_def .. "/([12]?[0-9]|3[0-2])$"}, - {title = "IPv6", type = "string", format = "ipv6"}, - {title = "IPv6/CIDR", type = "string", pattern = "^" .. ipv6_def .. "/[0-9]{1,3}$"}, -} - local config_schema = { type = "object", properties = { @@ -283,7 +264,7 @@ local config_schema = { type = "array", minItems = 1, items = { - anyOf = ip_def, + anyOf = schema_def.ip_def, }, uniqueItems = true }, diff --git a/apisix/core.lua b/apisix/core.lua index 14c5186a4aae..ffae65f45637 100644 --- a/apisix/core.lua +++ b/apisix/core.lua @@ -48,7 +48,7 @@ return { request = require("apisix.core.request"), response = require("apisix.core.response"), lrucache = require("apisix.core.lrucache"), - schema = require("apisix.schema_def"), + schema = require("apisix.core.schema"), string = require("apisix.core.string"), ctx = require("apisix.core.ctx"), timer = require("apisix.core.timer"), diff --git a/apisix/core/schema.lua b/apisix/core/schema.lua index 9ce6a553020b..474e276a15e0 100644 --- a/apisix/core/schema.lua +++ b/apisix/core/schema.lua @@ -21,6 +21,7 @@ local jsonschema = require('jsonschema') local lrucache = require("apisix.core.lrucache") +local schema_def = require("apisix.schema_def") local cached_validator = lrucache.new({count = 1000, ttl = 0}) local pcall = pcall @@ -68,4 +69,9 @@ end _M.valid = get_validator +setmetatable(_M, { + __index = schema_def, + __newindex = function() error("no modification allowed") end, +}) + return _M diff --git a/apisix/schema_def.lua b/apisix/schema_def.lua index 78f59fcd3e80..cff101007fd6 100644 --- a/apisix/schema_def.lua +++ b/apisix/schema_def.lua @@ -14,11 +14,8 @@ -- See the License for the specific language governing permissions and -- limitations under the License. -- -local schema = require('apisix.core.schema') local table_insert = table.insert local table_concat = table.concat -local setmetatable = setmetatable -local error = error local _M = {version = 0.5} @@ -1093,10 +1090,4 @@ _M.plugin_injected_schema = { } -setmetatable(_M, { - __index = schema, - __newindex = function() error("no modification allowed") end, -}) - - return _M From bb614f285ff8be528d4ba41a9432359667530dea Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Tue, 16 Sep 2025 18:31:41 +0800 Subject: [PATCH 34/37] chore: add comments --- apisix/init.lua | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/apisix/init.lua b/apisix/init.lua index 9c44156a6295..9d83a2112a1d 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -590,6 +590,7 @@ end local function handle_x_forwarded_headers(api_ctx) local addr_is_trusted = trusted_addresses_util.is_trusted(api_ctx.var.realip_remote_addr) + -- Only untrusted values need to be overwritten or cleared. if not addr_is_trusted then -- store the original x-forwarded-* headers -- to allow future use by other plugins or processes @@ -598,25 +599,49 @@ local function handle_x_forwarded_headers(api_ctx) api_ctx.var.original_x_forwarded_port = api_ctx.var.http_x_forwarded_port api_ctx.var.original_x_forwarded_for = api_ctx.var.http_x_forwarded_for + -- trusted ones + -- ref: ngx_tpl.lua#L831-L840 + -- + -- these values are observed directly by APISIX and cannot be forged, + -- making them highly credible. local proto = api_ctx.var.scheme local host = api_ctx.var.host local port = api_ctx.var.server_port - 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 - - -- override the x-forwarded-* headers to the trusted ones + -- override the x-forwarded-* headers to the trusted ones. + -- make sure that the correct values ​​are obtained + -- in the subsequent stages using `core.request.header`. 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` + -- later processed in ngx_tpl by `$proxy_add_x_forwarded_for`. core.request.set_header(api_ctx, "X-Forwarded-For", nil) + + -- update the cached value in http_x_forwarded_* to the trusted ones. + -- make sure that the correct values ​​are obtained + -- in the subsequent stages using `var.http_x_forwarded_*`. + 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 end end +-- in ngx_tpl.lua#L831-L840, +-- there is such code: `proxy_set_header X-Forwarded-XXX $var_x_forwarded_xxx;` +-- that is, set the `X-Forwarded-XXX` header through `var_x_forwarded_xxx`. +-- +-- therefore, it is necessary to set the trusted `http_x_forwarded_xxx` to `var_x_forwarded_xxx`. +-- So that the `X-Forwarded-XXX` header is updated to a trusted value. +-- +-- currently, only following headers are updated through these variables: +-- - X-Forwarded-Proto +-- - X-Forwarded-Port +-- - X-Forwarded-Host +-- +-- the `X-Forwarded-For` header is not updated through these variables. +-- because it is set by the `proxy_add_x_forwarded_for` directive. local function set_upstream_x_forwarded_headers(api_ctx) local proto = api_ctx.var.http_x_forwarded_proto if proto then From bccb5e6901ccca6ab9d30ed5974f347a06a3e622 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Tue, 16 Sep 2025 18:49:43 +0800 Subject: [PATCH 35/37] fix: lint --- apisix/core/schema.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/apisix/core/schema.lua b/apisix/core/schema.lua index 474e276a15e0..9a50d93b747d 100644 --- a/apisix/core/schema.lua +++ b/apisix/core/schema.lua @@ -24,6 +24,7 @@ local lrucache = require("apisix.core.lrucache") local schema_def = require("apisix.schema_def") local cached_validator = lrucache.new({count = 1000, ttl = 0}) local pcall = pcall +local error = error local _M = { version = 0.3, From 37c639b55c755ca40c30bb332060c772d7809be3 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Wed, 17 Sep 2025 16:37:20 +0800 Subject: [PATCH 36/37] chore: rm code which is covered by json schema --- apisix/utils/trusted-addresses.lua | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/apisix/utils/trusted-addresses.lua b/apisix/utils/trusted-addresses.lua index de40eaec3a39..ade0f8e2464d 100644 --- a/apisix/utils/trusted-addresses.lua +++ b/apisix/utils/trusted-addresses.lua @@ -23,16 +23,6 @@ local trusted_addresses_matcher local _M = {} -local function validate_trusted_addresses(trusted_addresses) - for _, cidr in ipairs(trusted_addresses) do - if not core.ip.validate_cidr_or_ip(cidr) then - core.log.error("invalid IP/CIDR '", cidr, "' exists in trusted_addresses") - return false - end - end - return true -end - function _M.init_worker() local local_conf = core.config.local_conf() local trusted_addresses = core.table.try_read_attr(local_conf, "apisix", "trusted_addresses") @@ -42,10 +32,6 @@ function _M.init_worker() return end - if not validate_trusted_addresses(trusted_addresses) then - return - end - local matcher, err = core.ip.create_ip_matcher(trusted_addresses) if not matcher then core.log.error("failed to create ip matcher for trusted_addresses: ", err) From fa5a6b6496c89c8c87246264c1da5173a8d63301 Mon Sep 17 00:00:00 2001 From: SkyeYoung Date: Wed, 17 Sep 2025 16:53:21 +0800 Subject: [PATCH 37/37] fix: lint --- apisix/utils/trusted-addresses.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/apisix/utils/trusted-addresses.lua b/apisix/utils/trusted-addresses.lua index ade0f8e2464d..df2a314a2a91 100644 --- a/apisix/utils/trusted-addresses.lua +++ b/apisix/utils/trusted-addresses.lua @@ -16,7 +16,6 @@ -- local require = require local core = require("apisix.core") -local ipairs = ipairs local trusted_addresses_matcher