mirror of
https://github.com/alibaba/higress.git
synced 2026-02-25 05:01:19 +08:00
update cpp wasm plugins (#1675)
This commit is contained in:
@@ -1,17 +1,15 @@
|
||||
## 功能说明
|
||||
# 功能说明
|
||||
`model-mapper`插件实现了基于LLM协议中的model参数路由的功能
|
||||
|
||||
## 配置字段
|
||||
# 配置字段
|
||||
|
||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||
| ----------- | --------------- | ----------------------- | ------ | ------------------------------------------- |
|
||||
| `modelKey` | string | 选填 | model | 请求body中model参数的位置 |
|
||||
| `modelMapping` | map of string | 选填 | - | AI 模型映射表,用于将请求中的模型名称映射为服务提供商支持模型名称。<br/>1. 支持前缀匹配。例如用 "gpt-3-*" 匹配所有名称以“gpt-3-”开头的模型;<br/>2. 支持使用 "*" 为键来配置通用兜底映射关系;<br/>3. 如果映射的目标名称为空字符串 "",则表示保留原模型名称。 |
|
||||
| `enableOnPathSuffix` | array of string | 选填 | ["/v1/chat/completions"] | 只对这些特定路径后缀的请求生效 ## 运行属性
|
||||
| `enableOnPathSuffix` | array of string | 选填 | ["/v1/chat/completions"] | 只对这些特定路径后缀的请求生效 |
|
||||
|
||||
|
||||
插件执行阶段:认证阶段
|
||||
插件执行优先级:800
|
||||
|
|
||||
## 效果说明
|
||||
|
||||
如下配置
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## 功能说明
|
||||
# 功能说明
|
||||
`model-router`插件实现了基于LLM协议中的model参数路由的功能
|
||||
|
||||
## 配置字段
|
||||
# 配置字段
|
||||
|
||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||
| ----------- | --------------- | ----------------------- | ------ | ------------------------------------------- |
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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`, 请在参数配置页调高此项。注意调高此参数配置后,网关内存使用将有显著增加。
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "extensions/request_block/plugin.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#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<std::string>(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<std::string>(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<ReMatcher>(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<std::string>(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<std::string>(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, {});
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#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<ReMatcher>;
|
||||
|
||||
struct RequestBlockConfigRule {
|
||||
int blocked_code = 403;
|
||||
std::string blocked_message;
|
||||
bool case_sensitive = true;
|
||||
std::vector<std::string> block_urls;
|
||||
std::vector<std::string> block_exact_urls;
|
||||
std::vector<ReMatcherPtr> block_regexp_urls;
|
||||
std::vector<std::string> block_headers;
|
||||
std::vector<std::string> block_bodys;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user