feat: add TopSec WAF IP blacklist and URL block service#191
feat: add TopSec WAF IP blacklist and URL block service#191Erosion2020 wants to merge 4 commits into
Conversation
- 7 RPC methods: AddBlacklistIP, DeleteBlacklistIP, ListBlacklistIPs, AddUrlBlock, DeleteUrlBlock, ListUrlBlocks, SetUrlBlockStatus - Session-based auth with AES-128-CBC encrypted login - Error mapping: 401/403→PERMISSION_DENIED, 4xx→FAILED_PRECONDITION, 5xx→UNAVAILABLE, param errors→INVALID_ARGUMENT - 52 unit tests with mock upstream, validate/pack:check passed - Monorepo integration: bin wrapper, tentacles registration Co-Authored-By: Claude <noreply@anthropic.com>
…istency - session: auto re-login on WAF 401/403 with single retry - timeout: AbortSignal.timeout() for fetch, returns DEADLINE_EXCEEDED - pagination: page/rows support in ListBlacklistIPs and ListUrlBlocks - error codes: 401 login -> UNAUTHENTICATED, 403 -> PERMISSION_DENIED - validation: action_data required for temp-redirect/perm-redirect - token extraction: robust regex for multiple response formats - tests: 52 -> 59, added session retry, timeout, pagination, action_data tests - docs: sync README with current code (paths, error table, file list, limitations) Co-Authored-By: Claude <noreply@anthropic.com>
真实设备 HTTP 请求/响应验证记录设备: TopSec WAF v3.2294.20238_waf 1. 安全策略列表(通过 POST /api/v1/security_policy_show 获取){
"token": "<token>",
"commands": [{"waf_security_policy_show": {}}]
}Response: {
"rows": [
{"ID": "8015", "name": "safety_priority", "protect_level": "high", "template": "true"},
{"ID": "8016", "name": "standard_security", "protect_level": "middle", "template": "true"},
{"ID": "8017", "name": "application_priority", "protect_level": "low", "template": "true"},
{"ID": "33804", "name": "test-acl-policy", "protect_level": "low", "template": "true"},
...
],
"total": "7"
}
2. 认证流程2.1 GET_MIKS — 获取加密密钥Request: POST /api/v1/get_miks HTTP/1.1
Host: <WAF_HOST>:8443
Content-Type: application/x-www-form-urlencodedResponse: HTTP/1.1 200 OK
Set-Cookie: PHPSESSID=<session-id>; path=/
Content-Type: text/html;charset=utf-8
<key-base64>
2.2 LOGIN — 登录获取 Token密码加密方式:使用 get_miks 返回的 key 作为 AES-128-CBC 的 key 和 iv,对原始密码做 zero-padding 后加密。 Request: POST /api/v1/login HTTP/1.1
Host: <WAF_HOST>:8443
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=<session-id>
name=<username>&password=<AES-128-CBC-encrypted-base64>Response (成功): HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
?[<token-base64>}?
Response (认证失败): HTTP/1.1 401 Unauthorized
Incorrect username or password
3. IP 黑名单 API (已验证通过 ✅)所有业务 API 使用 JSON body 格式: 3.1 ip_group_add — 添加 IP 黑名单WAF Request: {
"token": "<token>",
"commands": [{"waf_ip_group_add": {"name": "octobus-test-group", "address": "10.255.255.1,black"}}]
}
WAF Response: {"result": "success", "info": "Configuration succeeded"}gRPC (Connect RPC) 调用: curl -X POST \
http://127.0.0.1:19002/capsets/sa/connect/test-waf/TopSec_WAF.TopSec_WAF/AddBlacklistIP \
-H 'Content-Type: application/json' \
-d '{"name":"octobus-test-group","ip_addresses":["10.255.255.1,black"]}'gRPC Response: 3.2 ip_group_show — 查询 IP 黑名单WAF Request (全部列出): {"token": "<token>", "commands": [{"waf_ip_group_show": {}}]}WAF Request (按名称过滤): {"token": "<token>", "commands": [{"waf_ip_group_show": {"name": "octobus-test-group"}}]}WAF Response: {
"rows": [{
"name": "octobus-test-group",
"group_value": "10.255.255.1,black",
"ip_group_members": "10.255.255.1,32,black",
"m_type": "__"
}],
"total": "1"
}gRPC 调用: curl -X POST \
http://127.0.0.1:19002/capsets/sa/connect/test-waf/TopSec_WAF.TopSec_WAF/ListBlacklistIPs \
-H 'Content-Type: application/json' \
-d '{"name":"octobus-test-group"}'gRPC Response: 3.3 ip_group_delete — 删除 IP 黑名单WAF Request: {"token": "<token>", "commands": [{"waf_ip_group_delete": {"name": "octobus-test-group"}}]}WAF Response (成功): {"result": "success", "info": "Configuration succeeded"}WAF Response (不存在): {"result": "failed", "info": "group not found"}
gRPC 调用: curl -X POST \
http://127.0.0.1:19002/capsets/sa/connect/test-waf/TopSec_WAF.TopSec_WAF/DeleteBlacklistIP \
-H 'Content-Type: application/json' \
-d '{"name":"octobus-test-group"}'gRPC Response: 4. URL 拦截规则 API (已验证通过 ✅)4.1 7 种 Action 类型全部测试通过
4.2 user_policy_add — 添加 URL 拦截规则WAF Request: {
"token": "<token>",
"commands": [{
"waf_user_policy_ui_add": {
"security-policy": "test-acl-policy",
"name": "octobus-test-deny",
"enable": "on",
"phase": "request_header",
"action": "deny",
"log-message": "block: /test/deny.php",
"condition": "<base64-encoded>"
}
}]
}
WAF Response (成功): {"result": "success", "info": "Configuration succeeded"}gRPC 调用 (7 种 action): # deny
curl -X POST .../AddUrlBlock -H 'Content-Type: application/json' \
-d '{"security_policy":"test-acl-policy","name":"octobus-test-deny","url":"/test/deny.php","action":"deny"}'
# allow
curl -X POST .../AddUrlBlock -H 'Content-Type: application/json' \
-d '{"security_policy":"test-acl-policy","name":"octobus-test-allow","url":"/test/allow.php","action":"allow"}'
# alert
curl -X POST .../AddUrlBlock -H 'Content-Type: application/json' \
-d '{"security_policy":"test-acl-policy","name":"octobus-test-alert","url":"/test/alert.php","action":"alert"}'
# continue
curl -X POST .../AddUrlBlock -H 'Content-Type: application/json' \
-d '{"security_policy":"test-acl-policy","name":"octobus-test-continue","url":"/test/continue.php","action":"continue"}'
# deny-nlog
curl -X POST .../AddUrlBlock -H 'Content-Type: application/json' \
-d '{"security_policy":"test-acl-policy","name":"octobus-test-denynlog","url":"/test/denynlog.php","action":"deny-nlog"}'
# temp-redirect (需 action_data)
curl -X POST .../AddUrlBlock -H 'Content-Type: application/json' \
-d '{"security_policy":"test-acl-policy","name":"octobus-test-redirect","url":"/test/redirect.php","action":"temp-redirect","action_data":"https://www.baidu.com"}'
# perm-redirect (需 action_data)
curl -X POST .../AddUrlBlock -H 'Content-Type: application/json' \
-d '{"security_policy":"test-acl-policy","name":"octobus-test-permredirect","url":"/test/permredirect.php","action":"perm-redirect","action_data":"https://www.baidu.com"}'全部 7 种 gRPC Response: 4.3 user_policy_show — 查询 URL 拦截规则WAF Request: {
"token": "<token>",
"commands": [{"waf_url_rewrite_show_name": {"security-policy": "test-acl-policy"}}]
}
WAF Response: {
"rows": [{
"id": "33826",
"name": "octobus-test-deny",
"action": "deny",
"enable": "on",
"phase": "request_header",
"log_message": "block: /test/deny.php",
"conditions": "<base64-encoded XML>"
}],
"total": "1"
}gRPC 调用: curl -X POST .../ListUrlBlocks -H 'Content-Type: application/json' \
-d '{"security_policy":"test-acl-policy","name":"octobus-test-deny"}'gRPC Response: 4.4 user_policy_modify — 启用/禁用 URL 拦截规则WAF Request: {
"token": "<token>",
"commands": [{
"waf_user_policy_modify_ui": {
"security-policy": "test-acl-policy",
"name": "octobus-test-deny",
"enable": "off"
}
}]
}WAF Response: {"result": "success", "info": "Configuration succeeded"}gRPC 调用 (禁用): curl -X POST .../SetUrlBlockStatus -H 'Content-Type: application/json' \
-d '{"security_policy":"test-acl-policy","name":"octobus-test-deny","enable":"off"}'gRPC Response:
gRPC 调用 (重新启用): curl -X POST .../SetUrlBlockStatus -H 'Content-Type: application/json' \
-d '{"security_policy":"test-acl-policy","name":"octobus-test-deny","enable":"on"}'gRPC Response: 4.5 user_policy_delete — 删除 URL 拦截规则WAF Request: {
"token": "<token>",
"commands": [{
"waf_user_policy_delete": {
"security-policy": "test-acl-policy",
"name": "octobus-test-deny"
}
}]
}WAF Response: {"result": "success", "info": "Configuration succeeded"}gRPC 调用 (批量清理 7 条): for NAME in octobus-test-deny octobus-test-allow octobus-test-alert \
octobus-test-continue octobus-test-denynlog octobus-test-redirect octobus-test-permredirect; do
curl -X POST .../DeleteUrlBlock -H 'Content-Type: application/json' \
-d "{\"security_policy\":\"test-acl-policy\",\"name\":\"$NAME\"}"
done全部 gRPC Response: 5. 错误码映射(真实设备验证)
6. 完整验证结果汇总
7. OctoBus 完整启动命令# 启动 daemon
npx @chaitin-ai/octobus serve --data-dir /tmp/obus-data --addr 127.0.0.1:19002
# 导入 service
npx @chaitin-ai/octobus service import topsec-waf ./services/topsec__waf_v3-2294-20238
# 创建 instance
npx @chaitin-ai/octobus instance create test-waf \
--service topsec-waf \
--config-json '{"host":"https://<WAF_HOST>:8443","skipTlsVerify":true,"timeoutMs":10000}' \
--secret-json '{"username":"<username>","password":"<password>"}'
# 创建 capset 并绑定
npx @chaitin-ai/octobus capset create sa
npx @chaitin-ai/octobus capset add-instance sa test-waf8. Session 自动重试机制当 WAF API 返回 401/403(session 过期)时,服务会:
验证结果: 真实设备测试中全程无认证过期错误,session 复用正常。✅ 9. 请求超时(config.timeoutMs)通过 正常响应时不触发。超时时返回 {"code":"deadline_exceeded","message":"DEADLINE_EXCEEDED: WAF request timeout after 10000ms"}验证结果: 设置 10. 分页支持
WAF 层: {
"token": "<token>",
"commands": [{"waf_ip_group_show": {}}],
"page": 1,
"rows": 5
}gRPC 调用: curl -X POST .../ListBlacklistIPs -H 'Content-Type: application/json' \
-d '{"page":1,"rows":5}'验证结果: 真实设备 11. Redirect Action 的 action_data 强制校验
缺少 action_data 时的响应: {
"code": "invalid_argument",
"message": "INVALID_ARGUMENT: action_data required for temp-redirect and perm-redirect (redirect target URL)"
}提供 action_data 时正常创建: {"result":"success", "info":"Configuration succeeded"}验证结果: 无 action_data 正确拦截,带 action_data 正确创建。✅ 12. 登录错误码分离
验证结果: 真实设备使用错误密码返回 401,正确映射为 |
接入设备
v1(RESTful API)实现方法
本 service package 封装了 TopSec WAF 的 RESTful API,提供 IP 黑名单管理和 URL 自定义策略(ACL)操作,共 7 个 gRPC 方法:
IP 黑名单管理(3 个方法):
AddBlacklistIPDeleteBlacklistIPListBlacklistIPsURL 拦截规则(4 个方法):
AddUrlBlockDeleteUrlBlockListUrlBlocksSetUrlBlockStatus技术实现要点:
@chaitin-ai/octobus-sdk的defineService封装,运行时模式:long-runningAbortSignal.timeout()实现 HTTP 请求超时(config.timeoutMs),超时返回DEADLINE_EXCEEDEDListBlacklistIPs和ListUrlBlocks支持page/rows分页参数,映射到 WAF API 顶层字段INVALID_ARGUMENT;temp-redirect/perm-redirect缺少action_data时提前返回INVALID_ARGUMENT(无需等到 WAF 返回 400)UNAUTHENTICATED、403→PERMISSION_DENIED;网络/5xx→UNAVAILABLE;超时→DEADLINE_EXCEEDED;业务失败→FAILED_PRECONDITIONskipTlsVerify)用于自签证书测试环境写操作说明
每个写操作均说明了默认参数、幂等语义、回滚方式和审计字段,详见 README。
错误码映射
INVALID_ARGUMENTaction_data(redirect)INVALID_ARGUMENTUNAUTHENTICATEDPERMISSION_DENIEDPERMISSION_DENIEDFAILED_PRECONDITIONresult: failedFAILED_PRECONDITIONDEADLINE_EXCEEDEDUNAVAILABLEUNAVAILABLE/UNKNOWN测试命令
测试结果
覆盖场景:
grpcCodeFor、grpcErr、readConfig、buildCondition、toNum)已知限制
contains操作符m_type字段在 WAF 返回中始终为"__",未能区分 black/white 类型waf_url_rewrite_show_name接口(非user_policy_show),接口名称与功能不完全匹配(为 TopSec WAF 实际 API 行为)🤖 Generated with Claude Code