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: use interceptors to protect plugin's route #2416

Merged
merged 4 commits into from
Oct 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 18 additions & 5 deletions apisix/admin/plugin_metadata.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local error = error
local pcall = pcall
local require = require
local core = require("apisix.core")
local api_router = require("apisix.api_router")

local _M = {
}
Expand Down Expand Up @@ -44,15 +46,26 @@ local function check_conf(plugin_name, conf)
return nil, {error_msg = "invalid plugin name"}
end

local schema = plugin_object.metadata_schema
if not schema then
return nil, {error_msg = "no metadata schema for plugin " .. plugin_name}
end

if not conf then
return nil, {error_msg = "missing configurations"}
end

local schema = plugin_object.metadata_schema or {
type = "object",
properties = {},
}
if not schema.properties then
schema.properties = {
additionalProperties = false,
}
end

-- inject interceptors schema to each plugins
if schema.properties.interceptors then
error("'interceptors' can not be used as the name of metadata schema's field")
end
schema.properties.interceptors = api_router.interceptors_schema

core.log.info("schema: ", core.json.delay_encode(schema))
core.log.info("conf : ", core.json.delay_encode(conf))
local ok, err = core.schema.check(schema, conf)
Expand Down
72 changes: 67 additions & 5 deletions apisix/api_router.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,57 @@
--
local require = require
local router = require("resty.radixtree")
local plugin = require("apisix.plugin")
local plugin_mod = require("apisix.plugin")
local ip_restriction = require("apisix.plugins.ip-restriction")
local core = require("apisix.core")
local ipairs = ipairs


local _M = {}
local match_opts = {}
local interceptors = {
["ip-restriction"] = {
run = function (conf, ctx)
return ip_restriction.access(conf, ctx)
end,
schema = ip_restriction.schema,
}
}


_M.interceptors_schema = {
type = "array",
items = {
type = "object",
minItems = 1,
properties = {
name = {
type = "string",
enum = {"ip-restriction"},
},
conf = {
type = "object",
}
},
required = {"name", "conf"},
dependencies = {
name = {
oneOf = {}
}
}
}
}
for name, attrs in pairs(interceptors) do
core.table.insert(_M.interceptors_schema.items.properties.name.enum, name)
core.table.insert(_M.interceptors_schema.items.dependencies.name.oneOf, {
properties = {
name = {
enum = {name},
},
conf = attrs.schema,
}
})
end


local fetch_api_router
Expand All @@ -31,18 +75,36 @@ do
function fetch_api_router()
core.table.clear(routes)

for _, plugin in ipairs(plugin.plugins) do
for _, plugin in ipairs(plugin_mod.plugins) do
local api_fun = plugin.api
if api_fun then
local name = plugin.name
local api_routes = api_fun()
core.log.debug("fetched api routes: ",
core.json.delay_encode(api_routes, true))
for _, route in ipairs(api_routes) do
core.table.insert(routes, {
methods = route.methods,
paths = route.uri,
handler = function (...)
local code, body = route.handler(...)
handler = function (api_ctx)
local code, body

local metadata = plugin_mod.plugin_metadata(name)
if metadata and metadata.interceptors then
for _, rule in ipairs(metadata.interceptors) do
local f = interceptors[rule.name]
if f == nil then
core.log.error("unknown interceptor: ", rule.name)
else
code, body = f.run(rule.conf, api_ctx)
if code or body then
return core.response.exit(code, body)
end
end
end
end

code, body = route.handler(api_ctx)
if code or body then
core.response.exit(code, body)
end
Expand All @@ -59,7 +121,7 @@ end -- do


function _M.match(api_ctx)
local api_router = core.lrucache.global("api_router", plugin.load_times, fetch_api_router)
local api_router = core.lrucache.global("api_router", plugin_mod.load_times, fetch_api_router)
if not api_router then
core.log.error("failed to fetch valid api router")
return false
Expand Down
55 changes: 55 additions & 0 deletions doc/plugin-interceptors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!--
#
# 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.
#
-->

[Chinese](zh-cn/plugin-interceptors.md)

## Plugin interceptors

Some plugins will register API to serve their purposes.

Since these API are not added as regular [Route](admin-api.md), we can't add
plugins to protect them. To solve the problem, we add a new concept called 'interceptors'
to run rules to protect them.

Here is an example to limit the access of `/apisix/prometheus/metrics` (a route introduced via plugin prometheus)
to clients in `10.0.0.0/24`:

```shell
$ curl http://127.0.0.1:9080/apisix/admin/plugin_metadata/prometheus -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -i -X PUT -d '
{
"interceptors": [
{
"name": "ip-restriction",
"conf": {
"whitelist": ["10.0.0.0/24"]
}
}
]
}
```

You can see that the interceptors are configured like the plugins. The `name` is
the name of plugin which you want to run and the `conf` is the configuration of the
plugin.

Currently we only support a subset of plugins which can be run as interceptors.

Supported interceptors:

* [ip-restriction](./plugins/ip-restriction.md)
5 changes: 5 additions & 0 deletions doc/plugins/batch-requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@

None

## API

This plugin will add `/apisix/batch-requests` as the endpoint.
You may need to use [interceptors](plugin-interceptors.md) to protect it.

## How To Enable

Default enabled
Expand Down
5 changes: 5 additions & 0 deletions doc/plugins/jwt-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ For more information on JWT, refer to [JWT](https://jwt.io/) for more informatio
| exp | integer | optional | 86400 | [1,...] | token's expire time, in seconds |
| base64_secret | boolean | optional | false | | whether secret is base64 encoded |

## API

This plugin will add `/apisix/plugin/jwt/sign` to sign.
You may need to use [interceptors](plugin-interceptors.md) to protect it.

## How To Enable

1. set a consumer and config the value of the `jwt-auth` option
Expand Down
5 changes: 5 additions & 0 deletions doc/plugins/prometheus.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ This plugin exposes metrics in Prometheus Exposition format.

none.

## API

This plugin will add `/apisix/prometheus/metrics` to expose the metrics.
You may need to use [interceptors](plugin-interceptors.md) to protect it.

## How to enable it

`prometheus` plugin can be enable with empty table, because it doesn't have
Expand Down
9 changes: 9 additions & 0 deletions doc/plugins/wolf-rbac.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ The rbac feature is provided by [wolf](https://github.com/iGeeky/wolf). For more
| appid | string | optional | "unset" | | Set the app id. The app id must be added in wolf-console. |
| header_prefix | string | optional | "X-" | | prefix of custom HTTP header. After authentication is successful, three headers will be added to the request header (for backend) and response header (for frontend): `X-UserId`, `X-Username`, `X-Nickname`. |

## API

This plugin will add several API:

* /apisix/plugin/wolf-rbac/login
* /apisix/plugin/wolf-rbac/change_pwd
* /apisix/plugin/wolf-rbac/user_info

You may need to use [interceptors](plugin-interceptors.md) to protect it.

## Dependencies

Expand Down
50 changes: 50 additions & 0 deletions doc/zh-cn/plugin-interceptors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!--
#
# 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.
#
-->

[English](../plugin-interceptors.md)

## Plugin interceptors

有些插件为实现它的功能会注册额外的接口。

由于这些接口不是通过 admin API 添加的,所以没办法像管理 Route 那样管理它们。为了能够保护这些接口不被利用,我们引入了 interceptors 的概念。

下面是通过 interceptors 来保护由 prometheus 插件引入的 `/apisix/prometheus/metrics` 接口,限定只能由 `10.0.0.0/24` 网段的用户访问:

```shell
$ curl http://127.0.0.1:9080/apisix/admin/plugin_metadata/prometheus -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -i -X PUT -d '
{
"interceptors": [
{
"name": "ip-restriction",
"conf": {
"whitelist": ["10.0.0.0/24"]
}
}
]
}
```

我们能看到配置 interceptors 就像配置 plugin 一样:name 是 interceptor 的名称,而 conf 是它的配置。

当前我们只支持一部分插件作为 interceptor 运行。

支持的 interceptor:

* [ip-restriction](./plugins/ip-restriction.md)
5 changes: 5 additions & 0 deletions doc/zh-cn/plugins/batch-requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@


## 接口

插件会增加 `/apisix/batch-requests` 这个接口,你可能需要通过 [interceptors](plugin-interceptors.md)
来保护它。

## 如何启用

本插件默认启用。
Expand Down
5 changes: 5 additions & 0 deletions doc/zh-cn/plugins/jwt-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@
| exp | integer | 可选 | 86400 | [1,...] | token 的超时时间 |
| base64_secret | boolean | 可选 | false | | 密钥是否为 base64 编码 |

## 接口

插件会增加 `/apisix/plugin/jwt/sign` 这个接口,你可能需要通过 [interceptors](plugin-interceptors.md)
来保护它。

## 如何启用

1. 创建一个 consumer 对象,并设置插件 `jwt-auth` 的值。
Expand Down
5 changes: 5 additions & 0 deletions doc/zh-cn/plugins/prometheus.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@


## 接口

插件会增加 `/apisix/prometheus/metrics` 这个接口,你可能需要通过 [interceptors](plugin-interceptors.md)
来保护它。

## 如何开启插件

`prometheus` 插件用空{}就可以开启了,他没有任何的选项。
Expand Down
10 changes: 10 additions & 0 deletions doc/zh-cn/plugins/wolf-rbac.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ rbac功能由[wolf](https://github.com/iGeeky/wolf)提供, 有关 `wolf` 的更
| appid | string | 可选 | "unset" | | 设置应用id, 该应用id, 需要是在 `wolf-console` 中已经添加的应用id |
| header_prefix | string | 可选 | "X-" | | 自定义http头的前缀。`wolf-rbac`在鉴权成功后, 会在请求头(用于传给后端)及响应头(用于传给前端)中添加3个头: `X-UserId`, `X-Username`, `X-Nickname` |

## 接口

插件会增加这些接口:

* /apisix/plugin/wolf-rbac/login
* /apisix/plugin/wolf-rbac/change_pwd
* /apisix/plugin/wolf-rbac/user_info

你可能需要通过 [interceptors](plugin-interceptors.md) 来保护它们。

## 依赖项

### 安装 wolf, 并启动服务
Expand Down
Loading