-
Couldn't load subscription status.
- Fork 2.7k
fix: only trust X-Forwarded-* headers from trusted_addresses
#12551
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 18 commits
b67dc67
34b5953
5bcc9ff
a9b3e4c
1ab46cd
b9cf720
4f2ed0c
5411324
5e12819
197ce75
8a271a5
cd98ba8
809b1e9
1a072b2
6614dc0
510b2b3
16ecd73
1e3950e
1b6e5e6
f8c3aaa
247c208
1d74451
31947b8
818cb0a
80d561f
3bad749
0d8e1b8
395cf61
9457206
3acc848
b9cfd9c
5fd40fc
528116c
c06b7c2
c7e372d
30ce9c2
bb614f2
5020eb4
bccb5e6
4a3cb12
37c639b
be9a275
fa5a6b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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 | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -290,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 | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -599,6 +587,53 @@ function _M.handle_upstream(api_ctx, route, enable_websocket) | |||||||||||||||||
| end | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| local function handle_x_forwarded_headers(api_ctx) | ||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add some comments to explain the purpose of each code segment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||||||||||||||||||
| local addr_is_trusted = trusted_addresses_util.is_trusted(api_ctx.var.realip_remote_addr) | ||||||||||||||||||
membphis marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
|
|
||||||||||||||||||
| 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 | ||||||||||||||||||
| 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 | ||||||||||||||||||
| api_ctx.var.http_x_forwarded_for = nil | ||||||||||||||||||
|
|
||||||||||||||||||
| -- override the x-forwarded-* headers to the trusted ones | ||||||||||||||||||
|
||||||||||||||||||
| set $var_x_forwarded_proto $scheme; | |
| set $var_x_forwarded_host $host; | |
| set $var_x_forwarded_port $server_port; | |
| proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
| proxy_set_header X-Forwarded-Proto $var_x_forwarded_proto; | |
| proxy_set_header X-Forwarded-Host $var_x_forwarded_host; | |
| proxy_set_header X-Forwarded-Port $var_x_forwarded_port; |
Referenced the implementation of APISIX.
SkyeYoung marked this conversation as resolved.
Show resolved
Hide resolved
nic-6443 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not set X-Forwarded-Proto as http_x_forwarded_proto?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite understand.
- This function is extracted from the following code without any adjustments.
X-Forwarded-Protoin the header ==http_x_forwarded_proto
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| -- | ||
| -- 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 | ||
| 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") | ||
|
|
||
| if not trusted_addresses then | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This situation is already covered by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
| 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 | ||
|
|
||
| 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) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just defensive programming. The validation of ip/cidr has already been handled in Therefore, only There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might ask why verification is needed upfront. This is because I just discovered that https://github.com/api7/lua-resty-ipmatcher has the following issue that needs to be fixed:
This library considers |
||
| 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") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
| return false | ||
| end | ||
| return trusted_addresses_matcher:match(address) | ||
| end | ||
SkyeYoung marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return _M | ||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -141,6 +141,10 @@ 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 | ||||||||
SkyeYoung marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
| # trusted_addresses: # When enabled, APISIX will trust the `X-Forwarded-*` Headers | |
| # trusted_addresses: # When enabled, APISIX will trust the `X-Forwarded-*` Headers |
how to disabled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If not configured, it is disabled by default.
This statement references
apisix/conf/config.yaml.example
Line 138 in 1e3950e
| # status: # When enabled, APISIX will provide `/status` and `/status/ready` endpoints |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
write it in this file and docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No IP restriction is a bad example
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done

Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apisix/apisix/plugins/ai.lua
Lines 277 to 282 in 09f6e36
Because the code above will replace
handle_upstreamwithai_upstreambelow:apisix/apisix/plugins/ai.lua
Lines 115 to 117 in 09f6e36
This will cause this deleted part of the code to be skipped.
Now that we need to handle
X-Forwarded-*, we need to update thesevar_x_forwarded_*, so we need to extract this part of the code asupdate_var_x_forwarded_headers.apisix/apisix/cli/ngx_tpl.lua
Lines 835 to 838 in 3260931