Skip to content

feat(services): add CTDSG FW service package#432

Open
NingLinj wants to merge 4 commits into
chaitin:mainfrom
NingLinj:feat/ctdsg-fw-423
Open

feat(services): add CTDSG FW service package#432
NingLinj wants to merge 4 commits into
chaitin:mainfrom
NingLinj:feat/ctdsg-fw-423

Conversation

@NingLinj

@NingLinj NingLinj commented Jun 30, 2026

Copy link
Copy Markdown

Closes #423

变更概述

  • 新增 ctdsg-fw OctoBus service package,用于对接长亭 CTDSG 黑名单能力。
  • 支持以下 4 个一元 RPC:
    • BlockIP
    • UnblockIP
    • BlockDomain
    • UnblockDomain
  • 将该 service 注册到 OctoBus tentacles package 中。
  • 在适配器层完成自签名证书兼容、签名认证对齐及 CTDSG 特定请求格式适配。

接入设备信息

  • 厂商:长亭(Chaitin)
  • 产品:CTDSG
  • 已验证地址:https://10.211.194.22:9090
  • 认证方式:appId + secretKey,HMAC-MD5 请求签名
  • 运行模式:常驻运行(long-running)
  • 验证方式:通过 OctoBus 网关暴露的 Connect RPC 调用适配器

已实现方法

  • BlockIP:封禁 IP
  • UnblockIP:解封 IP
  • BlockDomain:封禁域名
  • UnblockDomain:解封域名

关键实现说明

  • 自签名证书兼容逻辑在 CTDSG adapter 内部处理,未修改 OctoBus 网关通用逻辑。
  • 签名认证与请求格式对齐现网参考实现:
    • /Users/admin/Desktop/tools/mssblockingcomponent/Chaitin_FW_CTDSG_8815E_HTTP/chaitinfw.py
  • 关键对齐点包括:
    • 请求头使用:
      • hy-bz-api-app-id
      • hy-bz-api-timestamp
      • hy-bz-api-signature
    • URL 查询参数使用 opt
    • BlockIP / BlockDomain 使用 addPatchblack2
    • UnblockIP / UnblockDomain 使用 delblack2
    • 多值字段使用换行拼接
    • typetimepunish_timetime_unitaddr_type 按字符串发送

本地验证

cd services
npm run validate -- --service-dir ctdsg__fw
npm test -- --service-dir ctdsg__fw
npm run pack:check

验证结果:

  • validate-service-package 通过
  • node:test 聚焦测试通过
  • npm pack --dry-run 通过

网关接入验证

./bin/octobus serve
./bin/octobus service import ctdsg-fw ./services//ctdsg__fw
./bin/octobus instance create ctdsg-fw-test \
  --service ctdsg-fw \
  --config-json '{"host":"https://10.211.194.22:9090","appId":"h*****","skipTlsVerify":true}' \
  --secret-json '{"secretKey":"REDACTED"}'
./bin/octobus capset create dev --name DevAgent
./bin/octobus capset add-instance dev ctdsg-fw-test
./bin/octobus catalog dev --all --json

已通过 OctoBus Connect RPC 网关 验证以下能力:

  • BlockIP
  • UnblockIP
  • BlockDomain
  • UnblockDomain

真实设备联调验证

本次联调验证的是 serveservice importinstancecapsetConnect RPC 的完整链路,验证结果为 OctoBus 适配器集成链路成功,不是直接绕过 OctoBus 访问设备。

联调证据一:BlockIP 跑通

Request

POST http://127.0.0.1:9000/capsets/dev/connect/ctdsg-fw-test/CTDSG_FW.CTDSG_FW/BlockIP
Content-Type: application/json

{"ips":["43.143.110.163"],"permanent":false,"punishTime":1,"timeUnit":1}

Response

HTTP/1.1 200 OK

{
  "statusCode": 200,
  "rawBody": "{\"code\":0,\"msg\":\"成功\"}",
  "bodyJson": {
    "code": 0,
    "msg": "成功"
  },
  "effectiveUrl": "https://10.211.194.22:9090/api.php/inter/Inter?opt=addPatchblack2"
}

联调证据二:UnblockIP 跑通

Request

POST http://127.0.0.1:9000/capsets/dev/connect/ctdsg-fw-test/CTDSG_FW.CTDSG_FW/UnblockIP
Content-Type: application/json

{"ips":["43.143.110.163"]}

Response

HTTP/1.1 200 OK

{
  "statusCode": 200,
  "rawBody": "{\"code\":0,\"msg\":\"成功\"}",
  "bodyJson": {
    "code": 0,
    "msg": "成功"
  },
  "effectiveUrl": "https://10.211.194.22:9090/api.php/inter/Inter?opt=delblack2"
}

联调证据三:BlockDomain 跑通

Request

POST http://127.0.0.1:9000/capsets/dev/connect/ctdsg-fw-test/CTDSG_FW.CTDSG_FW/BlockDomain
Content-Type: application/json

{"domains":["fget-career.com"],"permanent":false,"punishTime":1,"timeUnit":1}

Response

HTTP/1.1 200 OK

{
  "statusCode": 200,
  "rawBody": "{\"code\":0,\"msg\":\"成功\"}",
  "bodyJson": {
    "code": 0,
    "msg": "成功"
  },
  "effectiveUrl": "https://10.211.194.22:9090/api.php/inter/Inter?opt=addPatchblack2"
}

联调证据四:UnblockDomain 跑通

Request

POST http://127.0.0.1:9000/capsets/dev/connect/ctdsg-fw-test/CTDSG_FW.CTDSG_FW/UnblockDomain
Content-Type: application/json

{"domains":["fget-career.com"]}

Response

HTTP/1.1 200 OK

{
  "statusCode": 200,
  "rawBody": "{\"code\":0,\"msg\":\"成功\"}",
  "bodyJson": {
    "code": 0,
    "msg": "成功"
  },
  "effectiveUrl": "https://10.211.194.22:9090/api.php/inter/Inter?opt=delblack2"
}

联调验证截图

截图一:BlockIP 成功

curl -X POST \
  http://127.0.0.1:9000/capsets/dev/connect/ctdsg-fw-test/CTDSG_FW.CTDSG_FW/BlockIP \
  -H 'Content-Type: application/json' \
  -d '{"ips":["43.143.110.163"],"permanent":false,"punishTime":1,"timeUnit":1}'
Image

截图二:UnblockIP 成功

curl -X POST \
  http://127.0.0.1:9000/capsets/dev/connect/ctdsg-fw-test/CTDSG_FW.CTDSG_FW/UnblockIP \
  -H 'Content-Type: application/json' \
  -d '{"ips":["43.143.110.163"]}'
Image

截图三:BlockDomain 成功

curl -X POST \
  http://127.0.0.1:9000/capsets/dev/connect/ctdsg-fw-test/CTDSG_FW.CTDSG_FW/BlockDomain \
  -H 'Content-Type: application/json' \
  -d '{"domains":["fget-career.com"],"permanent":false,"punishTime":1,"timeUnit":1}'
Image

截图四:UnblockDomain 成功

curl -X POST \
  http://127.0.0.1:9000/capsets/dev/connect/ctdsg-fw-test/CTDSG_FW.CTDSG_FW/UnblockDomain \
  -H 'Content-Type: application/json' \
  -d '{"domains":["fget-career.com"]}'
Image

安全说明

  • 代码、测试和文档中未提交真实 secretKey、密码、Token 或 Authorization 请求头。
  • 联调证据中保留了完整请求路径、状态码和响应结构,仅对敏感凭据做脱敏。
  • 自签名证书兼容通过 adapter 内部的 skipTlsVerify 开关控制,不扩散到 OctoBus 网关全局。
  • 当前联调使用的是经授权的测试环境与测试对象,不包含生产环境凭据。

已知限制

  • skipTlsVerify 适用于大量自签名证书设备场景,生产环境如有可信 CA,建议优先走正常证书链路。
  • 目前联调证据以 Connect RPC 为主,MCP 调用方式已在 README 中补充,如有需要可继续追加 MCP 实测证据。

@monkeyscan

monkeyscan Bot commented Jun 30, 2026

Copy link
Copy Markdown

PR Title: feat(services): add CTDSG FW service package

Commit: d7d7018

本次 PR 新增了一个 CTDSG FW 的 OctoBus 服务包,用于通过 CTDSG 设备的黑名单 API 对 IP 和域名进行封禁/解封。核心变更包括:

  1. 新增了服务配置(config.schema.json、secret.schema.json、service.json、package.json)和 protobuf 定义(ctdsg_fw.proto)。
  2. 实现了主逻辑 ctdsg-fw.js,包含请求参数解析、CTDSG HMAC-MD5 签名、HTTP 请求构造与响应标准化。
  3. 提供了 CLI 入口(bin/ctdsg-fw.js)以及根服务注册(services/bin/ctdsg-fw.js、octobus-tentacles.js)。
  4. 编写了较完整的单元测试和 Mock 上游服务器。

整体代码结构清晰,测试覆盖了主要功能路径。但在审阅中发现两个高置信度的可靠性问题:withTlsBypass 存在全局环境变量竞争条件,以及 fetch 调用使用了不被 Node.js 原生 fetch 支持的 timeoutMs 选项导致超时失效。

Comment thread services/ctdsg__fw/src/ctdsg-fw.js

const extractHeaders = (res) => {
const map = new Map();
const headers = res?.headers;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetch 调用传入无效的 timeoutMs 选项导致请求超时失效

fetchHttp 向原生 fetch 传入了 timeoutMs: resolveTimeoutMs(ctx) 选项。Node.js 内置的 fetch(undici)并不识别 timeoutMs,该选项会被静默忽略。若上游 CTDSG 设备无响应,请求将一直挂起直至操作系统 TCP 超时(通常数分钟),而非代码预期的 5 秒(或配置值)。测试中对 captured.init.timeoutMs 的断言仅能验证选项被传入,无法验证实际生效。

Problem code:

Changed code at services/ctdsg__fw/src/ctdsg-fw.js:347-353

Recommendation:
timeoutMs 替换为 signal: AbortSignal.timeout(resolveTimeoutMs(ctx)),以利用 Node.js 原生 fetch 支持的 AbortSignal 机制实现真正的请求超时。

Suggested diff:

@@ -345,7 +345,7 @@
   let res;
   try {
     res = await withTlsBypass(ctx, () => fetch(url, {
       method: 'POST',
-      timeoutMs: resolveTimeoutMs(ctx),
+      signal: AbortSignal.timeout(resolveTimeoutMs(ctx)),
       headers: buildSignedHeaders(ctx, bodyJson),
       body: bodyJson,
     }));

@monkeyscan

monkeyscan Bot commented Jun 30, 2026

Copy link
Copy Markdown

PR Title: feat(services): add CTDSG FW service package

Commit: cbdb87b

本次 PR 的核心变更是移除 CTDSG FW 服务中硬编码的默认 appId('hybzapi'),将其从可选参数改为必填参数:

  1. 代码层面services/ctdsg__fw/src/ctdsg-fw.js 中删除了 DEFAULT_APP_ID 常量,并将 resolveAppIdoptionalString(... ) || DEFAULT_APP_ID 改为 requireString(..., 'appId')。当未提供 appId 时,现在会抛出 INVALID_ARGUMENT 错误而非静默使用默认值。

  2. 配置层面config.schema.json 移除了 appIddefault 字段,使其在 schema 层面也不再具有默认值。

  3. 测试层面test/ctdsg-fw.test.js 更新为使用 mock-app-id 替代硬编码的 hybzapi,并新增了断言验证缺失 appId 时抛出异常的行为。

  4. 文档层面README.md 示例中的 appId 做了脱敏处理。

总体评估:这是一项正向的安全改进,消除了多租户/多实例场景下因共享默认 appId 可能导致的凭证混淆或越权风险。但发现 config.schema.json 未同步将 appId 标记为 required,导致 schema 验证与运行时行为不一致。

Comment thread services/ctdsg__fw/config.schema.json
@monkeyscan

monkeyscan Bot commented Jun 30, 2026

Copy link
Copy Markdown

PR Title: feat(services): add CTDSG FW service package

Commit: e93e617

本次 PR 对 CTDSG FW 服务进行了两项主要加固:

  1. 将 TLS 跳过逻辑从修改全局 process.env.NODE_TLS_REJECT_UNAUTHORIZED 改为使用 undici.AgentrejectUnauthorized: false,消除了并发竞争条件;
  2. fetch 的超时从无效的 timeoutMs 选项改为标准的 AbortSignal.timeout()

同时,config.schema.json 显式要求 hostappId 字段,提升了配置校验的严谨性。

但在审查中发现两处由本次变更引入的问题:

  • resolveHost 函数在删除 const bindings = ctx.bindings || {}; 后,仍然引用 bindings.xxx,会导致运行时 ReferenceError
  • 测试文件中新增的 assert.throws(() => AbortSignal.timeout(10), ...) 断言不符合实际 API 行为(AbortSignal.timeout 同步返回信号对象,不会抛出),会导致测试失败。

assert.equal(_test.resolveHost({ req: {}, bindings: { restBaseUrl: 'https://binding.test/' } }), 'https://binding.test');
assert.equal(_test.resolveSecretKey({ req: {}, bindings: { secret_key: 's' } }), 's');
assert.equal(_test.resolveTimeoutMs({ req: { timeout_ms: 111 }, bindings: { timeoutMs: 222 }, limits: { timeoutMs: 333 } }), 111);
assert.throws(() => AbortSignal.timeout(10), /TimeoutError|The operation was aborted|signal/i);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

新增测试错误断言 AbortSignal.timeout 同步抛出异常

测试新增了一行 assert.throws(() => AbortSignal.timeout(10), ...),但 AbortSignal.timeout(ms) 是同步返回一个 AbortSignal 实例的工厂方法,本身不会抛出异常;超时行为是异步触发的。因此该断言在任何标准 Node.js 环境下都会失败,导致测试回归。

Problem code:

Changed code at services/ctdsg__fw/test/ctdsg-fw.test.js:291

Recommendation:
若目的是验证超时信号能被 fetch 正确消费,应改为对 fetchHttp 进行集成测试(例如使用极短的超时并断言抛出 UNAVAILABLEAbortError);若仅想验证 AbortSignal.timeout 存在,可直接调用而不包裹在 assert.throws 中。

Suggested diff:

--- a/services/ctdsg__fw/test/ctdsg-fw.test.js
+++ b/services/ctdsg__fw/test/ctdsg-fw.test.js
@@ -288,7 +288,6 @@ test('helpers cover parsing, aliases, signatures, timing, and normalization bran
   assert.equal(_test.resolveHost({ req: {}, bindings: { restBaseUrl: 'https://binding.test/' } }), 'https://binding.test');
   assert.equal(_test.resolveSecretKey({ req: {}, bindings: { secret_key: 's' } }), 's');
   assert.equal(_test.resolveTimeoutMs({ req: { timeout_ms: 111 }, bindings: { timeoutMs: 222 }, limits: { timeoutMs: 333 } }), 111);
-  assert.throws(() => AbortSignal.timeout(10), /TimeoutError|The operation was aborted|signal/i);
   assert.equal(_test.resolveTimeoutMs({ req: {}, bindings: { timeout_ms: 223 }, limits: { timeoutMs: 333 } }), 223);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已提交修复 请重新检查

@innomentats innomentats self-assigned this Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

长亭防火墙 Chaitin_FW_CTDSG_8815E

2 participants