Skip to content
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

feat(real-ip): support search recursive #6988

Merged
merged 19 commits into from
May 13, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions apisix/plugins/real-ip.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ local core = require("apisix.core")
local is_apisix_or, client = pcall(require, "resty.apisix.client")
local str_byte = string.byte
local str_sub = string.sub
local str_gmatch = string.gmatch
local tb_insert = table.insert
local ipairs = ipairs
local type = type

local matcher

local schema = {
type = "object",
Expand All @@ -33,6 +36,10 @@ local schema = {
source = {
type = "string",
minLength = 1
},
recursive = {
type = "boolean",
default = false
}
},
required = {"source"},
Expand Down Expand Up @@ -67,6 +74,19 @@ function _M.check_schema(conf)
end


local function addr_match(conf, addr)
if not conf.trusted_addresses then
tangzhenhuang marked this conversation as resolved.
Show resolved Hide resolved
return false
end

if not matcher then
tangzhenhuang marked this conversation as resolved.
Show resolved Hide resolved
matcher = core.ip.create_ip_matcher(conf.trusted_addresses)
end

return matcher:match(addr)
end


local function get_addr(conf, ctx)
if conf.source == "http_x_forwarded_for" then
-- use the last address from X-Forwarded-For header
Expand All @@ -84,6 +104,22 @@ local function get_addr(conf, ctx)
return addrs
end

if conf.recursive and conf.trusted_addresses then
local split_addrs = {}
local match_itr = str_gmatch(addrs, "[^,%s*]+")
tangzhenhuang marked this conversation as resolved.
Show resolved Hide resolved
for itr in match_itr do
tb_insert(split_addrs, itr)
end

for i = #split_addrs, 1, -1 do
tangzhenhuang marked this conversation as resolved.
Show resolved Hide resolved
if not addr_match(conf, split_addrs[i]) then
return split_addrs[i]
end
end

return split_addrs[1]
end

for i = idx + 1, #addrs do
if str_byte(addrs, i) == str_byte(" ") then
idx = idx + 1
Expand All @@ -105,12 +141,8 @@ function _M.rewrite(conf, ctx)
end

if conf.trusted_addresses then
if not conf.matcher then
conf.matcher = core.ip.create_ip_matcher(conf.trusted_addresses)
end

local remote_addr = ctx.var.remote_addr
local trusted = conf.matcher:match(remote_addr)
local trusted = addr_match(conf, remote_addr)
if not trusted then
return
end
tangzhenhuang marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
9 changes: 5 additions & 4 deletions docs/en/latest/plugins/real-ip.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ This Plugin requires APISIX to run on [APISIX-OpenResty](../how-to-build.md#step

## Attributes

| Name | Type | Required | Valid values | Description |
|-------------------|---------------|----------|-----------------------------------------------------------------|-----------------------------------------------------------------------------------|
| source | string | True | Any Nginx variable like `arg_realip` or `http_x_forwarded_for`. | Dynamically sets the client's IP address and an optional port from APISIX's view. |
| trusted_addresses | array[string] | False | List of IPs or CIDR ranges. | Dynamically sets the `set_real_ip_from` field. |
| Name | Type | Required | Valid values | Description |
|-------------------|---------------|----------|-----------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| source | string | True | Any Nginx variable like `arg_realip` or `http_x_forwarded_for`. | Dynamically sets the client's IP address and an optional port from APISIX's view. |
| trusted_addresses | array[string] | False | List of IPs or CIDR ranges. | Dynamically sets the `set_real_ip_from` field. |
| recursive | boolean | False | True to enable, false to disable, default is false | If recursive search is disabled, the original client address that matches one of the trusted addresses is replaced by the last address sent in the request header field defined by the real_ip_header directive. If recursive search is enabled, the original client address that matches one of the trusted addresses is replaced by the last non-trusted address sent in the request header field. |
tangzhenhuang marked this conversation as resolved.
Show resolved Hide resolved

:::note

Expand Down
7 changes: 4 additions & 3 deletions docs/zh/latest/plugins/real-ip.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ description: 本文介绍了关于 Apache APISIX `real-ip` 插件的基本信息
## 属性

| 名称 | 类型 | 必选项 | 有效值 | 描述 |
|-------------------|---------------|-------|-------------------------------------------------------------|----------------------------------------------------------------------|
| source | string | 是 | 任何 NGINX 变量,如 `arg_realip` 或 `http_x_forwarded_for` 。 | 动态设置客户端的 IP 地址和端口。如果该值不包含端口,则不会更改客户端的端口。|
| trusted_addresses | array[string] | 否 | IP 或 CIDR 范围列表。 | 动态设置 `set_real_ip_from` 字段。 |
|-------------------|---------------|--|-------------------------------------------------------------|----------------------------------------------------------------------|
| source | string | 是 | 任何 NGINX 变量,如 `arg_realip` 或 `http_x_forwarded_for` 。 | 动态设置客户端的 IP 地址和端口。如果该值不包含端口,则不会更改客户端的端口。|
| trusted_addresses | array[string] | 否 | IP 或 CIDR 范围列表。 | 动态设置 `set_real_ip_from` 字段。 |
| recursive | boolean | 否 | true 或者 false,默认是 false | 如果禁用递归搜索,则与受信任地址之一匹配的原始客户端地址将替换为由 real_ip_header 指令定义的请求标头字段中发送的最后一个地址。如果启用递归搜索,则与其中一个受信任地址匹配的原始客户端地址将替换为请求标头字段中发送的最后一个非受信任地址。 |

:::note

Expand Down
47 changes: 47 additions & 0 deletions t/plugin/real-ip.t
Original file line number Diff line number Diff line change
Expand Up @@ -427,3 +427,50 @@ passed
GET /hello
--- more_headers
X-Forwarded-For: 1.1.1.1



=== TEST 22: X-Forwarded-For and recursive
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/hello",
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
},
"plugins": {
"real-ip": {
"trusted_addresses": ["192.128.0.0/16", "127.0.0.0/24"],
"source": "http_x_forwarded_for",
"recursive": true
},
"ip-restriction": {
"whitelist": ["1.1.1.1"]
}
}
}]]
)

if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed



=== TEST 23: hit
--- request
GET /hello
--- more_headers
X-Forwarded-For: 1.1.1.1, 192.128.1.1, 127.0.0.1