You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by me...@apache.org on 2020/06/22 06:46:01 UTC
[incubator-apisix] branch master updated: feature: implemented
plugin `uri-blocklist` . (#1727)
This is an automated email from the ASF dual-hosted git repository.
membphis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 6a43a8c feature: implemented plugin `uri-blocklist` . (#1727)
6a43a8c is described below
commit 6a43a8c735cc984df1d23ce1b6c4bc834c628d18
Author: YuanSheng Wang <me...@gmail.com>
AuthorDate: Mon Jun 22 14:45:55 2020 +0800
feature: implemented plugin `uri-blocklist` . (#1727)
first step: #1617
---
README.md | 3 +-
README_CN.md | 3 +-
apisix/plugins/uri-blocker.lua | 86 +++++++++++++
conf/config.yaml | 3 +-
doc/README.md | 4 +-
doc/plugins/uri-blocker.md | 96 +++++++++++++++
doc/zh-cn/README.md | 1 +
t/admin/plugins.t | 2 +-
t/debug/debug-mode.t | 1 +
t/plugin/uri-blocker.t | 266 +++++++++++++++++++++++++++++++++++++++++
10 files changed, 460 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index da9ccf1..3572484 100644
--- a/README.md
+++ b/README.md
@@ -90,7 +90,8 @@ A/B testing, canary release, blue-green deployment, limit rate, defense against
- [Limit-count](doc/plugins/limit-count.md)
- [Limit-concurrency](doc/plugins/limit-conn.md)
- Anti-ReDoS(Regular expression Denial of Service): Built-in policies to Anti ReDoS without configuration.
- - [CORS](doc/plugins/cors.md)
+ - [CORS](doc/plugins/cors.md) Enable CORS(Cross-origin resource sharing) for your API.
+ - [uri-blocker](plugins/uri-blocker.md): Block client request by URI.
- **OPS friendly**
- OpenTracing: support [Apache Skywalking](doc/plugins/skywalking.md) and [Zipkin](doc/plugins/zipkin.md)
diff --git a/README_CN.md b/README_CN.md
index 1fbd998..408fc22 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -90,7 +90,8 @@ A/B 测试、金丝雀发布(灰度发布)、蓝绿部署、限流限速、抵
- [限制请求数](doc/zh-cn/plugins/limit-count.md)
- [限制并发](doc/zh-cn/plugins/limit-conn.md)
- 防御 ReDoS(正则表达式拒绝服务):内置策略,无需配置即可抵御 ReDoS。
- - [CORS](doc/zh-cn/plugins/cors.md)
+ - [CORS](doc/zh-cn/plugins/cors.md):为你的API启用 CORS。
+ - [uri-blocker](plugins/uri-blocker.md):根据 URI 拦截用户请求。
- **运维友好**
- OpenTracing 可观测性: 支持 [Apache Skywalking](doc/zh-cn/plugins/skywalking.md) 和 [Zipkin](doc/zh-cn/plugins/zipkin.md)。
diff --git a/apisix/plugins/uri-blocker.lua b/apisix/plugins/uri-blocker.lua
new file mode 100644
index 0000000..ab5b682
--- /dev/null
+++ b/apisix/plugins/uri-blocker.lua
@@ -0,0 +1,86 @@
+--
+-- 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 core = require("apisix.core")
+local re_compile = require("resty.core.regex").re_match_compile
+local re_find = ngx.re.find
+local ipairs = ipairs
+
+local schema = {
+ type = "object",
+ properties = {
+ block_rules = {
+ type = "array",
+ items = {
+ type = "string",
+ minLength = 1,
+ maxLength = 4096,
+ },
+ uniqueItems = true
+ },
+ rejected_code = {
+ type = "integer",
+ minimum = 200,
+ default = 403
+ },
+ },
+ required = {"block_rules"},
+}
+
+
+local plugin_name = "uri-blocker"
+
+local _M = {
+ version = 0.1,
+ priority = 2900,
+ name = plugin_name,
+ schema = schema,
+}
+
+
+function _M.check_schema(conf)
+ local ok, err = core.schema.check(schema, conf)
+ if not ok then
+ return false, err
+ end
+
+ local block_rules = {}
+ for i, re_rule in ipairs(conf.block_rules) do
+ local ok, err = re_compile(re_rule, "j")
+ -- core.log.warn("ok: ", tostring(ok), " err: ", tostring(err), " re_rule: ", re_rule)
+ if not ok then
+ return false, err
+ end
+ block_rules[i] = re_rule
+ end
+
+ conf.block_rules_concat = core.table.concat(block_rules, "|")
+ core.log.info("concat block_rules: ", conf.block_rules_concat)
+ return true
+end
+
+
+function _M.rewrite(conf, ctx)
+ core.log.info("uri: ", ctx.var.request_uri)
+ core.log.info("block uri rules: ", conf.block_rules_concat)
+ local from = re_find(ctx.var.request_uri, conf.block_rules_concat, "jo")
+ if from then
+ core.response.exit(conf.rejected_code)
+ end
+end
+
+
+return _M
diff --git a/conf/config.yaml b/conf/config.yaml
index d640ef7..cee95a6 100644
--- a/conf/config.yaml
+++ b/conf/config.yaml
@@ -95,7 +95,7 @@ apisix:
ssl_ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES2 [...]
key_encrypt_salt: "edd1c9f0985e76a2" # If not set, will save origin ssl key into etcd.
# If set this, must be a string of length 16. And it will encrypt ssl key with AES-128-CBC
- # !!! So do not change it after saving your ssl, it can't decrypt the ssl keys have be saved if you change !!
+ # !!! So do not change it after saving your ssl, it can't decrypt the ssl keys have be saved if you change !!
# discovery: eureka # service discovery center
nginx_config: # config for render the template to genarate nginx.conf
error_log: "logs/error.log"
@@ -168,6 +168,7 @@ plugins: # plugin list
- skywalking
- echo
- authz-keycloak
+ - uri-blocker
stream_plugins:
- mqtt-proxy
diff --git a/doc/README.md b/doc/README.md
index 042012d..3f9142a 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -65,10 +65,12 @@ Plugins
* [kafka-logger](plugins/kafka-logger.md): Log requests to External Kafka servers.
* [cors](plugins/cors.md): Enable CORS(Cross-origin resource sharing) for your API.
* [batch-requests](plugins/batch-requests.md): Allow you send mutiple http api via **http pipeline**.
-* [authz-keycloak](plugins/authz-keycloak.md): Authorization with Keycloak Identity Server
+* [authz-keycloak](plugins/authz-keycloak.md): Authorization with Keycloak Identity Server.
+* [uri-blocker](plugins/uri-blocker.md): Block client request by URI.
Deploy to the Cloud
=======
+
### AWS
The recommended approach is to deploy APISIX with [AWS CDK](https://aws.amazon.com/cdk/) on [AWS Fargate](https://aws.amazon.com/fargate/) which helps you decouple the APISIX layer and the upstream layer on top of a fully-managed and secure serverless container compute environment with autoscaling capabilities.
diff --git a/doc/plugins/uri-blocker.md b/doc/plugins/uri-blocker.md
new file mode 100644
index 0000000..270f303
--- /dev/null
+++ b/doc/plugins/uri-blocker.md
@@ -0,0 +1,96 @@
+<!--
+#
+# 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](uri-blocker.md)
+
+# Summary
+
+- [**Name**](#name)
+- [**Attributes**](#attributes)
+- [**How To Enable**](#how-to-enable)
+- [**Test Plugin**](#test-plugin)
+- [**Disable Plugin**](#disable-plugin)
+
+## Name
+
+The plugin helps we intercept user requests, we only need to indicate the `block_rules`.
+
+## Attributes
+
+|Name |Requirement |Description|
+|--------- |--------|-----------|
+|block_rules |required|Regular filter rule array. Each of these items is a regular rule. If the current request URI hits any one of them, set the response code to rejected_code to exit the current user request. Example: `["root.exe", "root.m+"]`.|
+|rejected_code |optional|The HTTP status code returned when the request URI hit any of `filter_rule`, default `403`.|
+
+## How To Enable
+
+Here's an example, enable the `uri blocker` plugin on the specified route:
+
+```shell
+curl -i http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "uri": "/*",
+ "plugins": {
+ "uri-blocker": {
+ "block_rules": ["root.exe", "root.m+"]
+ }
+ },
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:1980": 1
+ }
+ }
+}'
+```
+
+## Test Plugin
+
+```shell
+$ curl -i http://127.0.0.1:9080/root.exe?a=a
+HTTP/1.1 403 Forbidden
+Date: Wed, 17 Jun 2020 13:55:41 GMT
+Content-Type: text/html; charset=utf-8
+Content-Length: 150
+Connection: keep-alive
+Server: APISIX web server
+
+... ...
+```
+
+## Disable Plugin
+
+When you want to disable the `uri blocker` plugin, it is very simple,
+ you can delete the corresponding json configuration in the plugin configuration,
+ no need to restart the service, it will take effect immediately:
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "uri": "/*",
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:1980": 1
+ }
+ }
+}'
+```
+
+The `uri blocker` plugin has been disabled now. It works for other plugins.
diff --git a/doc/zh-cn/README.md b/doc/zh-cn/README.md
index 2d9965c..9b2c6c5 100644
--- a/doc/zh-cn/README.md
+++ b/doc/zh-cn/README.md
@@ -67,3 +67,4 @@ Reference document
* [cors](plugins/cors.md): 为你的API启用 CORS
* [batch-requests](plugins/batch-requests.md): 以 **http pipeline** 的方式在网关一次性发起多个 `http` 请求。
* [authz-keycloak](plugins/authz-keycloak-cn.md): 支持 Keycloak 身份认证服务器
+* [uri-blocker](plugins/uri-blocker.md): 根据 URI 拦截用户请求。
diff --git a/t/admin/plugins.t b/t/admin/plugins.t
index 1a5f06e..b134b97 100644
--- a/t/admin/plugins.t
+++ b/t/admin/plugins.t
@@ -30,7 +30,7 @@ __DATA__
--- request
GET /apisix/admin/plugins/list
--- response_body_like eval
-qr/\["limit-req","limit-count","limit-conn","key-auth","basic-auth","prometheus","node-status","jwt-auth","zipkin","ip-restriction","grpc-transcode","serverless-pre-function","serverless-post-function","openid-connect","proxy-rewrite","redirect","response-rewrite","fault-injection","udp-logger","wolf-rbac","proxy-cache","tcp-logger","proxy-mirror","kafka-logger","cors","consumer-restriction","syslog","batch-requests","http-logger","skywalking","echo","authz-keycloak"\]/
+qr/\["limit-req","limit-count","limit-conn","key-auth","basic-auth","prometheus","node-status","jwt-auth","zipkin","ip-restriction","grpc-transcode","serverless-pre-function","serverless-post-function","openid-connect","proxy-rewrite","redirect","response-rewrite","fault-injection","udp-logger","wolf-rbac","proxy-cache","tcp-logger","proxy-mirror","kafka-logger","cors","consumer-restriction","syslog","batch-requests","http-logger","skywalking","echo","authz-keycloak","uri-blocker"\]/
--- no_error_log
[error]
diff --git a/t/debug/debug-mode.t b/t/debug/debug-mode.t
index 2924cdc..05534e1 100644
--- a/t/debug/debug-mode.t
+++ b/t/debug/debug-mode.t
@@ -60,6 +60,7 @@ loaded plugin and sort by priority: 10000 name: serverless-pre-function
loaded plugin and sort by priority: 4010 name: batch-requests
loaded plugin and sort by priority: 4000 name: cors
loaded plugin and sort by priority: 3000 name: ip-restriction
+loaded plugin and sort by priority: 2900 name: uri-blocker
loaded plugin and sort by priority: 2599 name: openid-connect
loaded plugin and sort by priority: 2555 name: wolf-rbac
loaded plugin and sort by priority: 2520 name: basic-auth
diff --git a/t/plugin/uri-blocker.t b/t/plugin/uri-blocker.t
new file mode 100644
index 0000000..5f10640
--- /dev/null
+++ b/t/plugin/uri-blocker.t
@@ -0,0 +1,266 @@
+#
+# 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(2);
+no_long_string();
+no_root_location();
+no_shuffle();
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: invalid regular expression
+--- 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,
+ [[{
+ "plugins": {
+ "uri-blocker": {
+ "block_rules": [".+("]
+ }
+ },
+ "uri": "/hello"
+ }]])
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.print(body)
+ }
+}
+--- request
+GET /t
+--- error_code: 400
+--- response_body
+{"error_msg":"failed to check the configuration of plugin uri-blocker err: pcre_compile() failed: missing ) in \".+(\""}
+--- no_error_log
+[error]
+
+
+
+=== TEST 2: multiple valid rules
+--- 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,
+ [[{
+ "plugins": {
+ "uri-blocker": {
+ "block_rules": ["^a", "^b"]
+ }
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+}
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log
+concat block_rules: ^a|^b,
+
+
+
+=== TEST 3: multiple rules(include one invalid rule)
+--- 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,
+ [[{
+ "plugins": {
+ "uri-blocker": {
+ "block_rules": ["^a", "^b("]
+ }
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.print(body)
+ }
+}
+--- request
+GET /t
+--- error_code: 400
+--- response_body
+{"error_msg":"failed to check the configuration of plugin uri-blocker err: pcre_compile() failed: missing ) in \"^b(\""}
+--- no_error_log
+[error]
+
+
+
+=== TEST 4: sanity
+--- 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,
+ [[{
+ "plugins": {
+ "uri-blocker": {
+ "block_rules": ["aa"]
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]],
+ [[{
+ "node": {
+ "value": {
+ "plugins": {
+ "uri-blocker": {
+ "block_rules": ["aa"]
+ }
+ }
+ }
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+}
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log
+concat block_rules: aa,
+
+
+
+=== TEST 5: hit block rule
+--- request
+GET /hello?aa=1
+--- error_code: 403
+--- no_error_log
+[error]
+
+
+
+=== TEST 6: miss block rule
+--- request
+GET /hello?bb=2
+--- no_error_log
+[error]
+
+
+
+=== TEST 7: multiple block rules
+--- 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,
+ [[{
+ "plugins": {
+ "uri-blocker": {
+ "block_rules": ["aa", "bb", "c\\d+"]
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+}
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+--- error_log
+concat block_rules: aa|bb|c\d+,
+
+
+
+=== TEST 8: hit block rule
+--- request
+GET /hello?x=bb
+--- error_code: 403
+--- no_error_log
+[error]
+
+
+
+=== TEST 9: hit block rule
+--- request
+GET /hello?bb=2
+--- error_code: 403
+--- no_error_log
+[error]
+
+
+
+=== TEST 10: hit block rule
+--- request
+GET /hello?c1=2
+--- error_code: 403
+--- no_error_log
+[error]
+
+
+
+=== TEST 11: not hit block rule
+--- request
+GET /hello?cc=2
+--- no_error_log
+[error]