diff --git a/plugins/wasm-cpp/extensions/model_mapper/README.md b/plugins/wasm-cpp/extensions/model_mapper/README.md
index 1e9fc747f..256b68ac2 100644
--- a/plugins/wasm-cpp/extensions/model_mapper/README.md
+++ b/plugins/wasm-cpp/extensions/model_mapper/README.md
@@ -1,17 +1,15 @@
-## 功能说明
+# 功能说明
`model-mapper`插件实现了基于LLM协议中的model参数路由的功能
-## 配置字段
+# 配置字段
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ----------------------- | ------ | ------------------------------------------- |
| `modelKey` | string | 选填 | model | 请求body中model参数的位置 |
| `modelMapping` | map of string | 选填 | - | AI 模型映射表,用于将请求中的模型名称映射为服务提供商支持模型名称。
1. 支持前缀匹配。例如用 "gpt-3-*" 匹配所有名称以“gpt-3-”开头的模型;
2. 支持使用 "*" 为键来配置通用兜底映射关系;
3. 如果映射的目标名称为空字符串 "",则表示保留原模型名称。 |
-| `enableOnPathSuffix` | array of string | 选填 | ["/v1/chat/completions"] | 只对这些特定路径后缀的请求生效 ## 运行属性
+| `enableOnPathSuffix` | array of string | 选填 | ["/v1/chat/completions"] | 只对这些特定路径后缀的请求生效 |
+
-插件执行阶段:认证阶段
-插件执行优先级:800
- |
## 效果说明
如下配置
diff --git a/plugins/wasm-cpp/extensions/model_router/README.md b/plugins/wasm-cpp/extensions/model_router/README.md
index e78988e0e..7d7d30805 100644
--- a/plugins/wasm-cpp/extensions/model_router/README.md
+++ b/plugins/wasm-cpp/extensions/model_router/README.md
@@ -1,7 +1,7 @@
-## 功能说明
+# 功能说明
`model-router`插件实现了基于LLM协议中的model参数路由的功能
-## 配置字段
+# 配置字段
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ----------------------- | ------ | ------------------------------------------- |
diff --git a/plugins/wasm-cpp/extensions/request_block/BUILD b/plugins/wasm-cpp/extensions/request_block/BUILD
index f1eaa40d0..b8857774e 100644
--- a/plugins/wasm-cpp/extensions/request_block/BUILD
+++ b/plugins/wasm-cpp/extensions/request_block/BUILD
@@ -26,6 +26,7 @@ proxy_wasm_cc_binary(
"@com_google_absl//absl/time",
"//common:json_util",
"//common:http_util",
+ "//common:regex_util",
"//common:rule_util",
],
)
@@ -44,6 +45,7 @@ cc_library(
"//common:json_util",
"@proxy_wasm_cpp_host//:lib",
"//common:http_util_nullvm",
+ "//common:regex_util",
"//common:rule_util_nullvm",
],
)
diff --git a/plugins/wasm-cpp/extensions/request_block/README.md b/plugins/wasm-cpp/extensions/request_block/README.md
index 95c3cdecb..7cd762a3c 100644
--- a/plugins/wasm-cpp/extensions/request_block/README.md
+++ b/plugins/wasm-cpp/extensions/request_block/README.md
@@ -1,31 +1,22 @@
----
-title: 请求屏蔽
-keywords: [higress,request block]
-description: 请求屏蔽插件配置参考
----
-
-## 功能说明
+# 功能说明
`request-block`插件实现了基于 URL、请求头等特征屏蔽 HTTP 请求,可以用于防护部分站点资源不对外部暴露
-## 运行属性
+# 配置字段
-插件执行阶段:`鉴权阶段`
-插件执行优先级:`320`
+| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
+| -------- | -------- | -------- | -------- | -------- |
+| block_urls | array of string | 选填,`block_urls`,`block_exact_urls`,`block_regexp_urls`,`block_headers`,`block_bodies` 中至少必填一项 | - | 配置用于匹配需要屏蔽 URL 的字符串 |
+| block_exact_urls | array of string | 选填,`block_urls`,`block_exact_urls`,`block_regexp_urls`,`block_headers`,`block_bodies` 中至少必填一项 | - | 配置用于匹配需要精确屏蔽 URL 的字符串 |
+| block_regexp_urls | array of string | 选填,`block_urls`,`block_exact_urls`,`block_regexp_urls`,`block_headers`,`block_bodies` 中至少必填一项 | - | 配置用于匹配需要屏蔽 URL 的正则表达式 |
+| block_headers | array of string | 选填,`block_urls`,`block_exact_urls`,`block_regexp_urls`,`block_headers`,`block_bodies` 中至少必填一项 | - | 配置用于匹配需要屏蔽请求 Header 的字符串 |
+| block_bodies | array of string | 选填,`block_urls`,`block_exact_urls`,`block_regexp_urls`,`block_headers`,`block_bodies` 中至少必填一项 | - | 配置用于匹配需要屏蔽请求 Body 的字符串 |
+| blocked_code | number | 选填 | 403 | 配置请求被屏蔽时返回的 HTTP 状态码 |
+| blocked_message | string | 选填 | - | 配置请求被屏蔽时返回的 HTTP 应答 Body |
+| case_sensitive | bool | 选填 | true | 配置匹配时是否区分大小写,默认区分 |
-## 配置字段
+# 配置示例
-| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
-| -------- | -------- | -------- | -------- | -------- |
-| block_urls | array of string | 选填,`block_urls`,`block_headers`,`block_bodies` 中至少必填一项 | - | 配置用于匹配需要屏蔽 URL 的字符串 |
-| block_headers | array of string | 选填,`block_urls`,`block_headers`,`block_bodies` 中至少必填一项 | - | 配置用于匹配需要屏蔽请求 Header 的字符串 |
-| block_bodies | array of string | 选填,`block_urls`,`block_headers`,`block_bodies` 中至少必填一项 | - | 配置用于匹配需要屏蔽请求 Body 的字符串 |
-| blocked_code | number | 选填 | 403 | 配置请求被屏蔽时返回的 HTTP 状态码 |
-| blocked_message | string | 选填 | - | 配置请求被屏蔽时返回的 HTTP 应答 Body |
-| case_sensitive | bool | 选填 | true | 配置匹配时是否区分大小写,默认区分 |
-
-## 配置示例
-
-### 屏蔽请求 url 路径
+## 屏蔽请求 url 路径
```yaml
block_urls:
- swagger.html
@@ -40,7 +31,36 @@ curl http://example.com?foo=Bar
curl http://exmaple.com/Swagger.html
```
-### 屏蔽请求 header
+## 屏蔽精确匹配的请求 url 路径
+
+```yaml
+block_exact_urls:
+- /swagger.html?foo=bar
+case_sensitive: false
+```
+
+根据该配置,下列请求将被禁止访问:
+
+```bash
+curl http://exmaple.com/Swagger.html?foo=Bar
+```
+
+## 屏蔽正则匹配的请求 url 路径
+
+```yaml
+block_exact_urls:
+- .*swagger.*
+case_sensitive: false
+```
+
+根据该配置,下列请求将被禁止访问:
+
+```bash
+curl http://exmaple.com/Swagger.html?foo=Bar
+```
+
+
+## 屏蔽请求 header
```yaml
block_headers:
- example-key
@@ -54,9 +74,9 @@ curl http://example.com -H 'example-key: 123'
curl http://exmaple.com -H 'my-header: example-value'
```
-### 屏蔽请求 body
+## 屏蔽请求 body
```yaml
-block_bodies:
+block_bodys:
- "hello world"
case_sensitive: false
```
@@ -68,8 +88,30 @@ curl http://example.com -d 'Hello World'
curl http://exmaple.com -d 'hello world'
```
+## 对特定路由或域名开启
+```yaml
+# 使用 _rules_ 字段进行细粒度规则配置
+_rules_:
+# 规则一:按路由名称匹配生效
+- _match_route_:
+ - route-a
+ - route-b
+ block_bodys:
+ - "hello world"
+# 规则二:按域名匹配生效
+- _match_domain_:
+ - "*.example.com"
+ - test.com
+ block_urls:
+ - "swagger.html"
+ block_bodys:
+ - "hello world"
+```
+此例 `_match_route_` 中指定的 `route-a` 和 `route-b` 即在创建网关路由时填写的路由名称,当匹配到这两个路由时,将使用此段配置;
+此例 `_match_domain_` 中指定的 `*.example.com` 和 `test.com` 用于匹配请求的域名,当发现域名匹配时,将使用此段配置;
+配置的匹配生效顺序,将按照 `_rules_` 下规则的排列顺序,匹配第一个规则后生效对应配置,后续规则将被忽略。
-## 请求 Body 大小限制
+# 请求 Body 大小限制
-当配置了 `block_bodies` 时,仅支持小于 32 MB 的请求 Body 进行匹配。若请求 Body 大于此限制,并且不存在匹配到的 `block_urls` 和 `block_headers` 项时,不会对该请求执行屏蔽操作
-当配置了 `block_bodies` 时,若请求 Body 超过全局配置 DownstreamConnectionBufferLimits,将返回 `413 Payload Too Large`
+当配置了 `block_bodys` 时,仅支持小于 32 MB 的请求 Body 进行匹配。若请求 Body 大于此限制,并且不存在匹配到的 `block_urls` 和 `block_headers` 项时,不会对该请求执行屏蔽操作
+当配置了 `block_bodys` 时,若请求 Body 超过全局配置 DownstreamConnectionBufferLimits,将返回 `413 Payload Too Large`, 请在参数配置页调高此项。注意调高此参数配置后,网关内存使用将有显著增加。
diff --git a/plugins/wasm-cpp/extensions/request_block/plugin.cc b/plugins/wasm-cpp/extensions/request_block/plugin.cc
index 90c6c92f7..ce6492379 100644
--- a/plugins/wasm-cpp/extensions/request_block/plugin.cc
+++ b/plugins/wasm-cpp/extensions/request_block/plugin.cc
@@ -15,6 +15,7 @@
#include "extensions/request_block/plugin.h"
#include
+#include
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
@@ -89,6 +90,48 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
LOG_WARN("failed to parse configuration for block_urls.");
return false;
}
+ if (!JsonArrayIterate(
+ configuration, "block_exact_urls", [&](const json& item) -> bool {
+ auto url = JsonValueAs(item);
+ if (url.second != Wasm::Common::JsonParserResultDetail::OK) {
+ LOG_WARN("cannot parse block_exact_urls");
+ return false;
+ }
+ if (rule.case_sensitive) {
+ rule.block_exact_urls.push_back(std::move(url.first.value()));
+ } else {
+ rule.block_exact_urls.push_back(
+ absl::AsciiStrToLower(url.first.value()));
+ }
+ return true;
+ })) {
+ LOG_WARN("failed to parse configuration for block_exact_urls.");
+ return false;
+ }
+ if (!JsonArrayIterate(
+ configuration, "block_regexp_urls", [&](const json& item) -> bool {
+ auto url = JsonValueAs(item);
+ if (url.second != Wasm::Common::JsonParserResultDetail::OK) {
+ LOG_WARN("cannot parse block_regexp_urls");
+ return false;
+ }
+ std::string regex;
+ if (rule.case_sensitive) {
+ regex = url.first.value();
+ } else {
+ regex = absl::AsciiStrToLower(url.first.value());
+ }
+ auto re = std::make_unique(regex);
+ if (!re->error().empty()) {
+ LOG_WARN(re->error());
+ return false;
+ }
+ rule.block_regexp_urls.push_back(std::move(re));
+ return true;
+ })) {
+ LOG_WARN("failed to parse configuration for block_regexp_urls.");
+ return false;
+ }
if (!JsonArrayIterate(
configuration, "block_headers", [&](const json& item) -> bool {
auto header = JsonValueAs(item);
@@ -125,8 +168,28 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
LOG_WARN("failed to parse configuration for block_bodys.");
return false;
}
+ // compatiable
+ if (!JsonArrayIterate(
+ configuration, "block_bodies", [&](const json& item) -> bool {
+ auto body = JsonValueAs(item);
+ if (body.second != Wasm::Common::JsonParserResultDetail::OK) {
+ LOG_WARN("cannot parse block_bodies");
+ return false;
+ }
+ if (rule.case_sensitive) {
+ rule.block_bodys.push_back(std::move(body.first.value()));
+ } else {
+ rule.block_bodys.push_back(
+ absl::AsciiStrToLower(body.first.value()));
+ }
+ return true;
+ })) {
+ LOG_WARN("failed to parse configuration for block_bodies.");
+ return false;
+ }
if (rule.block_bodys.empty() && rule.block_headers.empty() &&
- rule.block_urls.empty()) {
+ rule.block_urls.empty() && rule.block_exact_urls.empty() &&
+ rule.block_regexp_urls.empty()) {
LOG_WARN("there is no block rules");
return false;
}
@@ -172,6 +235,18 @@ bool PluginRootContext::checkHeader(const RequestBlockConfigRule& rule,
urlstr = absl::AsciiStrToLower(request_url);
url = urlstr;
}
+ for (const auto& block_url : rule.block_exact_urls) {
+ if (url == block_url) {
+ sendLocalResponse(rule.blocked_code, "", rule.blocked_message, {});
+ return false;
+ }
+ }
+ for (const auto& block_url : rule.block_regexp_urls) {
+ if (block_url->match(url)) {
+ sendLocalResponse(rule.blocked_code, "", rule.blocked_message, {});
+ return false;
+ }
+ }
for (const auto& block_url : rule.block_urls) {
if (absl::StrContains(url, block_url)) {
sendLocalResponse(rule.blocked_code, "", rule.blocked_message, {});
diff --git a/plugins/wasm-cpp/extensions/request_block/plugin.h b/plugins/wasm-cpp/extensions/request_block/plugin.h
index 4c9288f2a..e9c6ce393 100644
--- a/plugins/wasm-cpp/extensions/request_block/plugin.h
+++ b/plugins/wasm-cpp/extensions/request_block/plugin.h
@@ -22,6 +22,7 @@
#include
#include "common/http_util.h"
+#include "common/regex.h"
#include "common/route_rule_matcher.h"
#define ASSERT(_X) assert(_X)
@@ -39,11 +40,16 @@ namespace request_block {
#endif
+using ReMatcher = Wasm::Common::Regex::CompiledGoogleReMatcher;
+using ReMatcherPtr = std::unique_ptr;
+
struct RequestBlockConfigRule {
int blocked_code = 403;
std::string blocked_message;
bool case_sensitive = true;
std::vector block_urls;
+ std::vector block_exact_urls;
+ std::vector block_regexp_urls;
std::vector block_headers;
std::vector block_bodys;
};
diff --git a/plugins/wasm-cpp/extensions/request_block/plugin_test.cc b/plugins/wasm-cpp/extensions/request_block/plugin_test.cc
index 40cc32711..15b7d9475 100644
--- a/plugins/wasm-cpp/extensions/request_block/plugin_test.cc
+++ b/plugins/wasm-cpp/extensions/request_block/plugin_test.cc
@@ -127,6 +127,8 @@ TEST_F(RequestBlockTest, CaseSensitive) {
std::string configuration = R"(
{
"block_urls": ["?foo=bar", "swagger.html"],
+ "block_exact_urls": ["/hello.html?abc=123"],
+ "block_regexp_urls": [".*monkey.*"],
"block_headers": ["headerKey", "headerValue"],
"block_bodys": ["Hello World"]
})";
@@ -150,6 +152,22 @@ TEST_F(RequestBlockTest, CaseSensitive) {
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
+ path_ = "/hello.html?abc=123";
+ EXPECT_CALL(*mock_context_, sendLocalResponse(403, testing::_, testing::_,
+ testing::_, testing::_));
+ EXPECT_EQ(context_->onRequestHeaders(0, false),
+ FilterHeadersStatus::StopIteration);
+
+ path_ = "/black/Monkey";
+ EXPECT_EQ(context_->onRequestHeaders(0, false),
+ FilterHeadersStatus::Continue);
+
+ path_ = "/black/monkey";
+ EXPECT_CALL(*mock_context_, sendLocalResponse(403, testing::_, testing::_,
+ testing::_, testing::_));
+ EXPECT_EQ(context_->onRequestHeaders(0, false),
+ FilterHeadersStatus::StopIteration);
+
path_ = "";
headers_ = {{"headerKey", "123"}};
EXPECT_CALL(*mock_context_, sendLocalResponse(403, testing::_, testing::_,
@@ -188,6 +206,8 @@ TEST_F(RequestBlockTest, CaseInsensitive) {
"blocked_code": 404,
"block_urls": ["?foo=bar", "swagger.html"],
"block_headers": ["headerKey", "headerValue"],
+ "block_exact_urls": ["/hello.html?abc=123"],
+ "block_regexp_urls": [".*monkey.*"],
"block_bodys": ["Hello World"]
})";
@@ -206,6 +226,24 @@ TEST_F(RequestBlockTest, CaseInsensitive) {
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
+ path_ = "/Hello.html?abc=123";
+ EXPECT_CALL(*mock_context_, sendLocalResponse(404, testing::_, testing::_,
+ testing::_, testing::_));
+ EXPECT_EQ(context_->onRequestHeaders(0, false),
+ FilterHeadersStatus::StopIteration);
+
+ path_ = "/black/Monkey";
+ EXPECT_CALL(*mock_context_, sendLocalResponse(404, testing::_, testing::_,
+ testing::_, testing::_));
+ EXPECT_EQ(context_->onRequestHeaders(0, false),
+ FilterHeadersStatus::StopIteration);
+
+ path_ = "/black/monkey";
+ EXPECT_CALL(*mock_context_, sendLocalResponse(404, testing::_, testing::_,
+ testing::_, testing::_));
+ EXPECT_EQ(context_->onRequestHeaders(0, false),
+ FilterHeadersStatus::StopIteration);
+
path_ = "";
headers_ = {{"headerkey", "123"}};
EXPECT_CALL(*mock_context_, sendLocalResponse(404, testing::_, testing::_,
@@ -232,6 +270,26 @@ TEST_F(RequestBlockTest, CaseInsensitive) {
FilterDataStatus::StopIterationNoBuffer);
}
+TEST_F(RequestBlockTest, Bodies) {
+ std::string configuration = R"(
+{
+ "case_sensitive": false,
+ "blocked_code": 404,
+ "block_bodies": ["Hello World"]
+})";
+
+ config_.set({configuration.data(), configuration.size()});
+ EXPECT_TRUE(root_context_->configure(configuration.size()));
+
+ body_.set("hello world");
+ EXPECT_EQ(context_->onRequestHeaders(0, false),
+ FilterHeadersStatus::Continue);
+ EXPECT_CALL(*mock_context_, sendLocalResponse(404, testing::_, testing::_,
+ testing::_, testing::_));
+ EXPECT_EQ(context_->onRequestBody(11, true),
+ FilterDataStatus::StopIterationNoBuffer);
+}
+
} // namespace request_block
} // namespace null_plugin
} // namespace proxy_wasm