Files
wx-win-agent/control-plane/README.md
T
rogee 0d29b828bb
Build web service image / build (push) Successful in 48s
feat: harden control-plane deployment
2026-09-12 11:09:42 +08:00

106 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WxAgent Control Plane
Go 1.23 控制面与 React 管理端。节点只主动出站连接,控制面不暴露节点端口。
## 本地启动
```bash
cd control-plane
npm --prefix web ci --no-audit --no-fund
npm --prefix web run build
go run ./cmd/wxagent-control-plane
```
启动前设置(单节点兼容写法):
```bash
export WXAGENT_NODE_ID=local-node
export WXAGENT_NODE_TOKEN='local-node-token'
export WXAGENT_WEB_USER=admin
export WXAGENT_WEB_PASSWORD='change-me'
```
多节点/多用户使用 JSON map:
```bash
export WXAGENT_NODE_TOKENS='{"node-a":"token-a","node-b":"token-b"}'
export WXAGENT_WEB_USERS='{"admin":"change-me","auditor":"read-only-password"}'
```
可选:`WXAGENT_CONTROL_PLANE_ADDR`(默认 `127.0.0.1:8090`)、`WXAGENT_CONTROL_PLANE_DATA`(默认 `control-plane-data.json`)。凭据可通过环境变量或 `*_FILE` Secret 文件读取,不写入控制面数据文件和日志。节点 `service.json` 可设置 `remoteConfigurationFile` 指向 CLI 管理的 `remote.json`;修改上报配置会在下一轮生效,修改远程地址/Token 后需重启 Agent。
生产控制面可直接启用 TLS 和节点 mTLS:
```text
WXAGENT_CONTROL_PLANE_TLS_CERT_FILE
WXAGENT_CONTROL_PLANE_TLS_KEY_FILE
WXAGENT_MTLS_CLIENT_CA_FILE
WXAGENT_MTLS_REQUIRE_NODE_CERT=true
WXAGENT_MTLS_REVOKED_CERTS_FILE
WXAGENT_NODE_TOKENS_FILE=/run/secrets/node_tokens
WXAGENT_WEB_USERS_FILE=/run/secrets/web_users
```
节点证书按证书原始 DER 的 SHA-256 指纹逐行写入撤销文件;文件读取失败时节点认证拒绝。控制面还提供 `/readyz`,并对数据文件使用单写入者锁、自动轮转备份和任务/事件/审计保留期限。生产示例见 [`compose.production.yml`](./compose.production.yml)。
浏览器访问 `http://127.0.0.1:8090/`,登录后管理节点、任务、白名单事件和审计记录。生产部署必须使用 HTTPS、节点 mTLS 和外部 Secret 文件;本地 HTTP 仅用于显式的私有网络/loopback 集成测试。
远程只读查询使用同一持久化任务队列,返回 `task_id` 后通过 `GET /v1/tasks/{task_id}` 取结果:
```text
POST /v1/reads/sessions { node_id, account_id, idempotency_key, limit?, offset? }
POST /v1/reads/contacts { node_id, account_id, idempotency_key, groups_only, contains?, limit?, offset? }
POST /v1/reads/messages { node_id, account_id, idempotency_key, chat_id, include_content?, limit?, offset? }
```
节点只返回本地已启用且 `identityVerified` 的白名单会话;未授权范围在节点读取前拒绝。`chat_id` 必须使用会话列表返回的稳定会话标识,消息结果是当前微信 UI 可见历史,不是数据库全量历史。联系人查询中的 `chat_id` 使用联系人数据库稳定 ID;所有响应仍受任务结果大小和分页上限约束。
## 验证
```bash
cd control-plane
go test ./...
go vet ./...
cd ../control-plane/web
npm run build
cd ../..
./scripts/remote-control-smoke.sh
```
`remote-control-smoke.sh` 会启动一次临时控制面,运行 .NET 节点协议客户端,验证注册、心跳、任务租约、幂等、结果回传和白名单拒绝;不会操作真实微信联系人。
控制面并发/读取任务基线(使用已注册的测试节点,不执行写任务):
```bash
WXAGENT_SCALE_WEB_PASSWORD='<test-password>' \
scripts/control-plane-scale-smoke.py --base-url http://127.0.0.1:8090 \
--node-id node-a --account-id account-a --requests 100 --workers 10
```
`control-plane-data.sh backup`/`restore` 用于停机前后的手工备份和原子恢复;自动备份由 Store 按 `WXAGENT_CONTROL_PLANE_BACKUP_INTERVAL`(默认 5 分钟)产生。当前 JSON 存储使用 active/passive 单写锁,不支持 active-active 多实例共享写入。
## Windows 手工验收
1. 在已登录且未锁定的微信桌面会话中运行 `WxAgent.Host doctor` 和 `WxAgent.Host inspect-ui --output artifacts/ui-tree.json`,确认账号绑定使用稳定 `accountId`,会话使用稳定 `AutomationId`,不使用昵称/PID/窗口句柄猜测。
2. 为节点配置 `remote.json`,例如先执行:
```powershell
WxAgent.Host remote auth set --config remote.json --address https://control.example --token $env:WXAGENT_NODE_TOKEN --node node-a --active-account <已确认的accountId>
WxAgent.Host remote reporting enable --config remote.json
WxAgent.Host remote reporting account-add --config remote.json --account <已确认的accountId>
WxAgent.Host remote reporting account-enable --config remote.json --account <已确认的accountId>
WxAgent.Host remote reporting allow --config remote.json --account <已确认的accountId> --type group --chat-id <已确认的稳定chatId> --identity-verified
WxAgent.Host remote probe run --config remote.json
```
3. 在节点 `service.json` 设置 `"remoteConfigurationFile": "<remote.json绝对路径>"` 后启动 `serve`。控制面应显示节点 `Online`、已验证账号和心跳版本。
4. 仅使用文件传输助手、`Hao 豪`、`吉祥三宝`、`消息测试专用群组`做发送/接收验证:先验证允许的群聊事件能到达控制面,再执行 `reporting deny`,确认后续正文不再上传;创建 `send-text` 前确认目标账号、稳定 chat ID 和文本。
5. 在控制面取消任务并验证节点不执行;断开网络后恢复,确认未完成副作用任务显示 `ResultUnconfirmed`,不会自动重放。
未解锁的 Session 0、昵称、PID、窗口句柄和未确认的运行时身份都应视为验收失败;手工验收不得改写微信数据库或绕过安全机制。
## 容器镜像
`.gitea/workflows/build-web-image.yml` 会构建并推送 `git.ipao.vip/<owner>/<repo>` 镜像:主分支额外更新 `latest`,`v*` 标签额外更新对应版本标签。请在 Gitea 账号级 Secrets 配置 `REGISTRY_TOKEN`;登录用户名使用仓库所有者。
生产环境不要提交 `compose.production.yml` 所引用的 `secrets/` 文件。将节点 Token map、Web 用户 map、TLS 私钥、客户端 CA 和撤销指纹列表放入外部 Secret 管理或受 ACL 保护的部署目录,再运行 `docker compose -f compose.production.yml up -d`。证书泄露时可用 `scripts/revoke-client-certificate.sh` 更新指纹列表,并重建 Docker Secret 挂载的容器。