3个版本 (重大更新)
0.4.1 | 2021年12月30日 |
---|---|
0.4.0 |
|
0.3.0 | 2021年12月27日 |
0.2.0 | 2021年12月25日 |
0.1.4 |
|
#923 在 HTTP服务器
每月下载量 26次
25KB
419 行
RuES - 表达式评估作为服务
RuES是一个使用JMESPath的最小表达式评估side-car,它可以处理任意JSON。这实际上使它成为一个通用逻辑表达式评估引擎,就像一些用于评估逻辑表达式的Python库。这反过来又允许您实现复杂的规则引擎、RBAC或策略引擎等。
以下是RuES的独特之处
- 轻量级且快速 - 检查以下初始基准。在单个CPU下,只需20MB即可轻松做到10K RPS。
- 无需重启 - 通过在
20 MB
中进行更改,无需重启即可动态添加/删除规则。 - HTTP & JSON - 通用!无需自定义协议,无需麻烦。
- UNIX哲学 - 只评估规则,没有花哨的钩子或集成。非常简单!
为什么?
可能一个很明显的问题就是,为什么选择RuES而不是直接使用库?在以下场景中,RuES可以带来以下好处,使其受益:
- 统一且一致的规则 - 无需处理库之间的差异,特别是在多语言堆栈中,您不必担心任何不一致性、性能问题或库维护。
- 隔离且可扩展 - 虽然嵌入式库可能有更广泛的攻击面,但隔离的过程为您提供了沙箱,由于它是轻量级的,因此作为side-car为您提供亚毫秒级延迟。这不仅允许开发人员将安全责任转交给正确的团队,而且还允许您与系统一起进行扩展。
- 集中管理 - 允许您拥有集中管理的部署和规则。更改规则甚至不需要新的部署。规则是实时重新加载的。这意味着您可以在0停机时间内动态添加/修改规则。
使用方法
在启动 rules
时,请确保您的当前工作目录中存在 rules.hjson
。以下是一个示例规则:
{
example_one: "value == `2`"
example_two: "a.b"
}
每个规则都通过 /eval/{rule_name}
作为 POST
端点公开,您可以将有效负载发布到该端点以评估表达式。简单使用 curl
进行测试。
> curl -X POST http://localhost:8080/eval/example_one -H 'Content-Type: application/json' -d '{"value": 2}'
{"Success":{"expression":"value == `2`","name":"example_one","is_truthy":true,"value":true}}
> curl -X POST http://localhost:8080/eval/example_two -H 'Content-Type: application/json' -d '{"a": {"b": "Hello"}}'
{"Success":{"expression":"a.b","name":"example_two","is_truthy":true,"value":"Hello"}}
如果评估成功,响应对象将包含 Success
。例如:
{
"Success": {
"name": "filter_active",
"expression": "[?isActive] | length(@)",
"is_truthy": true,
"value": 2
}
}
如果表达式中有错误或在评估表达式时存在某些违规行为(在这种情况下,reason
将包含一个原因),则响应将包含一个 Error
。
{
"Error": {
"name": "filter_registered",
"expression": "[?matched('^201\\d', registered)] | length(@)",
"reason": "Runtime error: Call to undefined function matched (line 0, column 9)\n[?matched('^201\\d', registered)] | length(@)\n ^\n"
}
}
如果未找到指定的规则,则响应将包含一个 NotFound
。
{
"NotFound": {
"name": "filter_register"
}
}
批量规则 API
许多时候您需要针对有效负载评估一组规则。RuES 支持使用批量 API 评估上下文与多个规则。给定以下规则文件:
{
example_one: "c == `2`"
example_two: "a.b"
}
可以通过简单调用 /eval
并使用 POST
数据来调用批量 API。
{
"context": {
"c": 3,
"a": {
"b": true
}
},
"rules": ["example_one", "example_two", "example_three"]
}
规则将按它们被传递的顺序顺序评估,服务器将返回一个数组响应。
[
{"Success":{"expression":"c == `2`","name":"example_one","is_truthy":false,"value":false}},
{"Success":{"expression":"a.b","name":"example_two","is_truthy":true,"value":true}},
{"NotFound":{"name":"example_three"}}
]
附加功能
除了 JMES 的内置函数之外,还有以下附加功能
- ✅
string[] match(expref string $regex, string $element)
- 返回所有正则表达式匹配的组数组或如果没有匹配则返回一个null
。正则表达式规范可以在 这里 找到。正则表达式按 LRU 顺序编译和缓存。给定的正则表达式必须是字符串字面量表达式,例如&'\d+'
这是为了确保正则表达式始终是字符串字面量,而不是变量,从而消除通过变量进行正则表达式注入的可能性,防止任何漏洞或正则表达式模式的意外爆炸。示例[?match(&'^[a-z0-9_-]{3,16}$', username)] [?match(&'^[a-z0-9_-]{3,16}$', 'user_123')] [?match(&'([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))', date)]
- ✅
bool valid_email(string $element)
(尚未实现) - 根据电子邮件格式返回true
或false
。除了格式之外,它还排除了临时电子邮件地址。示例[?valid_email('user123@gmail.com')] [?!valid_email('janette@guerrillamailblock.com')] [?valid_email(contact.email)]
- 🚧
number parse_datetime(string $element, string $format = 'rfc3339')
(尚未实现) - 将给定格式的日期时间转换为时间戳。然后可以使用该时间戳进行比较或重新格式化。 - 🚧
string to_datetime(number $element, , string $format = 'rfc3339')
(尚未实现) - 将时间戳转换为指定的字符串格式。 - 🚧
bool in_geo_fence(number[] $center, number $radius, number[] $element)
(尚未实现)- 如果$element
位于$center
的$radius
范围内,则返回true
或false
。 - 🚧
number[][] filter_in_geo_fence(number[] $center, number $radius, number[][] $elements)
(尚未实现)- 返回所有位于给定半径和中心地理围栏内的元素。 - 🚧
bool match_glob(string $pattern, string $element)
(尚未实现)- 如果$element
是$pattern
的 glob 匹配,则返回true
或false
。
配置变量
CONFIG_PATH
- 规则文件路径,文件可以是.json
,.yaml
或.hjson
。默认:rules.hjson
BIND_ADDRESS
- 绑定的服务地址。默认:0.0.0.0:8080
基准测试
我的简要压力测试表明,在一个 CPU 核心下(单个工作进程),3 条规则和 1.6 KB 的有效负载大小。服务器轻松处理了 10K RPS(即使在高负载下),RSS 内存占用仅为 19 MB,p99 为 4ms。
$ cat vegeta_attack.txt | vegeta attack -duration=10s -rate=10000 | vegeta report
Requests [total, rate, throughput] 100000, 10000.20, 9999.80
Duration [total, attack, wait] 10s, 10s, 394.927µs
Latencies [min, mean, 50, 90, 95, 99, max] 107.266µs, 811.954µs, 285.329µs, 2.128ms, 2.654ms, 4.517ms, 12.373ms
Bytes In [total, mean] 9566673, 95.67
Bytes Out [total, mean] 166000000, 1660.00
Success [ratio] 100.00%
Status Codes [code:count] 200:100000
Error Set:
在两个 CPU 核心下(两个工作进程),结果甚至更好
$ cat vegeta_attack.txt | vegeta attack -duration=10s -rate=10000 | vegeta report
Requests [total, rate, throughput] 100000, 10000.30, 10000.08
Duration [total, attack, wait] 10s, 10s, 217.653µs
Latencies [min, mean, 50, 90, 95, 99, max] 111.479µs, 270.125µs, 219.274µs, 413.215µs, 564.181µs, 1.021ms, 8.184ms
Bytes In [total, mean] 9566673, 95.67
Bytes Out [total, mean] 166000000, 1660.00
Success [ratio] 100.00%
Status Codes [code:count] 200:100000
Error Set:
所有规则和数据都已打包在 stress_test
下。欢迎分享您的结果,我将非常乐意将其包含在内。
依赖项
~27–38MB
~669K SLoC