-
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 33 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 |
|---|---|---|
|
|
@@ -259,6 +259,14 @@ local config_schema = { | |
| default = false, | ||
| description = "a global switch to disable upstream health checks", | ||
| }, | ||
| trusted_addresses = { | ||
| type = "array", | ||
| minItems = 1, | ||
| items = { | ||
| type = "string", | ||
|
||
| }, | ||
| uniqueItems = true | ||
| }, | ||
| } | ||
| }, | ||
| nginx_config = { | ||
|
|
||
| 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 | ||||||||||||||||||||||||||||
|
Comment on lines
-295
to
-309
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. Lines 277 to 282 in 09f6e36
Because the code above will replace Lines 115 to 117 in 09f6e36
This will cause this deleted part of the code to be skipped. Now that we need to handle Lines 835 to 838 in 3260931
|
||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
@@ -599,6 +587,54 @@ 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 | ||||||||||||||||||||||||||||
| -- 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 | ||||||||||||||||||||||||||||
| 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.
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,67 @@ | ||
| -- | ||
| -- 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 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 |
||
| core.log.info("trusted_addresses is not configured") | ||
| 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 | ||

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.
ref:
apisix/apisix/plugins/real-ip.lua
Lines 32 to 36 in bb71dfc
we can add more restrction to schema like this.
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.
Adding core.schema.ip_def doesn't seem to work.
https://github.com/apache/apisix/blob/8bb6802bd2b2ccd4dabe78e90beb8f87ec046035/apisix/schema_def.lua depends on https://github.com/apache/apisix/blob/0fd582bc299a9d90893d3843cd3ec21f217cb40e/apisix/core/schema.lua
Using it will cause an error
/usr/local/openresty//luajit/bin/luajit: ./apisix/core/lrucache.lua:22: module '\''resty.lrucache'\'' not found: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.
What is the reason for this error?
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.
The reason for this error is that there is no openresty environment in the apisix cli, which makes it impossible to use the
resty.lrulibrary.I have considered the dependency relationship between
schema_def.luaandschema.lua, and I think their dependency relationship is reversed. It should be thatschema.luadepends onschema_def.lua, allowingschema_def.luato retain only the definition of schema, in line with the naming of this file.Added a new commit to this PR to fix this dependency issue: 30ce9c2