chore: 整理项目结构,清理归档文件,更新部署配置
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# 智能IT支持服务台 - Secret 管理方案
|
||||
|
||||
**版本**: 1.0
|
||||
**更新日期**: 2026-06-14
|
||||
**状态**: 规划中
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
当前 `.env` 文件中存储了敏感信息:
|
||||
- WECOM_SECRET(企微应用密钥)
|
||||
- WECOM_ENCODING_AES_KEY(消息加密密钥)
|
||||
- DIFY_API_KEY(Dify API 密钥)
|
||||
- POSTGRES_PASSWORD(数据库密码)
|
||||
- REDIS_PASSWORD(Redis 密码)
|
||||
|
||||
**风险**:
|
||||
- `.env` 文件在 Git 仓库中(虽然被 .gitignore 排除,但部署时需手动复制)
|
||||
- 服务器上 `.env` 文件可能被未授权访问
|
||||
- 密钥轮换需要手动修改文件和重启服务
|
||||
|
||||
---
|
||||
|
||||
## 二、长期方案
|
||||
|
||||
### 方案对比
|
||||
|
||||
| 方案 | 复杂度 | 安全性 | 适用场景 |
|
||||
|------|--------|--------|----------|
|
||||
| **Server Keyring** | 低 | 中 | Linux 服务器(当前使用) |
|
||||
| **Docker Secrets** | 中 | 高 | K8s/ Swarm |
|
||||
| **HashiCorp Vault** | 高 | 高 | 企业级 |
|
||||
|
||||
### 推荐:Server Keyring
|
||||
|
||||
#### 方案1:Server Keyring(Linux,当前使用)
|
||||
|
||||
> ⚠️ NAS 部署方案已下线,当前使用公司内网服务器部署
|
||||
|
||||
```bash
|
||||
# 使用 keyring 工具
|
||||
keyring set wecom-it-desk WECOM_SECRET
|
||||
keyring get wecom-it-desk WECOM_SECRET
|
||||
```
|
||||
|
||||
#### 方案2:Docker Secrets(备选)
|
||||
|
||||
```bash
|
||||
# 使用 keyring 工具
|
||||
keyring set wecom-it-desk WECOM_SECRET
|
||||
keyring get wecom-it-desk WECOM_SECRET
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、短期止血
|
||||
|
||||
| 操作 | 说明 |
|
||||
|------|------|
|
||||
| 限制 .env 文件权限 | `chmod 600 .env` |
|
||||
| 不在 URL 中暴露 token | 已完成(P0-#4) |
|
||||
| 定期轮换密钥 | 建议每季度 |
|
||||
| 审计日志 | 规划中 |
|
||||
|
||||
---
|
||||
|
||||
## 四、实施计划
|
||||
|
||||
| 阶段 | 内容 | 优先级 |
|
||||
|------|------|--------|
|
||||
| MVP | 当前方案 + 限制文件权限 | P0 |
|
||||
| V1 | 迁移到 Server Keyring | P2 |
|
||||
| V2 | 迁移到 HashiCorp Vault | P3 |
|
||||
|
||||
---
|
||||
|
||||
## 五、相关文档
|
||||
|
||||
- `.env.example` - 环境变量模板(含 TODO 注释)
|
||||
- `docs/安全审计报告.md` - 安全审计记录
|
||||
@@ -0,0 +1,220 @@
|
||||
# 智能IT支持服务台 — 安全审计报告
|
||||
|
||||
> **编制日期**: 2026-06-14
|
||||
> **版本**: v1.0
|
||||
|
||||
---
|
||||
|
||||
## 1. 系统概述
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 系统名称 | 智能IT支持服务台 |
|
||||
| 部署环境 | 企业内网 (10.90.5.110) |
|
||||
| 访问方式 | 企微工作台应用 / HTTPS |
|
||||
| 用户规模 | ~6000人 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 安全架构
|
||||
|
||||
### 2.1 认证与授权
|
||||
|
||||
| 特性 | 实现方式 | 状态 |
|
||||
|------|----------|------|
|
||||
| **身份认证** | 企微OAuth2 + JWT Token | ✅ 已实现 |
|
||||
| **OTP双因素** | TOTP (Google Authenticator) | ✅ 已实现 |
|
||||
| **角色权限** | RBAC (user/agent/admin) | ✅ 已实现 |
|
||||
| **会话管理** | Redis Token + 过期时间 | ✅ 已实现 |
|
||||
| **密码策略** | 企微账户策略 | ✅ 依赖企微 |
|
||||
|
||||
### 2.2 网络安全
|
||||
|
||||
| 特性 | 实现方式 | 状态 |
|
||||
|------|----------|------|
|
||||
| **HTTPS** | Nginx SSL终止 | ✅ 已配置 |
|
||||
| **CORS** | 白名单域名 | ✅ 已配置 |
|
||||
| **IP白名单** | Nginx allow/deny | ⚠️ 待配置 |
|
||||
| **API限流** | Nginx rate_limit | ⚠️ 待配置 |
|
||||
| **WAF** | 腾讯WAF | ✅ 已接入 |
|
||||
|
||||
### 2.3 数据安全
|
||||
|
||||
| 特性 | 实现方式 | 状态 |
|
||||
|------|----------|------|
|
||||
| **数据库** | PostgreSQL (内网) | ✅ 已实现 |
|
||||
| **传输加密** | TLS 1.2+ | ✅ 已配置 |
|
||||
| **敏感脱敏** | 日志脱敏 | ⚠️ 待实现 |
|
||||
| **备份策略** | 定时备份 | ⚠️ 待配置 |
|
||||
| **加密存储** | 字段加密 | ❌ MVP不考虑 |
|
||||
|
||||
### 2.4 应用安全
|
||||
|
||||
| 特性 | 实现方式 | 状态 |
|
||||
|------|----------|------|
|
||||
| **SQL注入** | SQLAlchemy ORM | ✅ 已防护 |
|
||||
| **XSS** | 前端转义 | ✅ 已实现 |
|
||||
| **CSRF** | JWT Token | ✅ 已防护 |
|
||||
| **文件上传** | 类型限制 + 存储隔离 | ✅ 已实现 |
|
||||
| **API认证** | Token验证 | ✅ 已实现 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 审计日志
|
||||
|
||||
### 3.1 已记录事件
|
||||
|
||||
| 事件 | 记录位置 | 状态 |
|
||||
|------|----------|------|
|
||||
| 登录/登出 | 日志 | ✅ |
|
||||
| 消息发送 | 数据库 + 日志 | ✅ |
|
||||
| 会话创建/关闭 | 数据库 + 日志 | ✅ |
|
||||
| 管理员操作 | 日志 | ✅ |
|
||||
| 配置变更 | 数据库 | ✅ |
|
||||
|
||||
### 3.2 待记录事件
|
||||
|
||||
| 事件 | 优先级 | 说明 |
|
||||
|------|--------|------|
|
||||
| **敏感数据查询** | P1 | 查询用户信息、联系方式 |
|
||||
| **角色变更** | P1 | 管理员分配权限 |
|
||||
| **系统配置变更** | P1 | 功能开关、集成配置 |
|
||||
| **API调用统计** | P2 | 接口调用频率 |
|
||||
| **异常登录** | P1 | 异地登录、频繁失败 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 安全检查项
|
||||
|
||||
### 4.1 MVP必须通过
|
||||
|
||||
| # | 检查项 | 当前状态 | 建议 |
|
||||
|---|--------|----------|------|
|
||||
| 1 | 企微OAuth2认证 | ✅ | - |
|
||||
| 2 | JWT Token有效期 | ✅ 2小时 | - |
|
||||
| 3 | OTP绑定/验证 | ✅ | - |
|
||||
| 4 | 角色权限控制 | ✅ | - |
|
||||
| 5 | 数据库内网访问 | ✅ | - |
|
||||
| 6 | HTTPS全站加密 | ✅ | - |
|
||||
| 7 | 日志脱敏 | ⚠️ | 上线前完成 |
|
||||
| 8 | IP访问限制 | ⚠️ | 上线前完成 |
|
||||
|
||||
### 4.2 生产建议项
|
||||
|
||||
| # | 检查项 | 优先级 | 说明 |
|
||||
|---|--------|--------|------|
|
||||
| 9 | API限流 | P2 | 防DDoS |
|
||||
| 10 | 操作审计日志 | P2 | 合规要求 |
|
||||
| 11 | 数据库定时备份 | P2 | 灾备 |
|
||||
| 12 | 入侵检测 | P3 | 长期 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 消息状态功能(待实现)
|
||||
|
||||
### 5.1 需求
|
||||
|
||||
| 功能 | 说明 | 优先级 |
|
||||
|------|------|--------|
|
||||
| 已读未读状态 | 每条消息独立跟踪已读/未读 | P2 |
|
||||
| 已读时间戳 | 记录何时已读 | P2 |
|
||||
| 已读回执推送 | WS实时推送已读状态 | P2 |
|
||||
| 未读计数 | 会话未读消息数 | P2 |
|
||||
|
||||
### 5.2 现有代码
|
||||
|
||||
```python
|
||||
# 当前 Message 模型
|
||||
is_read: bool # 单字段,只能记录"是否已读"
|
||||
```
|
||||
|
||||
问题:多用户场景下无法区分用户独立已读状态
|
||||
|
||||
### 5.3 实现方案
|
||||
|
||||
```python
|
||||
# 新增 MessageStatus 表
|
||||
class MessageStatus(Base):
|
||||
message_id: str
|
||||
user_id: str # 读取者ID
|
||||
user_type: str # employee/agent
|
||||
status: Enum # sent/delivered/read
|
||||
read_at: datetime
|
||||
```
|
||||
|
||||
### 5.4 API设计
|
||||
|
||||
| API | 方法 | 说明 |
|
||||
|-----|------|------|
|
||||
| `/api/messages/{id}/read` | PUT | 标记消息已读 |
|
||||
| `/api/messages/{id}/status` | GET | 获取消息状态 |
|
||||
| `/api/conversations/{id}/unread-count` | GET | 未读计数 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险评估
|
||||
|
||||
| 风险 | 等级 | 缓解措施 |
|
||||
|------|------|----------|
|
||||
| 企微API限制 | 中 | 保持降级通道 |
|
||||
| 内网暴露面 | 中 | IP白名单 |
|
||||
| 社工攻击 | 低 | OTP + 安全培训 |
|
||||
| 数据泄露 | 低 | 内网 + HTTPS |
|
||||
|
||||
---
|
||||
|
||||
## 7. 架构优化(2026-06-14 讨论)
|
||||
|
||||
### 7.1 高可用方案
|
||||
|
||||
| 特性 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| restart: unless-stopped | ✅ 已配置 | 容器崩溃自动重启 |
|
||||
| healthcheck 后端 | ✅ 已配置 | curl /health |
|
||||
| healthcheck nginx | ✅ 已配置 | curl /itdesk/health |
|
||||
| healthcheck postgres | ✅ 已配置 | pg_isready |
|
||||
| healthcheck redis | ✅ 已配置 | redis-cli ping |
|
||||
|
||||
### 7.2 AI Gateway 设计
|
||||
|
||||
| 特性 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 内部抽象 | ⚠️ 待实现 | 抽离 AI 层为 Gateway |
|
||||
| 多模型支持 | ⚠️ 待实现 | dify/wingman/其他 |
|
||||
| 热切换 | ⚠️ 待实现 | 配置化切换 |
|
||||
| 降级机制 | ⚠️ 待实现 | 失败自动切换 |
|
||||
|
||||
设计目标:
|
||||
- 统一入口,支持 dify/wingman/其他模型
|
||||
- 配置化启用/禁用,无需改代码
|
||||
- 失败自动降级到备用模型
|
||||
|
||||
---
|
||||
|
||||
## 8. 结论
|
||||
|
||||
### 8.1 MVP可上线条件
|
||||
|
||||
- [x] 企微OAuth2认证
|
||||
- [x] OTP双因素
|
||||
- [x] 角色权限
|
||||
- [x] HTTPS
|
||||
- [x] Docker健康检查+自动重启
|
||||
- [ ] 日志脱敏(上线前完成)
|
||||
- [ ] IP访问限制(上线前完成)
|
||||
- [ ] AI Gateway(V2前完成)
|
||||
|
||||
### 8.2 下一步行动
|
||||
|
||||
| 行动 | 负责人 | 截止 | 状态 |
|
||||
|------|--------|------|------|
|
||||
| 日志脱敏 | 开发 | 上线前 | pending |
|
||||
| IP白名单 | 运维 | 上线前 | pending |
|
||||
| AI Gateway | 开发 | V2前 | pending |
|
||||
| 消息状态功能 | 开发 | V2 | pending |
|
||||
|
||||
---
|
||||
|
||||
> **编制人**: 宋献
|
||||
> **审核人**: 待定
|
||||
> **更新日期**: 2026-06-14
|
||||
@@ -0,0 +1,333 @@
|
||||
# 4 前端状态审计报告 + 统一优化路线
|
||||
|
||||
**审计日期**: 2026-06-15
|
||||
**审计人**: Claude
|
||||
**关联**: [[阶段1-已实现盘点]] / [[Wingman设计]] / 风险跟踪表
|
||||
|
||||
---
|
||||
|
||||
## 📌 1. 4 前端总览
|
||||
|
||||
| 前端 | 路径 | UI 框架 | 角色 | 视图数 | dist 大小(估) | 路由前缀 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| **admin** | `frontend-admin/` | Element Plus + Tailwind | 管理员 | 13+ | 大 | `/itadmin/` |
|
||||
| **agent** | `frontend-agent/` | Element Plus | 坐席 | 2(主) | 中 | `/itagent/` |
|
||||
| **h5** | `frontend-h5/` | Vant 4 | 员工 | 3 | 小 | `/itdesk/` |
|
||||
| **portal** | `frontend-portal/` | Element Plus | 统一入口 | 2 | 小 | `/itportal/` |
|
||||
|
||||
**技术栈统一度**: 🟢 高(Vue 3 + Vite + TypeScript + Pinia + Vue Router + Axios)
|
||||
|
||||
---
|
||||
|
||||
## 📌 2. frontend-admin 管理端
|
||||
|
||||
### 2.1 视图清单
|
||||
|
||||
| 路径 | 名称 | 状态 | 备注 |
|
||||
|---|---|---|---|
|
||||
| `/login` | 管理员登录 | ✅ | |
|
||||
| `/dashboard` | 运营总览 | ✅ | 统计卡片 |
|
||||
| `/configs` | 功能开关 | ✅ | |
|
||||
| `/agents` | 坐席管理 | ✅ | |
|
||||
| `/roles` | 角色管理 | ✅ | |
|
||||
| `/integrations` | 系统集成 | ✅ | 火绒/联软/aTrust/eHR |
|
||||
| `/quick-replies` | 快速回复 | ✅ | |
|
||||
| `/assignment-mode` | 分配模式 | ✅ | |
|
||||
| `/flowcharts` | 流程图 | ✅ | |
|
||||
| `/terminal-security` | 终端安全 | ✅ | |
|
||||
| `/session-audit` | 会话审计 | ✅ | |
|
||||
| `/system-logs` | 系统日志 | ✅ | |
|
||||
| `/agent-performance` | 坐席绩效 | ✅ | 阶段 4 数据看板扩展 |
|
||||
| `/monitor` | 监控 | ✅ | |
|
||||
|
||||
### 2.2 状态评估
|
||||
|
||||
- 🟢 **完成度高**:13+ 视图,功能齐全
|
||||
- 🟢 **使用 Element Plus + Tailwind**:UI 统一
|
||||
- 🟡 **缺失**:单元测试(Vitest 未配)
|
||||
- 🟡 **缺失**:E2E 测试(Playwright 未配)
|
||||
- 🟡 **缺失**:i18n(国际化)
|
||||
|
||||
### 2.3 已知问题
|
||||
|
||||
| # | 问题 | 严重度 | 解决 |
|
||||
|---|---|---|---|
|
||||
| A-1 | `/agent-performance` 是阶段 4 才有数据,目前空 | 🟡 | 阶段 4 实现 |
|
||||
| A-2 | `/system-logs` 没用虚拟滚动,日志多时卡 | 🟡 | vue-virtual-scroller |
|
||||
| A-3 | 角色管理权限粒度粗(没 RBAC) | 🟠 | 阶段 4 加 RBAC |
|
||||
| A-4 | 集成页只展示无配置 | 🟡 | 加配置表单 |
|
||||
|
||||
---
|
||||
|
||||
## 📌 3. frontend-agent 坐席端
|
||||
|
||||
### 3.1 视图清单
|
||||
|
||||
| 路径 | 名称 | 状态 | 备注 |
|
||||
|---|---|---|---|
|
||||
| `/login` | 坐席登录 | ✅ | 用户ID + 姓名 + password |
|
||||
| `/workspace` | 坐席工作台 | ✅ | 三栏(会话列表 / 对话 / 助手面板) |
|
||||
|
||||
### 3.2 组件清单(Workspace 包含)
|
||||
|
||||
| 组件 | 路径 | 状态 |
|
||||
|---|---|---|
|
||||
| ConversationList | `components/conversation/` | ✅ 6 分区 |
|
||||
| MessageBubble | `components/chat/` | ✅ 4 种气泡 |
|
||||
| ReplyBox | `components/chat/` | ✅ 输入框 + 草稿 |
|
||||
| AiAssistantPanel | `components/assistant/` | ✅ AI 助手面板 |
|
||||
| AiSuggestReply | `components/assistant/` | ✅ AI 草稿 |
|
||||
| AiDraftBubble | `components/chat/` | ✅ AI 草稿气泡 |
|
||||
| AiRecommendInline | `components/chat/` | ✅ AI 推荐内联 |
|
||||
| OperationSteps | `components/assistant/` | ✅ 操作步骤 |
|
||||
| RiskAlert | `components/assistant/` | ✅ 风险提示 |
|
||||
| UserInfoPanel | `components/assistant/` | ✅ 用户信息 |
|
||||
| QuickReplyPanel | `components/assistant/` | ✅ 快速回复 |
|
||||
| TroubleshootBar | `components/chat/` | ✅ 排查栏 |
|
||||
| TroubleshootProgress | (在 H5) | ✅ 员工端 |
|
||||
| TroubleshootFlow | (在 H5) | ✅ 员工端 |
|
||||
| FlowchartNode | `components/chat/` | ✅ 流程图节点 |
|
||||
| ScreenshotEditor | `components/chat/` | ✅ 截图编辑 |
|
||||
| InviteDialog | `components/conversation/` | ✅ 邀请弹窗 |
|
||||
| ParticipantBar | `components/conversation/` | ✅ 参与者栏 |
|
||||
| TodoPanel | `components/conversation/` | ✅ Todo 面板 |
|
||||
| TaskDetailView | `components/chat/` | ✅ 任务详情 |
|
||||
| TicketDetail | `components/chat/task/` | ✅ 工单详情 |
|
||||
| DeviceDetail | `components/chat/task/` | ✅ 设备详情 |
|
||||
| ApprovalDetail | `components/chat/task/` | ✅ 审批详情 |
|
||||
|
||||
### 3.3 Composables
|
||||
|
||||
- `useWebSocket.ts` (在别处)
|
||||
- `useTheme.ts` ✅
|
||||
- `useKeyboardShortcuts.ts` ✅
|
||||
- `useScreenCapture.ts` ✅
|
||||
|
||||
### 3.4 状态评估
|
||||
|
||||
- 🟢 **完成度极高**:23 组件 + 4 composables
|
||||
- 🟢 **三栏工作台**:会话 + 对话 + 助手,布局清晰
|
||||
- 🟢 **AI 集成**:AiSuggestReply / AiDraftBubble / AiRecommendInline 三个 AI 组件
|
||||
- 🟡 **缺失**:Vitest 单元测试
|
||||
- 🟡 **缺失**:操作步骤/风险提示数据源(等后端字段)
|
||||
- 🟡 **缺失**:坐席绩效统计(阶段 4)
|
||||
|
||||
### 3.5 已知问题
|
||||
|
||||
| # | 问题 | 严重度 | 解决 |
|
||||
|---|---|---|---|
|
||||
| A-5 | `useWebSocket.ts` token 用 subprotocol(P0 修复已加) | 🟢 | 已修 |
|
||||
| A-6 | ReplyBox 大量重渲染(200+ 消息卡) | 🟡 | 虚拟列表 |
|
||||
| A-7 | ScreenshotEditor 依赖 `html2canvas-pro` 体积大 | 🟡 | 改用 `dom-to-image` |
|
||||
| A-8 | mock 数据仍在用(`mock/data.ts`) | 🟡 | 删,接真实 API |
|
||||
|
||||
---
|
||||
|
||||
## 📌 4. frontend-h5 员工端
|
||||
|
||||
### 4.1 视图清单
|
||||
|
||||
| 路径 | 名称 | 状态 | 备注 |
|
||||
|---|---|---|---|
|
||||
| `/` | ChatView(聊天) | ✅ | 默认首页 |
|
||||
| `/login` | 降级登录 | ✅ | 本地开发用 |
|
||||
| `/wework-only` | 企微拦截 | ✅ | 非企微环境显示 |
|
||||
|
||||
### 4.2 组件清单
|
||||
|
||||
| 组件 | 状态 |
|
||||
|---|---|
|
||||
| ChatPanel | ✅ |
|
||||
| ShakeButton(敲桌子) | ✅ 7 种 SVG |
|
||||
| TroubleshootProgress | ✅ |
|
||||
| TroubleshootFlow | ✅ |
|
||||
| ScreenshotEditor | ✅ |
|
||||
| ParticipantList | ✅ |
|
||||
| AiHelperPanel | ✅ |
|
||||
| ApprovalLinks | ✅ |
|
||||
| SoftwareDownloads | ✅ |
|
||||
| RightPanel | ✅ |
|
||||
| ComingSoon | ✅ 占位 |
|
||||
|
||||
### 4.3 状态评估
|
||||
|
||||
- 🟢 **完成度高**:11 组件
|
||||
- 🟢 **Vant 4 移动端 UI**:适配好
|
||||
- 🟡 **缺失**:Vitest 单元测试
|
||||
- 🟡 **缺失**:摇人按钮(阶段 2 加)
|
||||
- 🟡 **缺失**:满意度评价(阶段 2 加)
|
||||
- 🟡 **缺失**:AI 回复展示(等 Dify 集成)
|
||||
|
||||
### 4.4 已知问题
|
||||
|
||||
| # | 问题 | 严重度 | 解决 |
|
||||
|---|---|---|---|
|
||||
| A-9 | OAuth2 callback 路径二次校验缺失(风险跟踪表 H-9 衍生) | 🟠 | 加 state 参数 |
|
||||
| A-10 | H5 不支持长连接(用轮询降级) | 🟡 | 优先 WS |
|
||||
| A-11 | Vant 4 vs Vant 3 API 差异,部分组件可能错版 | 🟡 | 走通测试 |
|
||||
|
||||
---
|
||||
|
||||
## 📌 5. frontend-portal 统一入口
|
||||
|
||||
### 5.1 视图清单
|
||||
|
||||
| 路径 | 名称 | 状态 | 备注 |
|
||||
|---|---|---|---|
|
||||
| `/select` | 角色选择 | ✅ | 跳 admin / agent |
|
||||
| `/loading` | 加载中 | ✅ | 中转页 |
|
||||
|
||||
### 5.2 状态评估
|
||||
|
||||
- 🟢 **简单但有效**:2 视图
|
||||
- 🟢 **集成 OAuth2**(走 admin/agent 的 token 传递)
|
||||
- 🟡 **缺失**:跳过 Portal 直跳有问题(必须先 select)
|
||||
|
||||
### 5.3 已知问题
|
||||
|
||||
| # | 问题 | 严重度 | 解决 |
|
||||
|---|---|---|---|
|
||||
| A-12 | token 传递用 URL ?token= 风险(同 WS) | 🟠 | 改 sessionStorage |
|
||||
| A-13 | `/loading` 没超时,卡死无 fallback | 🟡 | 10s 后回 `/select` |
|
||||
|
||||
---
|
||||
|
||||
## 📌 6. 跨前端共性问题
|
||||
|
||||
### 6.1 主题
|
||||
|
||||
- 🟢 **统一**: 都有 `useTheme.ts`
|
||||
- 🟡 主题切换没持久化(刷新丢)
|
||||
- 🟡 没暗色模式
|
||||
|
||||
### 6.2 错误处理
|
||||
|
||||
- 🟡 4 前端**都没全局错误边界**(try-catch 不一致)
|
||||
- 🟡 4 前端**错误码体系不统一**(各端自行处理)
|
||||
- 🟢 4 前端**都接 axios + 拦截器**
|
||||
|
||||
### 6.3 状态管理
|
||||
|
||||
- 🟢 **统一 Pinia**
|
||||
- 🟡 stores 命名不一致(有些用 `useXxxStore`,有些 `useXxx`)
|
||||
|
||||
### 6.4 构建产物
|
||||
|
||||
| 前端 | dist 大小(估) | Gzip 后 | 首屏 |
|
||||
|---|---|---|---|
|
||||
| admin | 2-3 MB | ~500KB | 慢 |
|
||||
| agent | 1.5-2 MB | ~400KB | 中 |
|
||||
| h5 | 1-1.5 MB | ~300KB | 快 |
|
||||
| portal | 200KB | ~50KB | 极快 |
|
||||
|
||||
**优化空间**:
|
||||
- Element Plus 按需引入(全量 vs tree-shaking)
|
||||
- 拆 vendor chunk
|
||||
- 图片用 WebP
|
||||
|
||||
### 6.5 测试覆盖
|
||||
|
||||
| 前端 | Vitest | Playwright | E2E |
|
||||
|---|---|---|---|
|
||||
| admin | ❌ 0% | ❌ 0% | ❌ 0% |
|
||||
| agent | ❌ 0% | ❌ 0% | ❌ 0% |
|
||||
| h5 | ❌ 0% | ❌ 0% | ❌ 0% |
|
||||
| portal | ❌ 0% | ❌ 0% | ❌ 0% |
|
||||
|
||||
**全是 0%** —— 严重问题,workbuddy W-3 加 pytest 后端测试,前端 Vitest 也要加。
|
||||
|
||||
---
|
||||
|
||||
## 📌 7. 统一优化路线
|
||||
|
||||
### 7.1 P1 优先(2 周)
|
||||
|
||||
| # | 任务 | 影响 |
|
||||
|---|---|---|
|
||||
| U-1 | 4 前端加 Vitest(基础测试框架) | 提升质量 |
|
||||
| U-2 | 全局错误边界 + 错误码体系 | 统一错误处理 |
|
||||
| U-3 | Pinia store 命名规范 | 一致性 |
|
||||
| U-4 | 主题持久化(localStorage) | UX 改进 |
|
||||
| U-5 | 删 agent `mock/data.ts`,接真实 API | 真实数据 |
|
||||
|
||||
### 7.2 P2 重要(4 周)
|
||||
|
||||
| # | 任务 | 影响 |
|
||||
|---|---|---|
|
||||
| U-6 | admin `/system-logs` 虚拟滚动 | 性能 |
|
||||
| U-7 | agent ReplyBox 消息虚拟化 | 性能 |
|
||||
| U-8 | 4 前端加 ESLint + Prettier 一致 | 代码质量 |
|
||||
| U-9 | agent ScreenshotEditor 换库 | 体积 |
|
||||
| U-10 | h5 OAuth2 state 校验 | 安全 |
|
||||
| U-11 | portal token 走 sessionStorage | 安全 |
|
||||
|
||||
### 7.3 P3 体验(2 月)
|
||||
|
||||
| # | 任务 | 影响 |
|
||||
|---|---|---|
|
||||
| U-12 | 暗色模式(全 4 前端) | UX |
|
||||
| U-13 | i18n(中/英) | 国际化 |
|
||||
| U-14 | PWA(offline 支持) | 体验 |
|
||||
| U-15 | Storybook 组件库 | 开发效率 |
|
||||
| U-16 | E2E 测试(Playwright) | 回归 |
|
||||
|
||||
### 7.4 性能优化(持续)
|
||||
|
||||
- Element Plus 按需引入
|
||||
- 图片 WebP + lazy load
|
||||
- Code Splitting 拆 vendor chunk
|
||||
- HTTP/2 + Brotli
|
||||
- 路由级代码分割(已有)
|
||||
|
||||
---
|
||||
|
||||
## 📌 8. 实施路径
|
||||
|
||||
### 8.1 阶段 1(本周)
|
||||
|
||||
- U-1 Vitest 基础(每个前端 1 模板测试)
|
||||
- U-5 删 mock data
|
||||
|
||||
### 8.2 阶段 2(下周)
|
||||
|
||||
- U-2 全局错误边界 + 错误码
|
||||
- U-3 Pinia 命名规范
|
||||
- U-4 主题持久化
|
||||
|
||||
### 8.3 阶段 3(下月)
|
||||
|
||||
- U-6 / U-7 性能优化
|
||||
- U-8 ESLint
|
||||
- U-10 / U-11 安全加固
|
||||
|
||||
### 8.4 阶段 4(季度)
|
||||
|
||||
- U-12 暗色模式
|
||||
- U-13 i18n
|
||||
- U-14 PWA
|
||||
- U-15 Storybook
|
||||
- U-16 E2E
|
||||
|
||||
---
|
||||
|
||||
## 📌 9. 风险与依赖
|
||||
|
||||
| 风险 | 等级 | 缓解 |
|
||||
|---|---|---|
|
||||
| 测试覆盖 0% → 重构风险 | 🟠 高 | 强制 Vitest 模板,新代码必带测试 |
|
||||
| 4 前端重复代码 | 🟡 中 | 抽公共组件库(Stage 4) |
|
||||
| 性能问题(Response 卡) | 🟡 中 | 虚拟列表 + 分页 |
|
||||
| 主题/暗色模式分歧 | 🟢 低 | 统一 theme store |
|
||||
|
||||
---
|
||||
|
||||
## 📌 10. 关联文档
|
||||
|
||||
- [[阶段1-已实现盘点]] §2.1/2.2/2.3
|
||||
- [[Wingman设计]] §4 前端设计
|
||||
- [[风险跟踪表]] H-9 / M-1 等
|
||||
- [[外部系统集成]] - portal/agent 集成
|
||||
|
||||
---
|
||||
|
||||
*本审计是 2026-06-15 Claude 满载跑批产出,待评审*
|
||||
@@ -0,0 +1,490 @@
|
||||
# CORS / CSP / 安全 Header 全套审计与改进
|
||||
|
||||
**审计日期**: 2026-06-15
|
||||
**审计人**: Claude(满载跑批)
|
||||
**关联**: [[风险跟踪表]] / [[后端架构]] / [[外部系统集成]]
|
||||
|
||||
---
|
||||
|
||||
## 📌 1. 现状盘点
|
||||
|
||||
### 1.1 后端 CORS 配置(`backend/app/main.py:363`)
|
||||
|
||||
```python
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.cors_origins_list, # 逗号分隔的列表
|
||||
allow_credentials=True,
|
||||
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||
allow_headers=["Authorization", "Content-Type", "X-Employee-Id"],
|
||||
)
|
||||
```
|
||||
|
||||
**当前 `cors_origins`**:
|
||||
- 默认: `localhost:5173,5174,5175`(开发)
|
||||
- 生产: `itsupport.servyou.com.cn`(.env.production)
|
||||
|
||||
### 1.2 Nginx 安全头(`nginx.conf` + `nginx-nas.conf`)
|
||||
|
||||
**已有**:
|
||||
```nginx
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
```
|
||||
|
||||
**缺失**:
|
||||
- `Strict-Transport-Security` (HSTS)
|
||||
- `Content-Security-Policy` (CSP)
|
||||
- `Referrer-Policy`
|
||||
- `Permissions-Policy`
|
||||
- `Cross-Origin-*` 系列
|
||||
|
||||
### 1.3 问题清单
|
||||
|
||||
| # | 问题 | 严重度 | 风险 |
|
||||
|---|---|---|---|
|
||||
| C-1 | CORS `allow_origins` 默认含 `*`(环境切换不当会泄露) | 🟠 | 跨域未授权 |
|
||||
| C-2 | CORS 没限制 `expose_headers`(前端拿不到 trace_id) | 🟡 | 排障不便 |
|
||||
| C-3 | CORS `max_age` 未设(每次预检) | 🟢 | 性能 |
|
||||
| C-4 | nginx 缺 HSTS | 🟠 | 中间人降级 |
|
||||
| C-5 | nginx 缺 CSP | 🟠 | XSS |
|
||||
| C-6 | nginx 缺 Referrer-Policy | 🟡 | 信息泄露 |
|
||||
| C-7 | nginx 缺 Permissions-Policy | 🟡 | 设备 API 滥用 |
|
||||
| C-8 | nginx 缺 COOP/COEP | 🟡 | 跨源攻击 |
|
||||
| C-9 | `/api/wecom/callback` 没限 IP | 🟡 | 恶意回调 |
|
||||
| C-10 | 4 前端没 CSP meta(防 XSS) | 🟠 | XSS |
|
||||
|
||||
---
|
||||
|
||||
## 📌 2. CORS 改进
|
||||
|
||||
### 2.1 后端 - 精细化 CORS
|
||||
|
||||
**新建 `backend/app/utils/cors_config.py`**:
|
||||
```python
|
||||
from typing import List
|
||||
from app.config import settings
|
||||
|
||||
|
||||
def build_cors_config() -> dict:
|
||||
"""根据环境构建 CORS 配置"""
|
||||
is_prod = settings.backend_env == "production" # 需新增环境变量
|
||||
|
||||
if is_prod:
|
||||
# 生产:严格白名单
|
||||
origins = [
|
||||
o.strip() for o in settings.cors_origins.split(",")
|
||||
if o.strip() and not o.startswith("*")
|
||||
]
|
||||
return {
|
||||
"allow_origins": origins,
|
||||
"allow_credentials": True,
|
||||
"allow_methods": ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
|
||||
"allow_headers": [
|
||||
"Authorization",
|
||||
"Content-Type",
|
||||
"X-Employee-Id",
|
||||
"X-Request-ID", # trace_id
|
||||
"X-CSRF-Token", # CSRF 防护
|
||||
"X-Agent-Id", # 坐席 ID
|
||||
],
|
||||
"expose_headers": [
|
||||
"X-Request-ID", # 暴露 trace_id
|
||||
"X-RateLimit-Remaining", # 限流剩余
|
||||
"X-RateLimit-Reset", # 限流重置
|
||||
],
|
||||
"max_age": 600, # 10 分钟预检缓存
|
||||
}
|
||||
|
||||
# 开发:宽松
|
||||
return {
|
||||
"allow_origins": settings.cors_origins_list,
|
||||
"allow_credentials": True,
|
||||
"allow_methods": ["*"],
|
||||
"allow_headers": ["*"],
|
||||
"expose_headers": ["*"],
|
||||
"max_age": 3600,
|
||||
}
|
||||
```
|
||||
|
||||
**更新 `main.py`**:
|
||||
```python
|
||||
from app.utils.cors_config import build_cors_config
|
||||
|
||||
cors_config = build_cors_config()
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
**cors_config,
|
||||
)
|
||||
```
|
||||
|
||||
### 2.2 新增环境变量
|
||||
|
||||
**`backend/app/config.py`**:
|
||||
```python
|
||||
# 新增
|
||||
backend_env: str = "development" # development / production
|
||||
```
|
||||
|
||||
**`.env.production`**:
|
||||
```bash
|
||||
BACKEND_ENV=production
|
||||
CORS_ORIGINS=https://itsupport.servyou.com.cn
|
||||
```
|
||||
|
||||
### 2.3 CORS 验证脚本
|
||||
|
||||
```bash
|
||||
# 验证 CORS 头
|
||||
curl -I -X OPTIONS \
|
||||
-H "Origin: https://itsupport.servyou.com.cn" \
|
||||
-H "Access-Control-Request-Method: POST" \
|
||||
-H "Access-Control-Request-Headers: Authorization" \
|
||||
http://localhost:8000/api/v1/auth/login
|
||||
|
||||
# 期望响应:
|
||||
# Access-Control-Allow-Origin: https://itsupport.servyou.com.cn
|
||||
# Access-Control-Allow-Credentials: true
|
||||
# Access-Control-Max-Age: 600
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 3. Nginx 安全 Header 完整套
|
||||
|
||||
### 3.1 完整版 nginx.conf(替换安全头部分)
|
||||
|
||||
```nginx
|
||||
# =================================================================
|
||||
# 安全响应头配置(全部)
|
||||
# =================================================================
|
||||
|
||||
# 1. HSTS - 强制 HTTPS(2 年,包含子域名)
|
||||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
||||
|
||||
# 2. CSP - 内容安全策略(严格版,API 网关除外)
|
||||
# 注意:API 路径不要 CSP(纯 JSON),只 HTML 路径需要
|
||||
location /itdesk/ {
|
||||
# 基础 CSP
|
||||
add_header Content-Security-Policy "
|
||||
default-src 'self';
|
||||
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://res.wx.qq.com;
|
||||
style-src 'self' 'unsafe-inline';
|
||||
img-src 'self' data: blob: https: http:;
|
||||
font-src 'self' data:;
|
||||
connect-src 'self' https://qyapi.weixin.qq.com wss://* https://*.servyou-it.com;
|
||||
media-src 'self' blob:;
|
||||
object-src 'none';
|
||||
frame-ancestors 'none';
|
||||
base-uri 'self';
|
||||
form-action 'self';
|
||||
upgrade-insecure-requests;
|
||||
" always;
|
||||
|
||||
alias /usr/share/nginx/html/itdesk/;
|
||||
# ...
|
||||
}
|
||||
|
||||
# 3. 防 MIME 嗅探
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
|
||||
# 4. 防点击劫持(更严:拒绝所有 frame 嵌入)
|
||||
add_header X-Frame-Options "DENY" always;
|
||||
|
||||
# 5. XSS 过滤器(现代浏览器已废弃,保留向后兼容)
|
||||
add_header X-XSS-Protection "0" always; # 0 = 关闭(CSP 已接管)
|
||||
|
||||
# 6. Referrer 策略(API 不发送 referrer,HTML 限制来源)
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# 7. Permissions Policy(禁用不用的设备 API)
|
||||
add_header Permissions-Policy "
|
||||
camera=(),
|
||||
microphone=(),
|
||||
geolocation=(),
|
||||
payment=(),
|
||||
usb=(),
|
||||
magnetometer=(),
|
||||
gyroscope=(),
|
||||
accelerometer=()
|
||||
" always;
|
||||
|
||||
# 8. 跨源隔离
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||||
|
||||
# 9. 服务器信息隐藏
|
||||
server_tokens off; # 隐藏 nginx 版本
|
||||
|
||||
# 10. API 路径特殊头(API 不需要 CSP,但要 CORS 友好)
|
||||
location /api/ {
|
||||
# 移除 CSP(API 返回 JSON,不要 CSP)
|
||||
more_clear_headers "Content-Security-Policy";
|
||||
|
||||
# API 也加 HSTS
|
||||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Cache-Control "no-store" always; # API 禁止缓存
|
||||
|
||||
proxy_pass http://backend_api/;
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 完整版 nginx-nas.conf(同上,Cloudflare 适配)
|
||||
|
||||
```nginx
|
||||
# Cloudflare Tunnel 已经在外层 HTTPS,这里加全头
|
||||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "DENY" always;
|
||||
add_header X-XSS-Protection "0" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||||
|
||||
server_tokens off;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 4. 前端 CSP Meta(双保险)
|
||||
|
||||
### 4.1 4 前端 `index.html` 加 meta CSP
|
||||
|
||||
**`frontend-admin/index.html`**:
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-Content-Type-Options" content="nosniff">
|
||||
|
||||
<!-- CSP - 与 nginx 头保持一致 -->
|
||||
<meta http-equiv="Content-Security-Policy" content="
|
||||
default-src 'self';
|
||||
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://res.wx.qq.com;
|
||||
style-src 'self' 'unsafe-inline';
|
||||
img-src 'self' data: blob: https: http:;
|
||||
font-src 'self' data:;
|
||||
connect-src 'self' https://qyapi.weixin.qq.com wss://* https://*.servyou-it.com;
|
||||
media-src 'self' blob:;
|
||||
object-src 'none';
|
||||
frame-ancestors 'none';
|
||||
base-uri 'self';
|
||||
form-action 'self';
|
||||
">
|
||||
|
||||
<meta name="referrer" content="strict-origin-when-cross-origin">
|
||||
<meta http-equiv="Permissions-Policy" content="
|
||||
camera=(), microphone=(), geolocation=(), payment=()
|
||||
">
|
||||
|
||||
<title>IT 智能服务台 - 管理后台</title>
|
||||
</head>
|
||||
```
|
||||
|
||||
### 4.2 CSP 报告模式(先观察,再强制)
|
||||
|
||||
**第 1 步: Report-Only 模式(2 周)**:
|
||||
```nginx
|
||||
add_header Content-Security-Policy-Report-Only "..." always;
|
||||
```
|
||||
|
||||
**第 2 步: 收集违规报告**(发到 `/api/v1/csp-report`)
|
||||
|
||||
**第 3 步: 改 enforce 模式**:
|
||||
```nginx
|
||||
add_header Content-Security-Policy "..." always; # 不带 Report-Only
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 5. 企微回调 IP 限制
|
||||
|
||||
### 5.1 企微回调 IP 段(从企微文档)
|
||||
|
||||
| 段 | 用途 |
|
||||
|---|---|
|
||||
| `101.226.103.0/24` | 企微上海 |
|
||||
| `101.226.108.0/24` | 企微上海 |
|
||||
| `140.207.54.0/24` | 企微上海 |
|
||||
| `140.207.61.0/24` | 企微深圳 |
|
||||
| `183.192.192.0/18` | 企微通用 |
|
||||
| `121.51.130.0/24` | 企微广州 |
|
||||
|
||||
**注意**: 实际范围可能变更,需查官方文档。
|
||||
|
||||
### 5.2 nginx 限制
|
||||
|
||||
```nginx
|
||||
location = /api/wecom/callback {
|
||||
# 只允许企微 IP 段
|
||||
allow 101.226.103.0/24;
|
||||
allow 101.226.108.0/24;
|
||||
allow 140.207.54.0/24;
|
||||
allow 140.207.61.0/24;
|
||||
allow 183.192.192.0/18;
|
||||
allow 121.51.130.0/24;
|
||||
|
||||
# 内网允许(开发)
|
||||
allow 127.0.0.1;
|
||||
allow 10.0.0.0/8;
|
||||
allow 172.16.0.0/12;
|
||||
allow 192.168.0.0/16;
|
||||
|
||||
deny all;
|
||||
|
||||
proxy_pass http://backend_api/api/wecom/callback;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 6. 速率限制(补充)
|
||||
|
||||
### 6.1 现有方案
|
||||
|
||||
`backend/app/main.py` 已用 `slowapi` 全局限流,默认 60/分钟。
|
||||
|
||||
### 6.2 精细化建议
|
||||
|
||||
| 路径 | 限制 | 理由 |
|
||||
|---|---|---|
|
||||
| `/api/v1/auth/login` | 5/分钟/IP | 防爆破 |
|
||||
| `/api/v1/auth/otp` | 3/分钟/IP | 防 OTP 暴力 |
|
||||
| `/api/v1/conversations` | 60/分钟/agent | 正常业务 |
|
||||
| `/api/v1/messages` | 120/分钟/agent | 消息多 |
|
||||
| `/api/wecom/callback` | 不限 | 企微回调 |
|
||||
|
||||
**配置示例**:
|
||||
```python
|
||||
# backend/app/main.py
|
||||
from slowapi import Limiter
|
||||
from slowapi.util import get_remote_address
|
||||
|
||||
limiter = Limiter(key_func=get_remote_address)
|
||||
|
||||
@app.post("/api/v1/auth/login")
|
||||
@limiter.limit("5/minute")
|
||||
async def login(...):
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 7. 测试与验证
|
||||
|
||||
### 7.1 安全 Header 在线检测
|
||||
|
||||
```bash
|
||||
# Mozilla Observatory
|
||||
https://observatory.mozilla.org/analyze/itsupport.servyou.com.cn
|
||||
|
||||
# Security Headers
|
||||
https://securityheaders.com/?q=itsupport.servyou.com.cn
|
||||
|
||||
# SSL Labs(SSL 评估)
|
||||
https://www.ssllabs.com/ssltest/analyze.html?d=itsupport.servyou.com.cn
|
||||
```
|
||||
|
||||
### 7.2 CORS 自动化测试
|
||||
|
||||
**`scripts/cors-test.sh`**(新建):
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# CORS 自动化测试
|
||||
set -e
|
||||
|
||||
API="http://localhost:8000"
|
||||
ORIGIN="http://localhost:5173"
|
||||
|
||||
echo "=== 1. 预检请求 ==="
|
||||
curl -s -I -X OPTIONS \
|
||||
-H "Origin: $ORIGIN" \
|
||||
-H "Access-Control-Request-Method: POST" \
|
||||
-H "Access-Control-Request-Headers: Authorization" \
|
||||
"$API/api/v1/auth/login" | head -20
|
||||
|
||||
echo ""
|
||||
echo "=== 2. 实际请求 ==="
|
||||
curl -s -I -H "Origin: $ORIGIN" "$API/api/v1/health" | head -20
|
||||
|
||||
echo ""
|
||||
echo "=== 3. 期望 ==="
|
||||
echo "Access-Control-Allow-Origin: $ORIGIN"
|
||||
echo "Access-Control-Allow-Credentials: true"
|
||||
```
|
||||
|
||||
### 7.3 安全 Header 验证
|
||||
|
||||
**`scripts/security-headers-test.sh`**(新建):
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 安全 Header 验证
|
||||
|
||||
URL="${1:-http://localhost}"
|
||||
|
||||
echo "=== 检查安全头 ==="
|
||||
HEADERS=$(curl -sI "$URL/")
|
||||
|
||||
check_header() {
|
||||
local header=$1
|
||||
local expected=$2
|
||||
if echo "$HEADERS" | grep -qi "^$header:"; then
|
||||
echo "✅ $header: $(echo "$HEADERS" | grep -i "^$header:" | cut -d':' -f2- | xargs)"
|
||||
else
|
||||
echo "❌ $header: 缺失"
|
||||
fi
|
||||
}
|
||||
|
||||
check_header "Strict-Transport-Security"
|
||||
check_header "X-Content-Type-Options" "nosniff"
|
||||
check_header "X-Frame-Options"
|
||||
check_header "Content-Security-Policy"
|
||||
check_header "Referrer-Policy"
|
||||
check_header "Permissions-Policy"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 8. 实施路径
|
||||
|
||||
### 8.1 立即(本次跑批)
|
||||
|
||||
- [x] 审计报告写完(本文件)
|
||||
- [ ] 更新 `nginx.conf` + `nginx-nas.conf` 加 HSTS/CSP/Permissions
|
||||
- [ ] 加 `server_tokens off`
|
||||
- [ ] 4 前端 `index.html` 加 CSP meta
|
||||
|
||||
### 8.2 下周
|
||||
|
||||
- [ ] 改 CORS 精细化(分环境)
|
||||
- [ ] 企微回调 IP 白名单
|
||||
- [ ] 加 `/api/v1/csp-report` 端点
|
||||
- [ ] 跑 `cors-test.sh` 验证
|
||||
|
||||
### 8.3 季度
|
||||
|
||||
- [ ] 提交 https://hstspreload.org/(HSTS 预加载)
|
||||
- [ ] 跑 Mozilla Observatory(A+ 目标)
|
||||
- [ ] 跑 Security Headers(A 目标)
|
||||
|
||||
---
|
||||
|
||||
## 📌 9. 关联文档
|
||||
|
||||
- [[风险跟踪表]] M-3(无统一错误码)/ H-4(WS token)
|
||||
- [[后端架构]] §5 错误处理 / §4 中间件
|
||||
- [[外部系统集成]] §1-4(企微凭据)
|
||||
- [[健康检查+错误码+日志结构化]] - trace_id(配合 CSP 报告)
|
||||
|
||||
---
|
||||
|
||||
*本审计是 2026-06-15 Claude 满载跑批产出,待评审*
|
||||
@@ -0,0 +1,375 @@
|
||||
# Dockerfile 优化 + 镜像审计报告
|
||||
|
||||
**审计日期**: 2026-06-15
|
||||
**审计人**: Claude
|
||||
**关联**: [[风险跟踪表]] / [[SOP-001-Gitea部署]]
|
||||
|
||||
---
|
||||
|
||||
## 📌 1. 现状盘点
|
||||
|
||||
| 镜像 | Dockerfile | 基础 | 估计大小 | 多阶段 |
|
||||
|---|---|---|---|---|
|
||||
| backend | `backend/Dockerfile` | `python:3.12-slim` | ~250 MB | ✅ |
|
||||
| frontend-agent | `frontend-agent/Dockerfile` | `nginx:1.27-alpine` | ~50 MB | ✅ |
|
||||
| frontend-h5 | `frontend-h5/Dockerfile` | `nginx:1.27-alpine` | ~50 MB | ✅ |
|
||||
| postgres | (用官方) | `postgres:16-alpine` | ~80 MB | — |
|
||||
| redis | (用官方) | `redis:7-alpine` | ~30 MB | — |
|
||||
| nginx | (用官方) | `nginx:1.27-alpine` | ~40 MB | — |
|
||||
|
||||
**总估计镜像大小**:`~500 MB`(4 业务 + 2 数据库)
|
||||
|
||||
---
|
||||
|
||||
## 📌 2. backend Dockerfile 审计
|
||||
|
||||
### 2.1 当前实现
|
||||
|
||||
```dockerfile
|
||||
FROM python:3.12-slim AS builder
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc libpq-dev libjpeg-dev zlib1g-dev curl
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir --timeout 120 --retries 5 \
|
||||
-i https://pypi.tuna.tsinghua.edu.cn/simple/ \
|
||||
--trusted-host pypi.tuna.tsinghua.edu.cn \
|
||||
-r requirements.txt
|
||||
|
||||
FROM python:3.12-slim
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends libpq5 curl
|
||||
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
|
||||
COPY --from=builder /usr/local/bin /usr/local/bin
|
||||
COPY . .
|
||||
EXPOSE 8000
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
```
|
||||
|
||||
### 2.2 问题清单
|
||||
|
||||
| # | 问题 | 严重度 | 优化 |
|
||||
|---|---|---|---|
|
||||
| B-1 | ⚠️ **装 curl** — 但 P1-3 已改 healthcheck 用 Python urllib | 🟡 | 删 curl(节省 1MB) |
|
||||
| B-2 | ⚠️ **不用非 root 用户** | 🟠 中 | 加 `USER appuser` |
|
||||
| B-3 | ⚠️ **没 HEALTHCHECK** — 交给 docker-compose | 🟡 | Dockerfile 也加 |
|
||||
| B-4 | ⚠️ **COPY . . 太宽** — 含 .git / tests / docs | 🟡 | 加 .dockerignore |
|
||||
| B-5 | 🟢 pip 装到 venv(更隔离) | 🟢 | 已用 site-packages |
|
||||
| B-6 | ⚠️ **没用 BuildKit cache mount** | 🟡 | 加 `--mount=type=cache` |
|
||||
| B-7 | ⚠️ **PyPI 用清华源** — 公司内网可,但生产建议官方 | 🟡 | 评估 |
|
||||
|
||||
### 2.3 优化版
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1.7
|
||||
FROM python:3.12-slim AS builder
|
||||
|
||||
# 创建非 root 用户
|
||||
RUN groupadd -r appuser && useradd -r -g appuser appuser
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 系统依赖(只装构建期需要的)
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
gcc libpq-dev libjpeg-dev zlib1g-dev && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 依赖(用 cache mount + BuildKit)
|
||||
COPY requirements.txt .
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
pip install --no-cache-dir --user \
|
||||
--timeout 120 --retries 5 \
|
||||
-i https://pypi.tuna.tsinghua.edu.cn/simple/ \
|
||||
--trusted-host pypi.tuna.tsinghua.edu.cn \
|
||||
-r requirements.txt
|
||||
|
||||
# 运行镜像
|
||||
FROM python:3.12-slim
|
||||
|
||||
# 复制非 root 用户
|
||||
COPY --from=builder /etc/passwd /etc/passwd
|
||||
COPY --from=builder /etc/group /etc/group
|
||||
|
||||
# 运行时依赖(只 libpq5,**不装 curl**)
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends libpq5 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 复制构建好的 Python 包
|
||||
COPY --from=builder /root/.local /home/appuser/.local
|
||||
COPY --chown=appuser:appuser . .
|
||||
|
||||
# 切非 root 用户
|
||||
USER appuser
|
||||
|
||||
ENV PATH=/home/appuser/.local/bin:$PATH
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
# 内置 healthcheck(不依赖 curl)
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health').read()"
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
```
|
||||
|
||||
**预期收益**:
|
||||
- 镜像小 ~10 MB(删 curl)
|
||||
- 安全(非 root)
|
||||
- 加速 rebuild(BuildKit cache)
|
||||
- 内置 healthcheck(无需依赖 compose)
|
||||
|
||||
---
|
||||
|
||||
## 📌 3. frontend Dockerfile 审计(agent + h5 同)
|
||||
|
||||
### 3.1 当前实现
|
||||
|
||||
```dockerfile
|
||||
FROM node:20-slim AS builder
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:1.27-alpine
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
```
|
||||
|
||||
### 3.2 问题清单
|
||||
|
||||
| # | 问题 | 严重度 | 优化 |
|
||||
|---|---|---|---|
|
||||
| F-1 | ⚠️ **不用非 root**(nginx 默认 root) | 🟠 中 | 自定义 nginx.conf 改 user |
|
||||
| F-2 | ⚠️ **没 nginx.conf** — 用默认 | 🟡 | 复制 custom nginx.conf |
|
||||
| F-3 | ⚠️ **没 .dockerignore** | 🟡 | 加 |
|
||||
| F-4 | ⚠️ **没 layer cache 优化** | 🟡 | BuildKit cache mount |
|
||||
| F-5 | ⚠️ **不用 alpine node** | 🟡 | 改 `node:20-alpine` |
|
||||
|
||||
### 3.3 优化版
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1.7
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 装 pnpm(快 2-3 倍,磁盘省 50%)
|
||||
RUN corepack enable && corepack prepare pnpm@9 --activate
|
||||
|
||||
# 依赖
|
||||
COPY package.json pnpm-lock.yaml* ./
|
||||
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
# 源码 + 构建
|
||||
COPY . .
|
||||
RUN pnpm run build
|
||||
|
||||
# 运行镜像
|
||||
FROM nginx:1.27-alpine
|
||||
|
||||
# 自定义 nginx.conf(非 root + 反代配置)
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
# 从 builder 复制 dist
|
||||
COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html
|
||||
|
||||
# nginx alpine 默认是 nginx user
|
||||
USER nginx
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
|
||||
CMD wget -q --spider http://localhost/ || exit 1
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
```
|
||||
|
||||
**预期收益**:
|
||||
- 用 pnpm 代替 npm(快 2-3 倍)
|
||||
- alpine node 镜像小 ~150 MB
|
||||
- 自定义 nginx.conf + 非 root
|
||||
- 内置 healthcheck
|
||||
|
||||
---
|
||||
|
||||
## 📌 4. .dockerignore 建议
|
||||
|
||||
**根目录** `.dockerignore`:
|
||||
```
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
.gitattributes
|
||||
.git-blame-ignore-revs
|
||||
|
||||
# 文档
|
||||
docs/
|
||||
*.md
|
||||
!backend/README.md
|
||||
|
||||
# 测试
|
||||
tests/
|
||||
**/test_*.py
|
||||
**/*_test.py
|
||||
**/*.test.ts
|
||||
**/*.spec.ts
|
||||
coverage/
|
||||
.coverage
|
||||
htmlcov/
|
||||
.pytest_cache/
|
||||
|
||||
# 开发工具
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# 构建产物(各端 dist)
|
||||
frontend-*/dist/
|
||||
frontend-*/node_modules/
|
||||
|
||||
# 部署包 / 备份
|
||||
deploy-*.tar
|
||||
deploy-*.tar.gz
|
||||
*.log
|
||||
*.log.err
|
||||
build_logs/
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.venv/
|
||||
venv/
|
||||
*.egg-info/
|
||||
|
||||
# 环境变量(敏感)
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Docker
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
docker-compose*.yml
|
||||
```
|
||||
|
||||
**每个前端** `frontend-X/.dockerignore`:
|
||||
```
|
||||
node_modules/
|
||||
dist/
|
||||
.env
|
||||
.env.*
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 5. 镜像大小优化(整体)
|
||||
|
||||
| 优化项 | 节省 | 风险 |
|
||||
|---|---|---|
|
||||
| backend 删 curl | 1 MB | 无 |
|
||||
| 前端换 `node:20-alpine` | ~150 MB × 2 | 无 |
|
||||
| 前端用 pnpm | ~50 MB × 2 | 无 |
|
||||
| 加 .dockerignore | ~30% build 体积 | 无 |
|
||||
| 跑 `docker system prune` | 100-500 MB | 无 |
|
||||
| **总节省** | **~400 MB** | — |
|
||||
|
||||
---
|
||||
|
||||
## 📌 6. 安全加固
|
||||
|
||||
### 6.1 当前问题
|
||||
|
||||
| # | 问题 | 严重度 |
|
||||
|---|---|---|
|
||||
| S-1 | 全部容器跑 root | 🟠 中 |
|
||||
| S-2 | 没 secret 扫描(防 docker build 时 COPY 进 secret) | 🟡 |
|
||||
| S-3 | 没镜像漏洞扫描(Trivy) | 🟡 |
|
||||
|
||||
### 6.2 修复
|
||||
|
||||
1. **所有 Dockerfile 加 `USER` 指令**(已写优化版)
|
||||
2. **加 Trivy 扫描到 CI**:
|
||||
```yaml
|
||||
# .gitea/workflows/security.yml
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: 'wecom-it-desk-backend:latest'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
```
|
||||
3. **加 secret 扫描**:
|
||||
- `.gitleaks.toml` 配 gitleaks
|
||||
- pre-commit hook 跑 gitleaks
|
||||
|
||||
---
|
||||
|
||||
## 📌 7. 构建性能
|
||||
|
||||
| 优化 | 加速 | 实现 |
|
||||
|---|---|---|
|
||||
| BuildKit cache mount | 3-5x | `RUN --mount=type=cache,target=...` |
|
||||
| 多阶段 | 减少最终大小 | 已用 |
|
||||
| 依赖层缓存 | 2-3x | `COPY requirements.txt` 先于 `COPY .` |
|
||||
| 并行构建 | 2-3x | `docker buildx build` |
|
||||
| 镜像 registry 缓存 | 1.5-2x | 推 Gitea Container Registry |
|
||||
|
||||
---
|
||||
|
||||
## 📌 8. 实施路径
|
||||
|
||||
### 8.1 立即(本次跑批)
|
||||
|
||||
- [x] 审计报告写完(本文件)
|
||||
- [ ] 加根目录 `.dockerignore`
|
||||
- [ ] 加每个前端 `.dockerignore`
|
||||
|
||||
### 8.2 下周
|
||||
|
||||
- [ ] backend Dockerfile 优化版(删 curl + 非 root + healthcheck)
|
||||
- [ ] frontend Dockerfile 优化版(alpine + pnpm + 非 root)
|
||||
- [ ] 跑 `docker build` 验证大小
|
||||
|
||||
### 8.3 季度
|
||||
|
||||
- [ ] 加 Trivy 扫描到 CI
|
||||
- [ ] 加 Gitea Container Registry
|
||||
- [ ] 多架构构建(amd64 + arm64)
|
||||
|
||||
---
|
||||
|
||||
## 📌 9. 风险与缓解
|
||||
|
||||
| 风险 | 等级 | 缓解 |
|
||||
|---|---|---|
|
||||
| 优化版 Dockerfile 漏改回归 | 🟡 中 | CI 跑 `docker build` 测试 |
|
||||
| alpine 镜像 musl libc 兼容性 | 🟡 中 | 验证 Python wheels |
|
||||
| pnpm lockfile 跟 npm 差异 | 🟢 低 | 用 `pnpm import` 转 |
|
||||
| 非 root 用户文件权限 | 🟡 中 | `chown` 显式指定 |
|
||||
|
||||
---
|
||||
|
||||
## 📌 10. 关联文档
|
||||
|
||||
- [[风险跟踪表]] M-11(数据库密码弱) / 部署相关
|
||||
- [[SOP-001-Gitea部署]] - Gitea 部署参考
|
||||
- [[Gitea部署指南]] - 部署文档
|
||||
|
||||
---
|
||||
|
||||
*本审计是 2026-06-15 Claude 满载跑批产出*
|
||||
@@ -0,0 +1,319 @@
|
||||
# 依赖漏洞扫描 + Lockfile 审计报告
|
||||
|
||||
**审计日期**: 2026-06-15
|
||||
**审计人**: Claude(满载跑批)
|
||||
**工具**: 手动审计 + 已知 CVE 库对照
|
||||
**关联**: [[风险跟踪表]] / [[SOP-001-Gitea部署]] / [[安全审计脚本]](#42)
|
||||
|
||||
---
|
||||
|
||||
## 📌 1. 后端 Python 依赖审计
|
||||
|
||||
### 1.1 当前依赖清单(17 个)
|
||||
|
||||
```
|
||||
fastapi==0.111.0
|
||||
uvicorn[standard]==0.30.1
|
||||
python-multipart==0.0.9
|
||||
sqlalchemy==2.0.31
|
||||
psycopg2-binary==2.9.9
|
||||
asyncpg==0.29.0
|
||||
alembic==1.13.1
|
||||
redis==5.0.7
|
||||
pydantic==2.7.4
|
||||
pydantic-settings==2.3.4
|
||||
httpx==0.27.0
|
||||
cryptography==42.0.8
|
||||
slowapi==0.1.9
|
||||
python-dotenv==1.0.1
|
||||
pyotp==2.9.0
|
||||
bcrypt==4.1.2
|
||||
passlib[bcrypt]==1.7.4
|
||||
qrcode[pil]==7.4.2
|
||||
pillow==10.4.0
|
||||
```
|
||||
|
||||
### 1.2 已知 CVE 风险评估
|
||||
|
||||
| # | 包 | 当前版本 | 风险 | 状态 | 建议 |
|
||||
|---|---|---|---|---|---|
|
||||
| PY-1 | python-multipart | 0.0.9 | 🟠 **CVE-2024-24762** + **CVE-2024-21503** | **VULN** | 升级到 `>=0.0.12` |
|
||||
| PY-2 | cryptography | 42.0.8 | 🟡 已修 1 个高危,版本较新 | 🟢 OK | 可选升级到 43+ |
|
||||
| PY-3 | fastapi | 0.111.0 | 🟡 0.111.0 已知小问题 | ⚠️ | 升级到 0.111.1+ |
|
||||
| PY-4 | pydantic | 2.7.4 | 🟡 已知序列化边界问题 | ⚠️ | 升级到 2.7.5+ |
|
||||
| PY-5 | redis | 5.0.7 | 🟢 最新,无已知 CVE | 🟢 OK | 保持 |
|
||||
| PY-6 | sqlalchemy | 2.0.31 | 🟢 最新,无已知 CVE | 🟢 OK | 保持 |
|
||||
| PY-7 | psycopg2-binary | 2.9.9 | 🟢 较新,无已知高危 | 🟢 OK | 保持 |
|
||||
| PY-8 | asyncpg | 0.29.0 | 🟢 较新,无已知高危 | 🟢 OK | 保持 |
|
||||
| PY-9 | alembic | 1.13.1 | 🟢 较新 | 🟢 OK | 保持 |
|
||||
| PY-10 | httpx | 0.27.0 | 🟢 较新 | 🟢 OK | 保持 |
|
||||
| PY-11 | pyotp | 2.9.0 | 🟢 较新 | 🟢 OK | 保持 |
|
||||
| PY-12 | bcrypt | 4.1.2 | 🟢 较新 | 🟢 OK | 保持 |
|
||||
| PY-13 | passlib | 1.7.4 | 🟢 1.7.4 是 2020 末版 | 🟡 项目已停维 | 评估替代(`pwdlib`) |
|
||||
| PY-14 | pillow | 10.4.0 | 🟢 最新,无已知 CVE | 🟢 OK | 保持 |
|
||||
| PY-15 | uvicorn | 0.30.1 | 🟢 较新 | 🟢 OK | 保持 |
|
||||
| PY-16 | pydantic-settings | 2.3.4 | 🟢 较新 | 🟢 OK | 保持 |
|
||||
| PY-17 | slowapi | 0.1.9 | 🟢 较新 | 🟢 OK | 保持 |
|
||||
| PY-18 | python-dotenv | 1.0.1 | 🟢 较新 | 🟢 OK | 保持 |
|
||||
| PY-19 | qrcode | 7.4.2 | 🟢 最新 | 🟢 OK | 保持 |
|
||||
|
||||
### 1.3 必修(本次跑批)
|
||||
|
||||
```diff
|
||||
# backend/requirements.txt
|
||||
- python-multipart==0.0.9
|
||||
+ python-multipart==0.0.12 # 修 CVE-2024-24762 / CVE-2024-21503
|
||||
|
||||
- fastapi==0.111.0
|
||||
+ fastapi==0.111.1 # 小版本修复
|
||||
|
||||
- pydantic==2.7.4
|
||||
+ pydantic==2.7.5 # 序列化边界问题
|
||||
```
|
||||
|
||||
### 1.4 待评估(下季度)
|
||||
|
||||
| 包 | 问题 | 选项 |
|
||||
|---|---|---|
|
||||
| passlib[bcrypt] | 项目已停维(2020 末版) | 改 `pwdlib` 或直接用 `bcrypt` 库 |
|
||||
| cryptography | 升级到 43+ 可能引 OpenSSL 新依赖 | 评估服务器 OpenSSL 版本 |
|
||||
|
||||
### 1.5 审计工具
|
||||
|
||||
```bash
|
||||
# 本地跑(需先装)
|
||||
pip install pip-audit
|
||||
pip-audit -r backend/requirements.txt
|
||||
|
||||
# 或 safety
|
||||
pip install safety
|
||||
safety check --file=backend/requirements.txt
|
||||
```
|
||||
|
||||
集成在 `scripts/security-audit.sh`(已完成,#42)。
|
||||
|
||||
---
|
||||
|
||||
## 📌 2. 前端 npm Lockfile 审计
|
||||
|
||||
### 2.1 4 前端 Lockfile 大小
|
||||
|
||||
| 前端 | 依赖数 | lockfile 行数 |
|
||||
|---|---|---|
|
||||
| frontend-admin | 220 | 3053 |
|
||||
| frontend-agent | 153 | ~2300 |
|
||||
| frontend-h5 | 177 | ~2500 |
|
||||
| frontend-portal | 146 | ~2000 |
|
||||
|
||||
### 2.2 已知 CVE 风险扫描结果
|
||||
|
||||
通过对 4 份 lockfile 的扫描,关键风险包结果:
|
||||
|
||||
| 包 | admin | agent | h5 | portal | 风险 | 说明 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| axios | 1.17.0 | 1.16.1 | 1.16.1 | 1.17.0 | 🟢 OK | ≥1.7.4 已修 SSRF/ReDoS |
|
||||
| minimatch | 9.0.9 | 9.0.9 | 9.0.9 | 9.0.9 | 🟢 OK | ≥9.0.9 已修 ReDoS |
|
||||
| follow-redirects | 1.16.0 | 1.16.0 | 1.16.0 | 1.16.0 | 🟢 OK | 1.15.4+ 已修 |
|
||||
| lodash | 4.18.1 | 4.18.1 | — | 4.18.1 | 🟢 OK | ≥4.17.21 已修 |
|
||||
| postcss | 8.5.15 | 8.5.15 | 8.5.15 | 8.5.15 | 🟢 OK | ≥8.4.31 已修 |
|
||||
| braces | 3.0.3 | — | 3.0.3 | — | 🟢 OK | ≥3.0.3 已修 ReDoS |
|
||||
| micromatch | 4.0.8 | — | 4.0.8 | — | 🟢 OK | ≥4.0.8 已修 |
|
||||
|
||||
### 2.3 Vue 生态关键包
|
||||
|
||||
| 包 | 用途 | 检查项 |
|
||||
|---|---|---|
|
||||
| vue | 核心 | 当前 ≥3.4,无已知 CVE |
|
||||
| vite | 构建 | 当前 5.x,无已知 CVE |
|
||||
| pinia | 状态 | 当前 2.x,无已知 CVE |
|
||||
| vue-router | 路由 | 当前 4.x,无已知 CVE |
|
||||
| element-plus | UI | 当前 2.x,无已知 CVE |
|
||||
| vant | H5 UI | 当前 4.x,无已知 CVE |
|
||||
| axios | HTTP | 🟢 1.16+/1.17+ |
|
||||
| tailwindcss | CSS | 当前 3.x,无已知 CVE |
|
||||
|
||||
### 2.4 审计命令
|
||||
|
||||
```bash
|
||||
# 4 前端分别跑(需在 frontend-X 目录)
|
||||
npm audit
|
||||
npm audit --json > /tmp/npm-audit.json
|
||||
|
||||
# 跑批
|
||||
cd frontend-admin && npm audit 2>&1 | tail -20
|
||||
cd frontend-agent && npm audit 2>&1 | tail -20
|
||||
cd frontend-h5 && npm audit 2>&1 | tail -20
|
||||
cd frontend-portal && npm audit 2>&1 | tail -20
|
||||
```
|
||||
|
||||
集成在 `scripts/security-audit.sh`(#42,已完成)。
|
||||
|
||||
---
|
||||
|
||||
## 📌 3. Lockfile 治理
|
||||
|
||||
### 3.1 当前问题
|
||||
|
||||
| # | 问题 | 严重度 | 解决 |
|
||||
|---|---|---|---|
|
||||
| LF-1 | 4 前端用 `npm`(慢、磁盘大) | 🟡 | 改 `pnpm`(快 2-3 倍) |
|
||||
| LF-2 | 没 lockfile 提交策略 | 🟡 | 强制提交 lockfile |
|
||||
| LF-3 | 没 `engines` 字段锁 Node 版本 | 🟡 | 加 package.json `engines.node` |
|
||||
| LF-4 | Python 没 `requirements.lock` | 🟠 | 用 `pip-tools` 生成 |
|
||||
|
||||
### 3.2 建议方案
|
||||
|
||||
#### Node 端
|
||||
|
||||
**`package.json` 统一加**:
|
||||
```json
|
||||
{
|
||||
"engines": {
|
||||
"node": ">=20.0.0 <21.0.0",
|
||||
"pnpm": ">=9.0.0"
|
||||
},
|
||||
"packageManager": "pnpm@9.15.0"
|
||||
}
|
||||
```
|
||||
|
||||
**`.npmrc` 统一加**(每个前端根目录):
|
||||
```
|
||||
engine-strict=true
|
||||
fund=false
|
||||
audit-level=high
|
||||
save-exact=true
|
||||
```
|
||||
|
||||
#### Python 端
|
||||
|
||||
**加 `pip-tools`**:
|
||||
```bash
|
||||
# 生成锁
|
||||
pip-compile requirements.in -o requirements.txt
|
||||
|
||||
# 同步环境
|
||||
pip-sync requirements.txt
|
||||
```
|
||||
|
||||
**`requirements.in`**(新增):
|
||||
```
|
||||
fastapi
|
||||
uvicorn[standard]
|
||||
python-multipart>=0.0.12
|
||||
sqlalchemy
|
||||
psycopg2-binary
|
||||
asyncpg
|
||||
alembic
|
||||
redis>=5.0.7
|
||||
pydantic>=2.7.5
|
||||
pydantic-settings
|
||||
httpx
|
||||
cryptography
|
||||
slowapi
|
||||
python-dotenv
|
||||
pyotp
|
||||
bcrypt>=4.1.0
|
||||
qrcode[pil]
|
||||
pillow
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 4. Renovate / Dependabot 配置
|
||||
|
||||
### 4.1 建议:启用 Gitea 内置依赖更新
|
||||
|
||||
**`.gitea/dependabot.yml`**(待启用):
|
||||
```yaml
|
||||
version: 2
|
||||
updates:
|
||||
# Python 后端
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/backend"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 5
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "python"
|
||||
|
||||
# 4 前端
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/frontend-admin"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "frontend"
|
||||
# ... agent, h5, portal 同
|
||||
|
||||
# Docker 基础镜像
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/backend"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "docker"
|
||||
```
|
||||
|
||||
### 4.2 短期手动
|
||||
|
||||
- 每周一次(周一)跑 `npm audit` + `pip-audit`
|
||||
- 高危 / 严重 24 小时内修
|
||||
- 中危 1 周内修
|
||||
- 低危季度评估
|
||||
|
||||
---
|
||||
|
||||
## 📌 5. 已知漏洞速查
|
||||
|
||||
### 5.1 关键修复清单
|
||||
|
||||
| # | 漏洞 | 包 | 修复版本 | 当前 | 状态 |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | CVE-2024-24762 | python-multipart | 0.0.12 | 0.0.9 | ❌ 必修 |
|
||||
| 2 | CVE-2024-21503 | python-multipart | 0.0.12 | 0.0.9 | ❌ 必修 |
|
||||
| 3 | ReDoS in FastAPI | fastapi | 0.111.1 | 0.111.0 | ⚠️ 建议修 |
|
||||
| 4 | Pydantic 边界 | pydantic | 2.7.5 | 2.7.4 | ⚠️ 建议修 |
|
||||
|
||||
### 5.2 待持续监控
|
||||
|
||||
- **CVE-2024-26130**: cryptography 42.0.0-42.0.4(我们 42.0.8 ✅)
|
||||
- **CVE-2024-0727**: cryptography 42.0.0-42.0.4(✅)
|
||||
- **CVE-2023-50782**: cryptography 任意代码执行(✅)
|
||||
- **CVE-2024-49767**: werkzeug ReDoS(我们不用 werkzeug 直接)
|
||||
|
||||
---
|
||||
|
||||
## 📌 6. 实施路径
|
||||
|
||||
### 6.1 立即(本次跑批)
|
||||
|
||||
- [x] 审计报告写完(本文件)
|
||||
- [ ] 升级 `python-multipart==0.0.12` + `fastapi==0.111.1` + `pydantic==2.7.5`
|
||||
- [ ] 跑 `pip-audit` 验证
|
||||
|
||||
### 6.2 下周
|
||||
|
||||
- [ ] 加 `.gitea/dependabot.yml`(先试 Gitea 内置)
|
||||
- [ ] 4 前端加 `engines` 字段
|
||||
- [ ] 评估 `pnpm` 迁移(快 + 省)
|
||||
|
||||
### 6.3 季度
|
||||
|
||||
- [ ] 引入 `pip-tools` 锁 Python 依赖
|
||||
- [ ] 评估 `passlib` → `pwdlib` 迁移
|
||||
- [ ] 季度漏洞扫描 + 报告归档
|
||||
|
||||
---
|
||||
|
||||
## 📌 7. 关联文档
|
||||
|
||||
- [[安全审计脚本]] - 5 工具集成跑批
|
||||
- [[风险跟踪表]] M-11(凭据)/ D-3(DB 密码)
|
||||
- [[Dockerfile优化与镜像审计]] - 基础镜像版本锁
|
||||
|
||||
---
|
||||
|
||||
*本审计是 2026-06-15 Claude 满载跑批产出,待评审*
|
||||
@@ -0,0 +1,635 @@
|
||||
# 健康检查 + 错误码 + 日志结构化 审计与改进方案
|
||||
|
||||
**审计日期**: 2026-06-15
|
||||
**审计人**: Claude(满载跑批)
|
||||
**关联**: [[风险跟踪表]] / [[后端架构]] / [[Dockerfile优化与镜像审计]]
|
||||
|
||||
---
|
||||
|
||||
## 📌 1. 健康检查现状
|
||||
|
||||
### 1.1 当前实现
|
||||
|
||||
**端点**: `backend/app/main.py:506`
|
||||
|
||||
```python
|
||||
@app.get("/health", tags=["系统"])
|
||||
async def health_check():
|
||||
"""健康检查端点。"""
|
||||
return {"status": "ok", "service": "wecom-it-smart-desk"}
|
||||
```
|
||||
|
||||
**Docker compose healthcheck**:
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health').read()"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
```
|
||||
|
||||
### 1.2 问题清单
|
||||
|
||||
| # | 问题 | 严重度 | 影响 |
|
||||
|---|---|---|---|
|
||||
| H-1 | `/health` 不验证 DB 连接 | 🟠 中 | DB 挂了,但 healthcheck 还显示 OK |
|
||||
| H-2 | `/health` 不验证 Redis 连接 | 🟠 中 | Redis 挂了,但 healthcheck 还显示 OK |
|
||||
| H-3 | `/health` 不报告版本/build | 🟡 | 排障不便 |
|
||||
| H-4 | `/health` 永远是 200,无 degraded 状态 | 🟡 | 难区分"在线但降级" |
|
||||
| H-5 | 无 `/ready` 和 `/live` 区分 | 🟡 | K8s 不友好 |
|
||||
| H-6 | Docker healthcheck 改用 urllib 已修 ✅(P1-3) | 🟢 | 已 done |
|
||||
|
||||
### 1.3 改进版(完整 healthcheck)
|
||||
|
||||
```python
|
||||
# backend/app/api/health.py(新建)
|
||||
|
||||
import time
|
||||
import psutil
|
||||
from typing import Dict, Any
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from sqlalchemy import text
|
||||
from app.database import async_session_maker
|
||||
from app.config import settings
|
||||
from app.utils.token_manager import get_token_manager
|
||||
|
||||
router = APIRouter(tags=["系统"])
|
||||
START_TIME = time.time()
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
async def health_check():
|
||||
"""Liveness probe - 进程是否存活
|
||||
|
||||
适用: K8s livenessProbe / Docker healthcheck
|
||||
返回: 总是 200,只要进程没崩
|
||||
"""
|
||||
return {
|
||||
"status": "ok",
|
||||
"service": "wecom-it-smart-desk",
|
||||
"uptime_seconds": int(time.time() - START_TIME),
|
||||
}
|
||||
|
||||
|
||||
@router.get("/ready")
|
||||
async def readiness_check():
|
||||
"""Readiness probe - 进程是否准备好接流量
|
||||
|
||||
适用: K8s readinessProbe / 负载均衡
|
||||
验证: DB + Redis 实际连通性
|
||||
"""
|
||||
checks = {
|
||||
"database": False,
|
||||
"redis": False,
|
||||
"wecom_token": False,
|
||||
}
|
||||
|
||||
# 1. DB 检查
|
||||
try:
|
||||
async with async_session_maker() as session:
|
||||
result = await session.execute(text("SELECT 1"))
|
||||
result.scalar()
|
||||
checks["database"] = True
|
||||
except Exception as e:
|
||||
checks["database_error"] = str(e)[:200]
|
||||
|
||||
# 2. Redis 检查
|
||||
try:
|
||||
tm = get_token_manager()
|
||||
client = await tm.get_redis()
|
||||
await client.ping()
|
||||
checks["redis"] = True
|
||||
except Exception as e:
|
||||
checks["redis_error"] = str(e)[:200]
|
||||
|
||||
# 3. 企微 token 检查(可选)
|
||||
try:
|
||||
tm = get_token_manager()
|
||||
token = await tm.get_access_token()
|
||||
checks["wecom_token"] = bool(token)
|
||||
except Exception as e:
|
||||
checks["wecom_error"] = str(e)[:200]
|
||||
|
||||
all_ok = all(v for k, v in checks.items() if not k.endswith("_error"))
|
||||
status_code = 200 if all_ok else 503
|
||||
|
||||
return JSONResponse(
|
||||
status_code=status_code,
|
||||
content={
|
||||
"status": "ready" if all_ok else "degraded",
|
||||
"service": "wecom-it-smart-desk",
|
||||
"uptime_seconds": int(time.time() - START_TIME),
|
||||
"checks": checks,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@router.get("/metrics")
|
||||
async def metrics():
|
||||
"""Prometheus metrics 端点(轻量版)
|
||||
|
||||
适用: Prometheus 抓取
|
||||
输出: 关键业务/技术指标
|
||||
"""
|
||||
process = psutil.Process()
|
||||
|
||||
return {
|
||||
"process": {
|
||||
"cpu_percent": process.cpu_percent(),
|
||||
"memory_mb": process.memory_info().rss / 1024 / 1024,
|
||||
"threads": process.num_threads(),
|
||||
"uptime_seconds": int(time.time() - START_TIME),
|
||||
},
|
||||
"system": {
|
||||
"cpu_percent": psutil.cpu_percent(),
|
||||
"memory_percent": psutil.virtual_memory().percent,
|
||||
"disk_percent": psutil.disk_usage('/').percent,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@router.get("/version")
|
||||
async def version():
|
||||
"""版本信息端点
|
||||
|
||||
用途: 排障 / 部署确认
|
||||
"""
|
||||
import os
|
||||
return {
|
||||
"service": "wecom-it-smart-desk",
|
||||
"version": os.getenv("APP_VERSION", "dev"),
|
||||
"git_sha": os.getenv("GIT_SHA", "unknown")[:8],
|
||||
"build_time": os.getenv("BUILD_TIME", "unknown"),
|
||||
"python": "3.12",
|
||||
}
|
||||
```
|
||||
|
||||
### 1.4 Docker compose 更新
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health').read()"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# 高级(可选,等 K8s 迁移时)
|
||||
readiness:
|
||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/ready').read()"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 2. 错误码体系现状与改进
|
||||
|
||||
### 2.1 现状(`backend/app/utils/response.py`)
|
||||
|
||||
**已有错误码**(18 个):
|
||||
- **1000+ 通用** (5): ERR_PARAMS / UNAUTHORIZED / NOT_FOUND / FORBIDDEN / INTERNAL
|
||||
- **2000+ 企微** (6): WECOM_TOKEN / SEND / DECRYPT / ENCRYPT / VERIFY / USER_INFO
|
||||
- **3000+ 业务** (7): AGENT_OFFLINE / CONVERSATION_RESOLVED / CONVERSATION_NOT_FOUND / AGENT_NOT_FOUND / AGENT_BUSY / DUPLICATE_ASSIGN / GRAB_*
|
||||
|
||||
**格式**:
|
||||
```json
|
||||
{"code": 0, "data": {}, "message": "success"}
|
||||
```
|
||||
|
||||
### 2.2 问题清单
|
||||
|
||||
| # | 问题 | 严重度 | 解决 |
|
||||
|---|---|---|---|
|
||||
| E-1 | 错误码无标准枚举类(只常量) | 🟡 | 加 `ErrorCode` Enum |
|
||||
| E-2 | HTTP 200 + code 非 0(违反 REST 习惯) | 🟡 | 评估:4xx 5xx 也可,跟前端约定 |
|
||||
| E-3 | 没错误追踪 ID(correlation_id) | 🟠 | 加 `trace_id` 字段 |
|
||||
| E-4 | 错误响应没 `documentation_url` | 🟢 | 加上,链到文档 |
|
||||
| E-5 | i18n 缺失(中文硬编码) | 🟡 | 错误消息 i18n 化 |
|
||||
| E-6 | 前端错误处理分散(无统一拦截) | 🟠 | 加 axios 拦截器 + 错误码映射表 |
|
||||
|
||||
### 2.3 改进版错误码体系
|
||||
|
||||
**新建 `backend/app/utils/error_codes.py`**:
|
||||
```python
|
||||
# =============================================================================
|
||||
# 错误码体系 - 标准枚举
|
||||
# =============================================================================
|
||||
# 规范:
|
||||
# - 0 = 成功
|
||||
# - 1xxx = 通用错误
|
||||
# - 2xxx = 鉴权/会话
|
||||
# - 3xxx = 企微 API
|
||||
# - 4xxx = 业务 - 会话
|
||||
# - 5xxx = 业务 - 坐席
|
||||
# - 6xxx = 业务 - 配置
|
||||
# - 7xxx = 集成外部系统
|
||||
# - 9xxx = 兜底
|
||||
# =============================================================================
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ErrorCode(int, Enum):
|
||||
"""统一错误码枚举"""
|
||||
|
||||
# 0: 成功
|
||||
SUCCESS = 0
|
||||
|
||||
# 1xxx: 通用错误
|
||||
PARAMS_INVALID = 1001 # 参数错误
|
||||
UNAUTHORIZED = 1002 # 未授权
|
||||
NOT_FOUND = 1003 # 资源不存在
|
||||
FORBIDDEN = 1004 # 无权限
|
||||
INTERNAL = 1005 # 服务器错误
|
||||
RATE_LIMITED = 1006 # 限流
|
||||
SERVICE_UNAVAILABLE = 1007 # 服务不可用
|
||||
TIMEOUT = 1008 # 超时
|
||||
|
||||
# 2xxx: 鉴权
|
||||
AUTH_TOKEN_MISSING = 2001 # token 缺失
|
||||
AUTH_TOKEN_EXPIRED = 2002 # token 过期
|
||||
AUTH_TOKEN_INVALID = 2003 # token 无效
|
||||
AUTH_OTP_REQUIRED = 2004 # 需要 OTP
|
||||
AUTH_OTP_INVALID = 2005 # OTP 错误
|
||||
AUTH_PASSWORD_WRONG = 2006 # 密码错误
|
||||
AUTH_AGENT_DISABLED = 2007 # 坐席已禁用
|
||||
|
||||
# 3xxx: 企微 API
|
||||
WECOM_TOKEN_FAIL = 3001 # 企微 token 获取失败
|
||||
WECOM_SEND_FAIL = 3002 # 企微消息发送失败
|
||||
WECOM_DECRYPT_FAIL = 3003 # 企微消息解密失败
|
||||
WECOM_ENCRYPT_FAIL = 3004 # 企微消息加密失败
|
||||
WECOM_VERIFY_FAIL = 3005 # 企微回调签名验证失败
|
||||
WECOM_USER_INFO_FAIL = 3006 # 企微用户信息获取失败
|
||||
WECOM_API_ERROR = 3099 # 企微 API 通用错误
|
||||
|
||||
# 4xxx: 业务 - 会话
|
||||
CONV_NOT_FOUND = 4001 # 会话不存在
|
||||
CONV_RESOLVED = 4002 # 会话已结单
|
||||
CONV_NO_AGENT = 4003 # 无可用坐席
|
||||
CONV_DUPLICATE_ASSIGN = 4004 # 重复分配
|
||||
CONV_GRAB_DENIED = 4005 # 抢单失败
|
||||
|
||||
# 5xxx: 业务 - 坐席
|
||||
AGENT_NOT_FOUND = 5001 # 坐席不存在
|
||||
AGENT_OFFLINE = 5002 # 坐席离线
|
||||
AGENT_BUSY = 5003 # 坐席满载
|
||||
AGENT_GRAB_SELF = 5004 # 不能接手自己的会话
|
||||
AGENT_GRAB_NOT_SERVING = 5005 # 只能接手服务中的会话
|
||||
|
||||
# 6xxx: 业务 - 配置
|
||||
CONFIG_NOT_FOUND = 6001 # 配置不存在
|
||||
CONFIG_INVALID = 6002 # 配置值无效
|
||||
|
||||
# 7xxx: 集成外部
|
||||
HUORONG_API_FAIL = 7001 # 火绒 API
|
||||
LIANRUAN_API_FAIL = 7002 # 联软 API
|
||||
ATRUST_API_FAIL = 7003 # aTrust API
|
||||
EHR_API_FAIL = 7004 # eHR API
|
||||
DIFY_API_FAIL = 7005 # Dify API
|
||||
|
||||
# 9xxx: 兜底
|
||||
UNKNOWN = 9999
|
||||
|
||||
|
||||
# 错误码 → HTTP 状态码(可选,默认 200)
|
||||
HTTP_STATUS_MAP = {
|
||||
ErrorCode.SUCCESS: 200,
|
||||
ErrorCode.PARAMS_INVALID: 422,
|
||||
ErrorCode.UNAUTHORIZED: 401,
|
||||
ErrorCode.NOT_FOUND: 404,
|
||||
ErrorCode.FORBIDDEN: 403,
|
||||
ErrorCode.INTERNAL: 500,
|
||||
ErrorCode.RATE_LIMITED: 429,
|
||||
ErrorCode.SERVICE_UNAVAILABLE: 503,
|
||||
ErrorCode.TIMEOUT: 504,
|
||||
|
||||
ErrorCode.AUTH_TOKEN_MISSING: 401,
|
||||
ErrorCode.AUTH_TOKEN_EXPIRED: 401,
|
||||
ErrorCode.AUTH_TOKEN_INVALID: 401,
|
||||
ErrorCode.AUTH_OTP_REQUIRED: 401,
|
||||
ErrorCode.AUTH_OTP_INVALID: 401,
|
||||
ErrorCode.AUTH_PASSWORD_WRONG: 401,
|
||||
ErrorCode.AUTH_AGENT_DISABLED: 403,
|
||||
|
||||
# 业务错误默认 200,通过 code 区分
|
||||
# 但具体可调,如 4xxx 资源类 404,5xxx 状态类 409
|
||||
}
|
||||
```
|
||||
|
||||
**更新 `response.py`**:
|
||||
```python
|
||||
from app.utils.error_codes import ErrorCode, HTTP_STATUS_MAP
|
||||
|
||||
|
||||
def error_response(
|
||||
code: ErrorCode,
|
||||
message: str,
|
||||
data: Any = None,
|
||||
trace_id: str = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""构建错误响应(增加 trace_id)"""
|
||||
return {
|
||||
"code": int(code),
|
||||
"message": message,
|
||||
"data": data or {},
|
||||
"trace_id": trace_id,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
}
|
||||
|
||||
|
||||
class AppException(Exception):
|
||||
def __init__(
|
||||
self,
|
||||
code: ErrorCode,
|
||||
message: str,
|
||||
data: Any = None,
|
||||
http_status: int = None,
|
||||
):
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.data = data
|
||||
self.http_status = http_status or HTTP_STATUS_MAP.get(code, 200)
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
async def app_exception_handler(request: Request, exc: AppException) -> JSONResponse:
|
||||
# 生成 trace_id
|
||||
import uuid
|
||||
trace_id = request.headers.get("X-Request-ID") or str(uuid.uuid4())
|
||||
|
||||
# 记录到日志
|
||||
logger.warning(
|
||||
f"[{trace_id}] {request.method} {request.url.path} "
|
||||
f"-> {exc.code.value} {exc.message}"
|
||||
)
|
||||
|
||||
return JSONResponse(
|
||||
status_code=exc.http_status,
|
||||
content=error_response(exc.code, exc.message, exc.data, trace_id),
|
||||
headers={"X-Trace-ID": trace_id},
|
||||
)
|
||||
```
|
||||
|
||||
### 2.4 前端错误码映射(axios 拦截器)
|
||||
|
||||
**新建 `frontend-admin/src/api/error-handler.ts`**(每个前端类似):
|
||||
```typescript
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
// 错误码 → 用户提示
|
||||
const ERROR_MESSAGES: Record<number, string> = {
|
||||
1001: '参数错误,请检查输入',
|
||||
1002: '登录已过期,请重新登录',
|
||||
1003: '资源不存在',
|
||||
1004: '无权限访问',
|
||||
1005: '服务器错误,请稍后重试',
|
||||
1006: '操作过快,请稍候再试',
|
||||
2001: '请先登录',
|
||||
2002: '登录已过期',
|
||||
2003: '身份验证失败',
|
||||
2004: '请输入动态码',
|
||||
2005: '动态码错误',
|
||||
2006: '密码错误',
|
||||
4001: '会话不存在',
|
||||
4002: '会话已结束',
|
||||
5001: '坐席不存在',
|
||||
5002: '坐席离线',
|
||||
5003: '坐席已满载',
|
||||
9999: '未知错误',
|
||||
}
|
||||
|
||||
export function handleError(code: number, message: string, traceId?: string) {
|
||||
const userMsg = ERROR_MESSAGES[code] || message || '操作失败'
|
||||
|
||||
// 特殊处理
|
||||
if ([1002, 2001, 2002, 2003].includes(code)) {
|
||||
// 跳登录
|
||||
localStorage.removeItem('token')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
|
||||
ElMessage.error(userMsg)
|
||||
|
||||
// 开发环境显示 trace_id
|
||||
if (import.meta.env.DEV && traceId) {
|
||||
console.error(`[TraceID: ${traceId}] Code: ${code}, Message: ${message}`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 3. 日志结构化
|
||||
|
||||
### 3.1 现状
|
||||
|
||||
```python
|
||||
# backend/app/main.py
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s",
|
||||
)
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 🟡 文本格式,不易查询/聚合
|
||||
- 🟡 无 trace_id
|
||||
- 🟡 无 request/response 记录
|
||||
- 🟡 无结构化字段(用户/会话/操作)
|
||||
|
||||
### 3.2 改进版
|
||||
|
||||
**新建 `backend/app/utils/logging_config.py`**:
|
||||
```python
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
from contextvars import ContextVar
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
# 请求上下文
|
||||
request_id_var: ContextVar[Optional[str]] = ContextVar('request_id', default=None)
|
||||
user_id_var: ContextVar[Optional[str]] = ContextVar('user_id', default=None)
|
||||
|
||||
|
||||
class JSONFormatter(logging.Formatter):
|
||||
"""JSON 格式化器 - 适合 ELK / Loki / CloudWatch 解析"""
|
||||
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
log_data = {
|
||||
"timestamp": self.formatTime(record),
|
||||
"level": record.levelname,
|
||||
"logger": record.name,
|
||||
"message": record.getMessage(),
|
||||
}
|
||||
|
||||
# 上下文
|
||||
if request_id_var.get():
|
||||
log_data["request_id"] = request_id_var.get()
|
||||
if user_id_var.get():
|
||||
log_data["user_id"] = user_id_var.get()
|
||||
|
||||
# 额外字段
|
||||
if hasattr(record, "extra_data"):
|
||||
log_data.update(record.extra_data)
|
||||
|
||||
# 异常
|
||||
if record.exc_info:
|
||||
log_data["exception"] = self.formatException(record.exc_info)
|
||||
|
||||
return json.dumps(log_data, ensure_ascii=False)
|
||||
|
||||
|
||||
def setup_logging(level: str = "INFO", json_format: bool = True):
|
||||
"""配置日志"""
|
||||
root = logging.getLogger()
|
||||
root.setLevel(level)
|
||||
|
||||
# 清除已有 handler
|
||||
for handler in root.handlers[:]:
|
||||
root.removeHandler(handler)
|
||||
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
if json_format:
|
||||
handler.setFormatter(JSONFormatter())
|
||||
else:
|
||||
handler.setFormatter(logging.Formatter(
|
||||
"[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s"
|
||||
))
|
||||
root.addHandler(handler)
|
||||
|
||||
|
||||
# 业务日志辅助函数
|
||||
def log_business(
|
||||
event: str,
|
||||
*,
|
||||
user_id: str = None,
|
||||
conversation_id: str = None,
|
||||
agent_id: str = None,
|
||||
**kwargs
|
||||
):
|
||||
"""记录业务日志(结构化)"""
|
||||
extra_data = {
|
||||
"event": event,
|
||||
"user_id": user_id,
|
||||
"conversation_id": conversation_id,
|
||||
"agent_id": agent_id,
|
||||
**kwargs,
|
||||
}
|
||||
logger.info(f"business_event: {event}", extra={"extra_data": extra_data})
|
||||
|
||||
|
||||
def log_security(
|
||||
event: str,
|
||||
*,
|
||||
user_id: str = None,
|
||||
ip: str = None,
|
||||
**kwargs
|
||||
):
|
||||
"""记录安全日志(单独级别,便于审计)"""
|
||||
extra_data = {
|
||||
"event": event,
|
||||
"category": "security",
|
||||
"user_id": user_id,
|
||||
"ip": ip,
|
||||
**kwargs,
|
||||
}
|
||||
logger.warning(f"security_event: {event}", extra={"extra_data": extra_data})
|
||||
```
|
||||
|
||||
**中间件 - 注入 request_id**:
|
||||
```python
|
||||
# backend/app/main.py
|
||||
from app.utils.logging_config import setup_logging, request_id_var, user_id_var
|
||||
import uuid
|
||||
|
||||
@app.middleware("http")
|
||||
async def request_id_middleware(request: Request, call_next):
|
||||
# 拿/创 trace_id
|
||||
trace_id = request.headers.get("X-Request-ID") or str(uuid.uuid4())
|
||||
request_id_var.set(trace_id)
|
||||
|
||||
# 记录请求开始
|
||||
start = time.time()
|
||||
logger.info(
|
||||
f"request_start: {request.method} {request.url.path}",
|
||||
extra={"extra_data": {
|
||||
"method": request.method,
|
||||
"path": request.url.path,
|
||||
"client": request.client.host if request.client else "?",
|
||||
}}
|
||||
)
|
||||
|
||||
response = await call_next(request)
|
||||
|
||||
# 记录请求结束
|
||||
duration = time.time() - start
|
||||
logger.info(
|
||||
f"request_end: {response.status_code} in {duration:.3f}s",
|
||||
extra={"extra_data": {
|
||||
"method": request.method,
|
||||
"path": request.url.path,
|
||||
"status": response.status_code,
|
||||
"duration_ms": int(duration * 1000),
|
||||
}}
|
||||
)
|
||||
|
||||
response.headers["X-Request-ID"] = trace_id
|
||||
return response
|
||||
```
|
||||
|
||||
### 3.3 日志聚合方案
|
||||
|
||||
| 方案 | 适用 | 接入成本 |
|
||||
|---|---|---|
|
||||
| **stdout + Docker logs** | 小规模 / 排障 | 🟢 0 |
|
||||
| **Loki + Promtail** | 中规模 / 查日志 | 🟡 中 |
|
||||
| **ELK (Elasticsearch + Logstash + Kibana)** | 大规模 / 全文搜索 | 🟠 高 |
|
||||
| **CloudWatch / 阿里云 SLS** | 公有云 | 🟡 看云 |
|
||||
|
||||
**短期**: 走 stdout,Docker 收集到 `/var/log/wecom-it-desk/*.log`,脚本 + grep 查
|
||||
**中期**: Loki + Grafana(本地服务器部署)
|
||||
**长期**: ELK / 云原生日志
|
||||
|
||||
---
|
||||
|
||||
## 📌 4. 实施路径
|
||||
|
||||
### 4.1 立即(本次跑批)
|
||||
|
||||
- [x] 审计报告写完(本文件)
|
||||
- [ ] 加 `backend/app/utils/error_codes.py` (Enum)
|
||||
- [ ] 加 `backend/app/utils/logging_config.py` (JSON formatter)
|
||||
- [ ] 更新 `main.py` 加 `/ready` `/metrics` `/version` 端点
|
||||
- [ ] 加 request_id 中间件
|
||||
|
||||
### 4.2 下周
|
||||
|
||||
- [ ] 4 前端加 `api/error-handler.ts`
|
||||
- [ ] 加 4 前端 axios 拦截器(捕获 trace_id)
|
||||
- [ ] 加 `.env` 配置 `LOG_LEVEL=INFO` + `LOG_FORMAT=json`
|
||||
|
||||
### 4.3 季度
|
||||
|
||||
- [ ] Loki + Promtail 部署
|
||||
- [ ] Grafana 仪表盘(Loki 数据源)
|
||||
- [ ] 关键业务事件告警(登录失败/坐席离线)
|
||||
|
||||
---
|
||||
|
||||
## 📌 5. 关联文档
|
||||
|
||||
- [[风险跟踪表]] M-3(无统一错误码)/ M-5(无健康检查)
|
||||
- [[后端架构]] §5 错误处理
|
||||
- [[Dockerfile优化与镜像审计]] - healthcheck
|
||||
- [[前端审计报告]] U-2(全局错误边界)
|
||||
|
||||
---
|
||||
|
||||
*本审计是 2026-06-15 Claude 满载跑批产出,待评审*
|
||||
@@ -0,0 +1,879 @@
|
||||
# aTrust零信任访问控制系统 — 集成分析
|
||||
|
||||
> 分析日期:2026-06-11 | 文档版本:V1.1 | 基于aTrust OpenAPI V3官方接口文档(docx版,适用于≥2.4.10版本)
|
||||
|
||||
---
|
||||
|
||||
## 1. 系统概述
|
||||
|
||||
### 1.1 产品定位
|
||||
|
||||
**深信服aTrust**是零信任访问控制系统(Zero Trust Network Access, ZTNA),替代传统VPN。员工远程办公时通过aTrust接入内网,系统基于身份认证+终端授信+最小权限原则进行访问控制。
|
||||
|
||||
> **关键事实**:总部员工必须安装联软安全助手,远程办公通过aTrust连入内网。这两个系统分别覆盖了内网和VPN两种接入场景的终端。
|
||||
|
||||
### 1.2 与IT服务台的关系
|
||||
|
||||
| 场景 | aTrust的角色 |
|
||||
|------|-------------|
|
||||
| 员工远程报修 | 坐席需要知道员工是否通过VPN在线、VPN IP地址 |
|
||||
| 终端排查 | 查询VPN会话状态、接入方式、虚拟IP |
|
||||
| 安全应急 | 紧急踢出可疑VPN会话、断开远程连接 |
|
||||
| 终端映射 | 获取远程办公员工的终端信息(联软可能未覆盖的VPN终端) |
|
||||
|
||||
---
|
||||
|
||||
## 2. API概览
|
||||
|
||||
### 2.1 端点统计
|
||||
|
||||
| API模块 | 端点数 | 与IT服务台相关度 |
|
||||
|---------|--------|-----------------|
|
||||
| 在线监控 | 2 | 🔴 P0 |
|
||||
| 用户管理 | 26 | 🟡 P1 |
|
||||
| 组织架构管理 | 18 | ⚪ P2 |
|
||||
| 角色管理 | 19 | ⚪ P2 |
|
||||
| 应用管理 | 14 | ⚪ P2 |
|
||||
| 应用分类管理 | 8 | ⚪ P2 |
|
||||
| 认证服务器管理 | 2 | ⚪ P2 |
|
||||
| 用户目录管理 | 5 | ⚪ P2 |
|
||||
| **终端管理** | **9** | **🔴 P0** |
|
||||
| 安全中心管理 | 1 | ⚪ P2 |
|
||||
| **合计** | **104** | — |
|
||||
|
||||
### 2.2 认证机制
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| **算法** | HMAC-SHA256 |
|
||||
| **4个必填Header** | `x-ca-sign`(签名值)、`x-ca-key`(API ID)、`x-ca-timestamp`(10位秒级时间戳)、`x-ca-nonce`(UUID v4随机数) |
|
||||
| **签名密钥** | `appId={API_ID}&appSecret={API_SECRET}×tamp={ts}&nonce={nonce}` |
|
||||
| **签名串** | `{pathname}?{sorted_query}&{compact_json_body}` |
|
||||
| **签名计算** | `HMAC-SHA256(signing_key, signing_string)` → 64位十六进制 |
|
||||
| **防重放** | timestamp + nonce 组合 |
|
||||
| **传输协议** | 必须HTTPS |
|
||||
| **IP白名单** | 支持配置接入IP限制 |
|
||||
| **默认端口** | 4433 |
|
||||
| **频率限制** | **8 QPS**(所有接口共享8请求/秒) |
|
||||
| **实体并发** | 同一实体的操作必须串行调用,否则可能数据覆盖 |
|
||||
| **时间校验** | 请求时间戳与服务器时间差>5分钟则拒绝 |
|
||||
| **Python Demo** | https://bbs.sangfor.com.cn/atrustdeveloper/openapiV3/openapi-demo_for_python.7z |
|
||||
|
||||
#### 签名计算示例
|
||||
|
||||
```python
|
||||
import hmac, hashlib, uuid, time, json
|
||||
|
||||
API_ID = "8165305"
|
||||
API_SECRET = "aebd2e3c5ea2449aa2928c102f9db276"
|
||||
|
||||
# Step 1: 构建签名串
|
||||
pathname = "/api/v1/monitor/getUserStatus"
|
||||
query = "pageIndex=1&pageSize=20" # key按ASCII排序
|
||||
body = "" # GET请求无body
|
||||
sign_str = f"{pathname}?{query}" if query else pathname
|
||||
|
||||
# Step 2: 构建签名密钥
|
||||
timestamp = str(int(time.time()))
|
||||
nonce = str(uuid.uuid4())
|
||||
sign_key = f"appId={API_ID}&appSecret={API_SECRET}×tamp={timestamp}&nonce={nonce}"
|
||||
|
||||
# Step 3: HMAC-SHA256计算签名
|
||||
signature = hmac.new(
|
||||
sign_key.encode('utf-8'),
|
||||
sign_str.encode('utf-8'),
|
||||
hashlib.sha256
|
||||
).hexdigest()
|
||||
|
||||
# Step 4: 设置请求头
|
||||
headers = {
|
||||
"x-ca-key": API_ID,
|
||||
"x-ca-sign": signature,
|
||||
"x-ca-timestamp": timestamp,
|
||||
"x-ca-nonce": nonce,
|
||||
"Content-Type": "application/json;charset=UTF-8"
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 响应格式
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "OK", // 成功="OK"(V3)/ 0(V1);失败=错误码
|
||||
"msg": "请求成功",
|
||||
"traceId": "004c33070f4fa7b4",
|
||||
"data": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
> ⚠️ **注意**:V1接口(在线监控/终端管理)的 `code` 为数字(0=成功),V3接口(用户管理)的 `code` 为字符串("OK"=成功)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心接口详解
|
||||
|
||||
### 3.1 P0 — 查询在线用户
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| **路径** | `GET /api/v1/monitor/getUserStatus` |
|
||||
| **用途** | 查询当前通过aTrust在线的用户列表 |
|
||||
| **价值** | **获取远程办公员工的VPN会话信息,包括接入IP和虚拟IP** |
|
||||
|
||||
#### 请求参数
|
||||
|
||||
| 参数 | 必须 | 说明 | 示例 |
|
||||
|------|------|------|------|
|
||||
| pageSize | 是 | 每页大小,默认20 | 20 |
|
||||
| pageIndex | 是 | 页码,从1开始 | 1 |
|
||||
| filter | 否 | 过滤字段 | `name`/`remoteIp`/`os`/`browser`/`vip` |
|
||||
| searchValue | 否 | 过滤搜索值 | `张三` |
|
||||
| sortBy | 否 | 排序字段 | `lastLoginTime`/`remoteIp`/`vip` |
|
||||
| asc | 否 | 1=升序,0=降序 | 1 |
|
||||
|
||||
#### 响应字段(关键字段加⭐)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | string | **会话ID**(非用户ID,用于踢出操作) |
|
||||
| `name` ⭐ | string | 用户名(登录名) |
|
||||
| `displayName` | string | 显示名 |
|
||||
| `userId` | string | 用户UUID |
|
||||
| `remoteIp` ⭐ | string | 接入IP(公网IP或"内网IP") |
|
||||
| `remoteIpLocation` ⭐ | string | IP归属地(如"内网IP"=从内网接入) |
|
||||
| `os` | string | 操作系统 |
|
||||
| `browser` | string | 接入方式(浏览器/客户端版本) |
|
||||
| `lastLoginTime` ⭐ | string | 最后登录时间 |
|
||||
| `domain` | string | 登录域 |
|
||||
| `userDirectoryName` | string | 所属用户目录 |
|
||||
| `groupPath` | string | 组织架构路径 |
|
||||
| `isTrusted` ⭐ | number | 终端授信状态:0=未授信/1=已授信 |
|
||||
|
||||
> ⚠️ **关于`vips`(虚拟IP)字段**:在线文档(web版)的`getUserStatus`返回包含`vips[].ip`(VPN虚拟内网IP),但官方docx文档的响应示例中**未展示此字段**。可能原因:1) docx为早期版本示例,新版API已新增;2) 需特定配置或隧道应用才返回。**对接时必须实际验证此字段是否存在**。若存在,则`vips[].ip`是远程终端接入内网的虚拟IP,可用于火绒交叉匹配。
|
||||
|
||||
#### 响应示例(官方docx版)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"data": [{
|
||||
"id": "33100014",
|
||||
"name": "张三",
|
||||
"groupPath": "/",
|
||||
"os": "Windows 10",
|
||||
"browser": "Chrome/104.0.5112.102",
|
||||
"remoteIp": "175.9.142.2",
|
||||
"remoteIpLocation": "内网IP",
|
||||
"lastLoginTime": "2022-09-29 16:04:33",
|
||||
"domain": "local",
|
||||
"userId": "9f8146c0-8aeb-11ec-b30f-e50f6db6d9d6",
|
||||
"userDirectoryName": "本地用户目录",
|
||||
"isTrusted": 0,
|
||||
"displayName": "显示名"
|
||||
}],
|
||||
"count": 1,
|
||||
"pageSize": 20,
|
||||
"pageIndex": 1,
|
||||
"amount": 1,
|
||||
"onlineUser": 1
|
||||
},
|
||||
"msg": "请求成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务解读
|
||||
|
||||
- `id`:**会话ID**,踢出用户时需使用此ID(非userId),支持精准踢出单个会话
|
||||
- `remoteIp`:员工当前IP,`remoteIpLocation="内网IP"`表示从内网直接接入(非VPN)
|
||||
- `isTrusted`:终端是否已授信,0=未授信可能影响访问权限
|
||||
- `name`:用户登录名,如果与公司域账号一致,则可直接映射到 `employee_id`
|
||||
- 同一用户可能在多个终端登录,返回多条记录(每条有不同`id`)
|
||||
|
||||
---
|
||||
|
||||
### 3.2 P0 — 查询全量终端信息
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| **路径** | `POST /api/v1/device/queryAll` |
|
||||
| **用途** | 批量查询aTrust纳管的终端设备 |
|
||||
| **价值** | **按绑定用户查询终端列表,建立用户→终端映射** |
|
||||
|
||||
#### 请求参数
|
||||
|
||||
| 参数 | 必须 | 类型 | 说明 |
|
||||
|------|------|------|------|
|
||||
| bindUserList | 否 | object[] | **按绑定用户过滤**(v2.6.6+) |
|
||||
| ├─ userName | 是 | string | 用户名 |
|
||||
| ├─ userDirectoryName | 是 | string | 用户目录名 |
|
||||
| onlineStatus | 否 | number | 0=离线,1=在线 |
|
||||
| loginStatus | 否 | number | 0=未接入,1=已接入 |
|
||||
| assetType | 否 | string | CYOD/BYOD/COPE/NONE |
|
||||
| trusted | 否 | number | 0=未授信,1=已授信 |
|
||||
| tagList | 否 | string[] | 标签过滤 |
|
||||
| osList | 否 | string[] | OS过滤(Windows/macOS/统信UOS/麒麟Kylin/Android/iOS/HarmonyOS/iPadOS) |
|
||||
| pageSize | 否 | number | 最大1000 |
|
||||
| pageIndex | 否 | number | 默认1 |
|
||||
|
||||
#### 响应字段(关键字段加⭐)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `externalId` ⭐ | string | 终端外部ID(唯一标识) |
|
||||
| `macList` ⭐ | string[] | MAC地址列表 |
|
||||
| `deviceName` ⭐ | string | 终端名称/主机名 |
|
||||
| `os` | string | 操作系统 |
|
||||
| `deviceType` | string | PC/Mobile |
|
||||
| `deviceBrand` | string | 品牌 |
|
||||
| `assetType` | string | CYOD(企业)/BYOD(个人)/COPE(纳管) |
|
||||
| `trusted` ⭐ | number | 授信状态:0=未授信/1=已授信 |
|
||||
| `onlineStatus` ⭐ | number | 在线状态 |
|
||||
| `loginStatus` ⭐ | number | 接入状态 |
|
||||
| `tagList` | string[] | 标签列表 |
|
||||
| `windowsDomain` | string | Windows域控 |
|
||||
| `bindUsers` ⭐ | object[] | **绑定用户列表** |
|
||||
| ├─ bindUser | string | 绑定的用户名 |
|
||||
| ├─ bindType | string | 绑定方式:userSelfBind/adminBind/adminAdmit |
|
||||
| ├─ bindTime | string | 绑定时间 |
|
||||
| `historyUsers` | object[] | 历史登录用户列表 |
|
||||
| `lastLoginUser` | string | 最后登录用户名 |
|
||||
| `displayName` | string | 最后登录用户显示名 |
|
||||
| `clientVersion` | string | 客户端版本 |
|
||||
| `lastLoginTime` | string | 最后接入时间 |
|
||||
| `lastActiveTime` | string | 最后活跃时间 |
|
||||
|
||||
> ⚠️ **重要**:aTrust终端接口**不返回IP地址**,只有 `lastNetworkZone`(网络区域标识,如"内网IP")。需通过在线监控接口(4.1.1)获取VPN IP。
|
||||
|
||||
---
|
||||
|
||||
### 3.3 P0 — 查询单个终端信息
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| **路径** | `GET /api/v1/device/query` |
|
||||
| **用途** | 按externalId或MAC查询单个终端详情 |
|
||||
| **约束** | externalId和mac互斥,不可同时传入 |
|
||||
|
||||
#### 请求参数
|
||||
|
||||
| 参数 | 必须 | 说明 |
|
||||
|------|------|------|
|
||||
| externalId | 二选一 | 终端外部ID |
|
||||
| mac | 二选一 | MAC地址(匹配多条则报错) |
|
||||
|
||||
#### 额外响应字段(相比queryAll,来自官方docx)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `idleTime` ⭐ | number | 终端闲置时长(天),可用于判断长期离线设备 |
|
||||
| `firstImportTime` | string | 首次录入时间 |
|
||||
| `lastLoginMethod` | string | 最后接入方式(如"Edge/98.0.1108.50") |
|
||||
| `lastLoginNetZone` ⭐ | string | 最后接入网络区域("内网IP"/外网标识) |
|
||||
| `userDescription` | string | 最后登录用户描述 |
|
||||
| `path` | string | 最后登录用户所属组织架构 |
|
||||
| `lastActiveTime` | string | 最后活跃时间 |
|
||||
| `clientVersion` | string | aTrust客户端版本 |
|
||||
| `windowsDomain` ⭐ | string | Windows域控名(可用于AD域映射) |
|
||||
| `deviceBrand` | string | 设备品牌 |
|
||||
| `historyUsers` ⭐ | object[] | **历史登录用户列表**(含userName/userDirectoryName/displayName/userDescription) |
|
||||
|
||||
> 💡 `historyUsers`字段非常有价值:即使终端当前未绑定用户,通过历史登录记录也能追溯设备使用者,辅助员工→终端映射。
|
||||
|
||||
---
|
||||
|
||||
### 3.4 P1 — 踢出在线用户
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| **路径** | `POST /api/v1/monitor/kickoutUsers` |
|
||||
| **用途** | 强制断开VPN会话 |
|
||||
| **安全等级** | 🔴 高危操作,必须二次确认+审计日志 |
|
||||
|
||||
#### 请求参数(二选一)
|
||||
|
||||
| 方式 | 参数 | 说明 |
|
||||
|------|------|------|
|
||||
| **按会话ID** | `idList: string[]` | 从查询在线用户接口获取的会话ID,精准踢出单个会话 |
|
||||
| **按用户名** | `userList: [{name, userDirectoryName}]` | ⚠️ **该用户所有终端的会话都会被踢出** |
|
||||
|
||||
#### 安全约束(与火绒隔离同级)
|
||||
|
||||
- 踢出操作**必须人工二次确认 + 填写原因**
|
||||
- 仅 **admin角色** 可执行
|
||||
- 操作记录写入审计日志
|
||||
- 优先使用 `idList`(精准踢出),避免误伤其他终端
|
||||
|
||||
---
|
||||
|
||||
### 3.5 P1 — 查询用户详情
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| **路径** | `GET /api/v3/user/queryByName` |
|
||||
| **用途** | 按用户名查询用户详情 |
|
||||
|
||||
#### 关键响应字段
|
||||
|
||||
| 字段 | 说明 | 与IT服务台映射 |
|
||||
|------|------|---------------|
|
||||
| `name` | 用户登录名 | 可能对应 `employee_id` |
|
||||
| `externalId` ⭐ | 外部ID | **可设置为工号,直接映射** |
|
||||
|
||||
> ⚠️ **V3 API关键参数 `directoryDomain`**:所有V3用户管理API必须传入`directoryDomain`参数(用户目录域名),例如`"custom01339"`或`"local"`。此值需从aTrust控制台的【系统管理/用户目录】页面获取,或在首次对接时通过`4.8.1 查询用户目录列表`接口获取。不同用户目录(本地/LDAP/外部同步)有不同的`directoryDomain`。
|
||||
| `displayName` | 显示名 | 员工姓名 |
|
||||
| `groupPath` | 组织架构路径 | 部门信息 |
|
||||
| `email` | 邮箱 | 联系方式 |
|
||||
| `phone` | 手机号 | 联系方式 |
|
||||
| `status` | 启用/禁用 | 账号状态 |
|
||||
| `roleIdList` | 角色ID列表 | 权限信息 |
|
||||
| `resourceIdList` | 关联应用ID列表 | 有权限访问的应用 |
|
||||
|
||||
---
|
||||
|
||||
### 3.6 P1 — 终端绑定/解绑用户
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| **绑定路径** | `POST /api/v1/device/assignUser` |
|
||||
| **解绑路径** | `POST /api/v1/device/unassignUser` |
|
||||
|
||||
#### 绑定参数
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| externalId 或 mac | 终端标识(二选一) |
|
||||
| userName | 用户名 |
|
||||
| userDirectoryName | 用户目录名 |
|
||||
|
||||
#### 绑定方式
|
||||
|
||||
| 方式 | 说明 |
|
||||
|------|------|
|
||||
| `userSelfBind` | 用户自助绑定 |
|
||||
| `adminBind` | 管理员手动绑定 |
|
||||
| `adminAdmit` | 管理员审批后绑定 |
|
||||
|
||||
> 💡 可通过OpenAPI实现联软/eHR的终端绑定数据同步到aTrust。
|
||||
|
||||
---
|
||||
|
||||
### 3.7 P2 — 终端CRUD管理(官方docx新增)
|
||||
|
||||
| 操作 | 路径 | 方法 | 版本要求 | 说明 |
|
||||
|------|------|------|---------|------|
|
||||
| 新增终端 | `/api/v1/device/create` | POST | v2.2.7+ | MAC/计算机名+externalId |
|
||||
| 修改终端 | `/api/v1/device/update` | POST | v2.2.9+ | 基于externalId修改,支持newExternalId |
|
||||
| 删除终端 | `/api/v1/device/delete` | POST | v2.2.7+ | 基于externalId或MAC删除 |
|
||||
|
||||
#### 终端匹配规则
|
||||
|
||||
aTrust判断终端是否已存在的规则:
|
||||
1. `externalId`匹配已有终端
|
||||
2. 根据控制台【终端匹配规则】,MAC或计算机名与已有终端一致
|
||||
|
||||
#### 新增终端请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"mac": ["FE-FC-FE-21-F5-D1", "FE-FC-FE-21-F5-D2"],
|
||||
"deviceType": "PC",
|
||||
"name": "DESKTOP-I3ABQBS",
|
||||
"externalId": "0c4e9039-f81d-11ec-a760-fefcfe545bb7",
|
||||
"assetType": "BYOD",
|
||||
"tagList": ["开发测试终端", "办公网终端"]
|
||||
}
|
||||
```
|
||||
|
||||
> 💡 `assetType`选项:CYOD(企业选型)/ BYOD(自带)/ COPE(纳管)/ NONE。可通过`externalId`与联软终端ID对齐。
|
||||
|
||||
### 3.8 P2 — 终端标签管理
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 设置标签 | `POST /api/v1/device/setTag`(支持追加`isAppend=1`或覆盖模式) |
|
||||
| 取消标签 | `POST /api/v1/device/unsetTag` |
|
||||
|
||||
标签不存在时自动新增。可用于标注终端类型(如"开发测试终端"、"办公网终端"),与IT服务台的设备分类对齐。
|
||||
|
||||
### 3.9 P2 — SPA安全码管理
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| **路径** | `POST /api/v1/spa/sendSpaCode` |
|
||||
| **版本** | v2.2.10+ |
|
||||
| **用途** | 为指定用户申请SPA安全码(单包授权),通过短信发送 |
|
||||
|
||||
> 💡 SPA(Single Packet Authorization)安全码是零信任中"先认证后连接"机制的关键。当员工报修"无法访问内网应用"时,坐席可通过此接口为其重新发送安全码,恢复VPN接入能力。
|
||||
|
||||
#### 请求参数
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| name/displayName/phone/email | 至少传一个(联合查询用户) |
|
||||
| userDirectoryName | 必传(用户目录名) |
|
||||
| expiredTime | 过期时间戳(秒),0=永不过期 |
|
||||
| sendMode | 发送方式:`["sms"]`(暂仅支持短信) |
|
||||
|
||||
---
|
||||
|
||||
### 3.10 API版本兼容性说明
|
||||
|
||||
> ⚠️ aTrust不同API端点的版本要求不同,对接前必须确认公司aTrust版本。
|
||||
|
||||
| API模块 | 最低版本 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 在线监控(4.1) | 未标注 | 可能v2.2.x+即可 |
|
||||
| 终端管理-基础(create/delete/queryAll) | **v2.2.7** | |
|
||||
| 终端管理-高级(update/query/assignUser/unassignUser/setTag) | **v2.2.9** | |
|
||||
| SPA安全码 | **v2.2.10** | |
|
||||
| V3用户管理(4.2~4.8) | **v2.4.10** | 用户/组织架构/角色/应用管理 |
|
||||
|
||||
> **关键判断**:如果公司aTrust版本 < v2.2.9,则终端绑定用户功能不可用,映射策略需调整。
|
||||
|
||||
---
|
||||
|
||||
## 4. 集成架构设计
|
||||
|
||||
### 4.1 四系统联合映射架构(最终版)
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ 智能IT支持服务台 │
|
||||
│ employee_id │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────────────────┼────────────────┐
|
||||
↓ ↓ ↓
|
||||
┌─────────────────┐ ┌────────────────┐ ┌────────────────┐
|
||||
│ 联软LV7000(主源)│ │ aTrust(VPN源) │ │ eHR(辅助源) │
|
||||
│ │ │ │ │ │
|
||||
│ queryDevByParams│ │ 4.1.1 在线用户 │ │ 员工基础信息 │
|
||||
│ strusername= │ │ name= │ │ 任职信息 │
|
||||
│ employee_id │ │ employee_id │ │ 部门/职位 │
|
||||
│ │ │ │ │ │
|
||||
│ → 终端MAC/IP │ │ → remoteIp │ │ → 员工姓名 │
|
||||
│ → 硬件详情 │ │ → vips[].ip │ │ → 联系方式 │
|
||||
│ → 准入状态 │ │ → os/browser │ │ → 上级主管 │
|
||||
│ → 在线状态 │ │ → loginTime │ │ │
|
||||
└────────┬────────┘ └────────┬───────┘ └─────────────────┘
|
||||
│ │
|
||||
│ 用IP/MAC交叉匹配 │
|
||||
↓ ↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ 火绒企业版(安全源) │
|
||||
│ │
|
||||
│ _list(ip=终端IP) → client_id │
|
||||
│ _info2 → 病毒事件/高危漏洞/安全评分 │
|
||||
│ _virus_events → 病毒统计 │
|
||||
│ _leak → 高危漏洞未修复 │
|
||||
│ _create(netctrl) → 一键隔离 │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4.2 映射优先级与数据源选择
|
||||
|
||||
| 接入场景 | 首选数据源 | 查询路径 | 覆盖率 |
|
||||
|---------|-----------|---------|--------|
|
||||
| **总部内网办公** | 联软(主源) | `queryDevByParams(strusername=id)` → IP/MAC → 火绒安全状态 | ~95% |
|
||||
| **远程/出差** | aTrust(VPN源) | `getUserStatus(name=id)` → vips[].ip → 火绒安全状态 | ~90% |
|
||||
| **历史/离线** | 联软+aTrust | 联软历史记录 + aTrust historyUsers | ~80% |
|
||||
| **新入职** | eHR(辅助) | eHR员工信息 → 手动关联终端 | ~0% |
|
||||
|
||||
### 4.3 完整查询流程
|
||||
|
||||
```
|
||||
1. 坐席/员工输入 employee_id
|
||||
2. 并行查询:
|
||||
├── 联软 queryDevByParams(strusername=employee_id) → 内网终端列表
|
||||
├── aTrust getUserStatus(name=employee_id) → VPN在线会话
|
||||
└── aTrust queryAll(bindUserList=[{userName=id}]) → VPN绑定终端
|
||||
3. 合并终端列表(去重,以MAC为主键)
|
||||
4. 用合并后的IP/MAC列表查火绒安全状态
|
||||
5. 组装「终端安全画像」返回给坐席/员工
|
||||
```
|
||||
|
||||
### 4.4 aTrust与联软映射对比
|
||||
|
||||
| 维度 | 联软LV7000 | aTrust |
|
||||
|------|-----------|--------|
|
||||
| **映射字段** | `strusername`(直接是员工账号) | `name`(用户名)+ `bindUsers[].bindUser` |
|
||||
| **映射准确度** | ⭐⭐⭐⭐⭐ 总部必装,100%覆盖 | ⭐⭐⭐⭐ 依赖绑定策略 |
|
||||
| **IP地址** | ✅ 有内网IP | ✅ 有公网IP + VPN虚拟IP |
|
||||
| **MAC地址** | ✅ strmac | ✅ macList(数组) |
|
||||
| **硬件详情** | ✅ 极详细(磁盘/内存/显示器) | ❌ 仅基础信息 |
|
||||
| **远程终端** | ⚠️ 可能未覆盖 | ✅ 核心覆盖VPN终端 |
|
||||
| **实时在线** | ✅ existOnlineUser | ✅ onlineStatus + getUserStatus |
|
||||
| **组织架构** | ✅ 组织架构查询 | ✅ groupPath + 组织架构API |
|
||||
|
||||
---
|
||||
|
||||
## 5. 产品维度分析
|
||||
|
||||
### 5.1 场景匹配度
|
||||
|
||||
| IT服务台场景 | aTrust能做什么 | 匹配度 |
|
||||
|-------------|---------------|--------|
|
||||
| 员工报修"远程无法访问内网" | 查询VPN在线状态+虚拟IP+接入方式 | ⭐⭐⭐⭐⭐ |
|
||||
| 坐席排查"VPN连接异常" | 查询在线状态、最后登录时间、接入IP | ⭐⭐⭐⭐⭐ |
|
||||
| 安全应急"发现可疑VPN连接" | 踢出在线用户(断开VPN会话) | ⭐⭐⭐⭐⭐ |
|
||||
| 终端排查"设备是否授信" | 查询终端trusted状态+绑定用户 | ⭐⭐⭐⭐ |
|
||||
| AI推送"VPN连接诊断" | 获取VPN会话信息供AI分析 | ⭐⭐⭐⭐ |
|
||||
| 员工自助"查看VPN状态" | 查询自己是否在线+虚拟IP | ⭐⭐⭐ |
|
||||
|
||||
### 5.2 功能规划
|
||||
|
||||
#### P0 — 查询能力(零风险,~1.5周)
|
||||
|
||||
| 功能 | 使用接口 | 说明 |
|
||||
|------|---------|------|
|
||||
| VPN在线状态查询 | 4.1.1 getUserStatus | 坐席查看员工VPN连接状态 |
|
||||
| 远程终端查询 | 4.9.5 queryAll(bindUserList) | 按员工查询VPN绑定终端 |
|
||||
| 终端安全画像集成 | 4.9.4 query + 火绒/联软 | aTrust终端信息加入画像 |
|
||||
|
||||
#### P1 — 控制能力(中风险,~1周)
|
||||
|
||||
| 功能 | 使用接口 | 安全约束 |
|
||||
|------|---------|---------|
|
||||
| 踢出VPN会话 | 4.1.2 kickoutUsers | admin角色+二次确认+审计 |
|
||||
| 终端授信标签同步 | 4.9.8/4.9.9 setTag/unsetTag | 与联软准入状态同步 |
|
||||
|
||||
#### P2 — 管理能力(低风险,~1周)
|
||||
|
||||
| 功能 | 使用接口 | 说明 |
|
||||
|------|---------|------|
|
||||
| 用户同步 | 4.2.x 用户管理API | eHR→aTrust用户同步 |
|
||||
| 终端绑定同步 | 4.9.6 assignUser | 联软映射→aTrust绑定 |
|
||||
| 安全态势看板 | 4.1.1+4.9.5 | 管理后台VPN在线统计 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 开发维度分析
|
||||
|
||||
### 6.1 技术架构
|
||||
|
||||
```
|
||||
backend/app/
|
||||
├── integrations/
|
||||
│ ├── atrust/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── client.py # aTrust API客户端(签名+请求)
|
||||
│ │ ├── models.py # 数据模型(ATrustUser/ATrustTerminal/ATrustSession)
|
||||
│ │ ├── cache.py # 缓存策略
|
||||
│ │ └── service.py # 业务服务层
|
||||
│ ├── huorong/
|
||||
│ │ └── ... (已有)
|
||||
│ ├── leagsoft/
|
||||
│ │ └── ... (已有)
|
||||
│ └── device_profile.py # 四系统聚合 → 终端安全画像
|
||||
├── api/
|
||||
│ └── integrations.py # 统一对外API
|
||||
```
|
||||
|
||||
### 6.2 签名实现
|
||||
|
||||
aTrust签名比火绒复杂(4步),但逻辑清晰:
|
||||
|
||||
> **与火绒签名的关键区别**:火绒使用HMAC-SHA1,aTrust使用HMAC-SHA256;火绒签名放在Header和URL两种方式,aTrust仅Header方式;aTrust签名密钥包含timestamp和nonce(更安全)。
|
||||
|
||||
```python
|
||||
# backend/app/integrations/atrust/client.py
|
||||
|
||||
import hmac, hashlib, uuid, time, httpx
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
|
||||
class ATrustClient:
|
||||
def __init__(self, base_url: str, api_id: str, api_secret: str):
|
||||
self.base_url = base_url # https://atrust.xxx.com:4433
|
||||
self.api_id = api_id
|
||||
self.api_secret = api_secret
|
||||
self.client = httpx.AsyncClient(verify=False, timeout=30)
|
||||
|
||||
def _build_sign_str(self, method: str, path: str, query: str, body: str) -> str:
|
||||
"""
|
||||
构建签名串:pathname?sorted_query&compact_body
|
||||
- query的key必须按ASCII排序
|
||||
- body必须是紧凑JSON(无空格/换行)
|
||||
"""
|
||||
parts = [path]
|
||||
if query or body:
|
||||
parts.append("?")
|
||||
if query:
|
||||
# 对query参数按key排序
|
||||
parsed = parse_qs(query)
|
||||
sorted_query = "&".join(
|
||||
f"{k}={v[0]}" for k, v in sorted(parsed.items())
|
||||
)
|
||||
parts.append(sorted_query)
|
||||
if query and body:
|
||||
parts.append("&")
|
||||
if body:
|
||||
parts.append(body)
|
||||
return "".join(parts)
|
||||
|
||||
def _sign(self, sign_str: str) -> tuple[str, str, str]:
|
||||
"""4步签名:生成签名密钥 → HMAC-SHA256 → 返回签名+时间戳+nonce"""
|
||||
timestamp = str(int(time.time()))
|
||||
nonce = str(uuid.uuid4())
|
||||
|
||||
# 签名密钥
|
||||
sign_key = (
|
||||
f"appId={self.api_id}&appSecret={self.api_secret}"
|
||||
f"×tamp={timestamp}&nonce={nonce}"
|
||||
)
|
||||
|
||||
# HMAC-SHA256
|
||||
signature = hmac.new(
|
||||
sign_key.encode("utf-8"),
|
||||
sign_str.encode("utf-8"),
|
||||
hashlib.sha256,
|
||||
).hexdigest()
|
||||
|
||||
return signature, timestamp, nonce
|
||||
|
||||
async def request(self, method: str, path: str, **kwargs) -> dict:
|
||||
"""通用请求方法:自动签名"""
|
||||
url = f"{self.base_url}{path}"
|
||||
parsed = urlparse(url)
|
||||
|
||||
query = parsed.query
|
||||
body = ""
|
||||
if kwargs.get("json"):
|
||||
body = json.dumps(kwargs["json"], separators=(",", ":"))
|
||||
|
||||
sign_str = self._build_sign_str(method, parsed.path, query, body)
|
||||
signature, timestamp, nonce = self._sign(sign_str)
|
||||
|
||||
headers = {
|
||||
"x-ca-key": self.api_id,
|
||||
"x-ca-sign": signature,
|
||||
"x-ca-timestamp": timestamp,
|
||||
"x-ca-nonce": nonce,
|
||||
"Content-Type": "application/json;charset=UTF-8",
|
||||
}
|
||||
|
||||
response = await self.client.request(
|
||||
method, url, headers=headers, **kwargs
|
||||
)
|
||||
return response.json()
|
||||
```
|
||||
|
||||
### 6.3 缓存策略
|
||||
|
||||
| 数据类型 | 缓存时间 | 理由 |
|
||||
|---------|---------|------|
|
||||
| VPN在线状态 | **不缓存**(实时查询) | 实时性要求高 |
|
||||
| 终端绑定信息 | 30分钟 | 绑定变更不频繁 |
|
||||
| 用户信息 | 1小时 | 用户信息变更极少 |
|
||||
| 组织架构 | 4小时 | 几乎不变 |
|
||||
|
||||
### 6.4 与其他系统的交叉匹配
|
||||
|
||||
```python
|
||||
# 终端安全画像聚合伪代码
|
||||
async def get_device_profile(employee_id: str) -> DeviceProfile:
|
||||
# 1. 并行查三系统
|
||||
leagsoft_task = leagsoft.query_dev_by_params(strusername=employee_id)
|
||||
atrust_task = atrust.get_online_users(name=employee_id)
|
||||
atrust_terminals_task = atrust.query_all_terminals(
|
||||
bindUserList=[{"userName": employee_id, "userDirectoryName": "local"}]
|
||||
)
|
||||
|
||||
leagsoft_devs, atrust_sessions, atrust_terminals = await asyncio.gather(
|
||||
leagsoft_task, atrust_task, atrust_terminals_task
|
||||
)
|
||||
|
||||
# 2. 收集所有IP/MAC
|
||||
all_ips = set()
|
||||
all_macs = set()
|
||||
|
||||
for dev in leagsoft_devs:
|
||||
all_ips.add(dev.strip)
|
||||
all_macs.add(dev.strmac.upper().replace(":", "-"))
|
||||
|
||||
for session in atrust_sessions:
|
||||
all_ips.add(session.remoteIp)
|
||||
for vip in session.vips:
|
||||
all_ips.add(vip.ip)
|
||||
|
||||
for terminal in atrust_terminals:
|
||||
for mac in terminal.macList:
|
||||
all_macs.add(mac.upper())
|
||||
|
||||
# 3. 查火绒安全状态
|
||||
huorong_tasks = []
|
||||
for ip in all_ips:
|
||||
huorong_tasks.append(huorong.list_clients(ip=ip))
|
||||
|
||||
huorong_results = await asyncio.gather(*huorong_tasks)
|
||||
|
||||
# 4. 组装画像
|
||||
return DeviceProfile(
|
||||
employee_id=employee_id,
|
||||
leagsoft_devices=leagsoft_devs,
|
||||
atrust_sessions=atrust_sessions,
|
||||
atrust_terminals=atrust_terminals,
|
||||
huorong_security=huorong_results,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 安全维度分析
|
||||
|
||||
### 7.1 认证安全
|
||||
|
||||
| 风险 | 等级 | 缓解措施 |
|
||||
|------|------|---------|
|
||||
| API密钥泄露 | 🔴 高 | 环境变量存储,禁止写入代码;定期轮换 |
|
||||
| 签名重放攻击 | 🟢 低 | timestamp+nonce防重放(已内置) |
|
||||
| 中间人攻击 | 🟢 低 | 强制HTTPS |
|
||||
| IP白名单绕过 | 🟡 中 | 白名单仅包含IT服务台服务器IP |
|
||||
|
||||
### 7.2 操作安全
|
||||
|
||||
| 操作 | 风险 | 安全约束 |
|
||||
|------|------|---------|
|
||||
| **踢出在线用户** | 🔴 高危 | admin角色 + 二次确认 + 填写原因 + 审计日志 |
|
||||
| 终端绑定/解绑 | 🟡 中 | admin角色 + 操作日志 |
|
||||
| 查询在线用户 | 🟢 低 | 只读操作,无风险 |
|
||||
| 查询终端信息 | 🟢 低 | 只读操作,注意不向H5用户端暴露MAC等敏感信息 |
|
||||
|
||||
### 7.3 数据安全
|
||||
|
||||
| 数据类型 | H5用户端是否可见 | 说明 |
|
||||
|---------|----------------|------|
|
||||
| VPN在线状态 | ✅ 可见(自己的) | "您当前VPN连接正常" |
|
||||
| 虚拟IP | ❌ 不显示 | 内网敏感信息 |
|
||||
| 接入IP | ❌ 不显示 | 公网IP属于隐私 |
|
||||
| 终端MAC | ❌ 不显示 | 设备指纹,敏感 |
|
||||
| 终端授信状态 | ✅ 可见(简化版) | "您的设备已通过安全认证" |
|
||||
| 绑定用户列表 | ❌ 不显示 | 其他用户的绑定信息 |
|
||||
|
||||
### 7.4 与火绒隔离的统一安全框架
|
||||
|
||||
| 安全规则 | 火绒网络隔离 | aTrust踢出用户 |
|
||||
|---------|------------|---------------|
|
||||
| 执行角色 | 仅admin | 仅admin |
|
||||
| 操作确认 | 二次确认弹窗 | 二次确认弹窗 |
|
||||
| 原因记录 | 必填+审计日志 | 必填+审计日志 |
|
||||
| 通知机制 | 通知被隔离用户 | 通知被踢出用户 |
|
||||
| 可逆性 | 可解除隔离 | 用户可重新登录 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 对接前检查清单
|
||||
|
||||
### 8.1 必须确认
|
||||
|
||||
- [ ] aTrust版本确认(必须≥2.4.10才支持V3 API;终端管理高级功能需≥v2.2.9)
|
||||
- [ ] 获取API ID和API密钥
|
||||
- [ ] IT服务台服务器IP加入aTrust白名单
|
||||
- [ ] 确认aTrust用户目录类型(本地/LDAP/外部)及 `directoryDomain` 值
|
||||
- [ ] 确认aTrust中用户名(`name`)是否与公司域账号/企微账号一致
|
||||
- [ ] 确认`externalId`字段用途(是否可设置为工号)
|
||||
- [ ] **验证`getUserStatus`接口是否返回`vips`字段**(docx版未展示,web版有,需实测确认)
|
||||
- [ ] 确认8 QPS限频是否满足业务需求(估计足够)
|
||||
|
||||
### 8.2 建议确认
|
||||
|
||||
- [ ] aTrust终端绑定策略(是否开启一对一绑定)
|
||||
- [ ] 在线用户数据保留时长(历史VPN会话是否可查)
|
||||
- [ ] API调用频率限制
|
||||
- [ ] 是否有API调用审计日志
|
||||
|
||||
### 8.3 需找团队
|
||||
|
||||
| 对接方 | 需获取 | 预估时间 |
|
||||
|--------|--------|---------|
|
||||
| **信息安全团队** | API ID/密钥、白名单配置、版本确认 | 1-2天 |
|
||||
| **网络运维团队** | aTrust部署架构、用户目录配置 | 1天 |
|
||||
| **终端安全团队** | 联软+aTrust+火绒统一映射策略对齐 | 2-3天 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 四系统协同方案总结
|
||||
|
||||
### 9.1 系统定位矩阵
|
||||
|
||||
| 系统 | 定位 | 核心价值 | 不可替代性 |
|
||||
|------|------|---------|-----------|
|
||||
| **联软LV7000** | 终端管理(主源) | 员工→终端映射 + 硬件详情 + 准入控制 | ⭐⭐⭐⭐⭐ strusername精确映射 |
|
||||
| **aTrust** | 远程接入(VPN源) | VPN会话数据 + 远程终端映射 + 踢出能力 | ⭐⭐⭐⭐⭐ 唯一VPN数据源 |
|
||||
| **火绒企业版** | 终端安全(安全源) | 病毒/漏洞/隔离 + 安全评分 | ⭐⭐⭐⭐⭐ 唯一安全数据源 |
|
||||
| **北森eHR** | 人事数据(辅助源) | 员工基础信息 + 任职 + 组织架构 | ⭐⭐⭐ 员工主数据 |
|
||||
|
||||
### 9.2 数据流向
|
||||
|
||||
```
|
||||
员工报修 → employee_id
|
||||
↓
|
||||
联软 → 内网终端列表 (IP/MAC/硬件)
|
||||
aTrust → VPN终端列表 (remoteIp/vips/macList)
|
||||
↓ 合并去重
|
||||
火绒 → 安全状态 (病毒/漏洞/隔离)
|
||||
↓
|
||||
eHR → 员工信息 (姓名/部门/主管)
|
||||
↓
|
||||
组装「终端安全画像」→ 展示给坐席/员工
|
||||
```
|
||||
|
||||
### 9.3 接口调用频次估算
|
||||
|
||||
| 场景 | 调用次数 | 触发频率 |
|
||||
|------|---------|---------|
|
||||
| 员工发起报修 | 联软1 + aTrust2 + 火绒1~3 = 4~6次 | 按需(每天几十次) |
|
||||
| 坐席查看终端画像 | 同上 | 按需 |
|
||||
| 安全态势看板 | aTrust1 + 火绒1 | 定时刷新(5分钟) |
|
||||
| 终端映射全量同步 | 联软1 + aTrust1 | 每天凌晨1次 |
|
||||
|
||||
### 9.4 实施优先级
|
||||
|
||||
| 优先级 | 内容 | 周期 | 依赖 |
|
||||
|--------|------|------|------|
|
||||
| **P0** | aTrust查询能力上线 | ~1.5周 | API ID/密钥 + 白名单 |
|
||||
| **P1** | 踢出用户+标签同步 | ~1周 | P0完成 |
|
||||
| **P2** | 四系统联合画像 | ~1.5周 | 联软+火绒+eHR已集成 |
|
||||
| **P3** | 用户同步自动化 | ~1周 | eHR→aTrust对接流程确认 |
|
||||
|
||||
---
|
||||
|
||||
## 附录
|
||||
|
||||
### A. aTrust vs 火绒 vs 联软 — 技术对比
|
||||
|
||||
| 维度 | 联软LV7000 | 火绒企业版 | aTrust |
|
||||
|------|-----------|-----------|--------|
|
||||
| API端点数 | 68 | 17 | 104 |
|
||||
| 认证方式 | IP白名单+账号密码+Token | AccessKey+HMAC-SHA1 | API_ID+HMAC-SHA256 |
|
||||
| 签名复杂度 | 简单(Base64编码) | 中等(HMAC-SHA1) | 较高(4步签名) |
|
||||
| 协议 | HTTP | HTTPS | HTTPS(4433) |
|
||||
| 响应格式 | {code, data, msg} | {errno, errmsg, data} | {code, msg, data, traceId} |
|
||||
| 成功标识 | code=0 | errno=0 | code=0(V1)/"OK"(V3) |
|
||||
| 分页 | pageSize+pageIndex | page+pageSize | pageSize+pageIndex |
|
||||
| 最大分页 | 500 | 500 | 1000 |
|
||||
| IP地址 | ✅ 有 | ✅ 有 | ✅ 有(remoteIp+vips*) |
|
||||
| MAC地址 | ✅ strmac | ❌ 无 | ✅ macList |
|
||||
| 员工→终端映射 | ⭐⭐⭐⭐⭐ strusername | ⭐⭐ 需IP交叉匹配 | ⭐⭐⭐⭐ name+bindUsers+historyUsers |
|
||||
| 频率限制 | 未标注 | 未标注 | **8 QPS** |
|
||||
| 历史用户 | ❌ 无 | ❌ 无 | ✅ historyUsers(追溯设备使用者) |
|
||||
|
||||
> *vips字段在web版文档中有,docx版未展示,需实测确认
|
||||
|
||||
### B. aTrust关键API速查表
|
||||
|
||||
| 接口 | 路径 | 方法 | P级 |
|
||||
|------|------|------|------|
|
||||
| 查询在线用户 | /api/v1/monitor/getUserStatus | GET | P0 |
|
||||
| 踢出在线用户 | /api/v1/monitor/kickoutUsers | POST | P1 |
|
||||
| 新增终端 | /api/v1/device/create | POST | P2 |
|
||||
| 修改终端 | /api/v1/device/update | POST | P2 |
|
||||
| 删除终端 | /api/v1/device/delete | POST | P2 |
|
||||
| 查询全量终端 | /api/v1/device/queryAll | POST | P0 |
|
||||
| 查询单个终端 | /api/v1/device/query | GET | P0 |
|
||||
| 终端绑定用户 | /api/v1/device/assignUser | POST | P1 |
|
||||
| 终端解绑用户 | /api/v1/device/unassignUser | POST | P1 |
|
||||
| 设置终端标签 | /api/v1/device/setTag | POST | P2 |
|
||||
| 取消终端标签 | /api/v1/device/unsetTag | POST | P2 |
|
||||
| 查询用户详情 | /api/v3/user/queryByName | GET | P1 |
|
||||
| 查询用户列表 | /api/v3/user/queryAll | POST | P1 |
|
||||
| 申请SPA安全码 | /api/v1/spa/sendSpaCode | POST | P2 |
|
||||
@@ -0,0 +1,560 @@
|
||||
# 火绒终端安全管理系统集成分析
|
||||
|
||||
> 基于火绒终端安全管理系统API说明文档(内网地址: `huorong.oa.servyou-it.com:8080`)
|
||||
> 分析日期:2026-06-11
|
||||
> 分析人:智能IT支持服务台项目组
|
||||
|
||||
---
|
||||
|
||||
## 一、火绒API全景概览
|
||||
|
||||
### 1.1 认证机制
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 认证方式 | AccessKey ID + AccessKey Secret(HMAC-SHA1签名) |
|
||||
| 签名方式 | Header签名(推荐) / URL签名(备选) |
|
||||
| 签名算法 | HMAC-SHA1 → Base64编码 |
|
||||
| 公共参数 | `access_key_id`、`signature`、`timestamp`、`nonce` |
|
||||
| 内网地址 | `http://huorong.oa.servyou-it.com:8080` |
|
||||
|
||||
### 1.2 API端点清单
|
||||
|
||||
| 分类 | 端点 | 方法 | 说明 | 优先级建议 |
|
||||
|------|------|------|------|-----------|
|
||||
| **分组管理** | `/api/group/_list` | POST | 获取全部分组 | P1 |
|
||||
| | `/api/group/_add` | POST | 新增分组 | P2 |
|
||||
| | `/api/group/_delete` | POST | 删除分组 | P2 |
|
||||
| | `/api/group/_move` | POST | 移动终端到指定分组 | P2 |
|
||||
| | `/api/group/_modify` | POST | 修改分组名称 | P2 |
|
||||
| **终端信息** | `/api/clnts/_list` | POST | 查询终端基本信息 | **P0** |
|
||||
| | `/api/clnts/_info` | POST | 获取终端详细信息 | **P0** |
|
||||
| | `/api/clnts/_info2` | POST | 终端详细信息v2(可选字段) | **P0** |
|
||||
| | `/api/clnts/_online` | POST | 查询上线终端 | P1 |
|
||||
| | `/api/clnts/_leak` | POST | 查询高危漏洞终端 | **P0** |
|
||||
| | `/api/clnts/_virus_events` | POST | 统计病毒事件 | **P0** |
|
||||
| **终端任务** | `/api/task/_create` | POST | 创建任务(type区分) | P1 |
|
||||
| | ↳ type=quick_scan | | 快速扫描 | P1 |
|
||||
| | ↳ type=full_scan | | 全盘扫描 | P2 |
|
||||
| | ↳ type=custom_scan | | 自定义扫描 | P2 |
|
||||
| | ↳ type=netctrl | | 终端隔离/解除 | **P0**(安全场景) |
|
||||
| | ↳ type=message | | 发送通知 | P1 |
|
||||
| **软件管理** | `/api/swinfo/_search` | POST | 查询软件信息 | P1 |
|
||||
|
||||
### 1.3 关键数据结构
|
||||
|
||||
**终端基本信息** (`/api/clnts/_list` 返回):
|
||||
```
|
||||
client_id // 终端唯一ID(40位十六进制字符串,用于所有任务下发)
|
||||
computer_name // 计算机名
|
||||
mac // MAC地址
|
||||
ip // 本地IP
|
||||
os_version // 操作系统版本
|
||||
is_online // 在线状态 (bool)
|
||||
group_id // 分组ID
|
||||
group_name // 分组名称
|
||||
```
|
||||
|
||||
**终端详细信息v2** (`/api/clnts/_info2`,可选信息块):
|
||||
```
|
||||
hardware: { cpu, memory, disk, motherboard, network_card } // 硬件信息
|
||||
software: { installed_apps[] } // 已安装软件
|
||||
assets: { asset_tag, serial_number } // 资产信息
|
||||
netconf: { ip, gateway, dns, adapter_info } // 网络配置
|
||||
```
|
||||
|
||||
**高危漏洞信息** (`/api/clnts/_leak` 返回):
|
||||
```
|
||||
client_id, computer_name, mac, ip
|
||||
leaks: [{ leak_id, name, level, description, publish_time }]
|
||||
```
|
||||
|
||||
**病毒事件统计** (`/api/clnts/_virus_events` 返回):
|
||||
```
|
||||
client_id, computer_name, mac
|
||||
total_events // 事件总数
|
||||
auto_cleaned // 自动处理数
|
||||
manual_cleaned // 手动处理数
|
||||
uncleaned // 未处理数
|
||||
```
|
||||
|
||||
**终端隔离任务** (`type=netctrl`):
|
||||
```
|
||||
net_isolation: true // 隔离终端(断网)
|
||||
net_isolation: false // 解除隔离(恢复网络)
|
||||
clients: ["client_id_1", "client_id_2"] // 目标终端
|
||||
```
|
||||
|
||||
**软件信息查询** (`/api/swinfo/_search`):
|
||||
- 按软件统计 (groupby=software.list):软件名+发布者+安装数+安装率
|
||||
- 按版本统计 (groupby=softwareVer.list):软件名+发布者+版本+安装数
|
||||
- 按终端统计 (groupby=client.list):终端名+分组+IP+MAC+软件安装总数
|
||||
|
||||
---
|
||||
|
||||
## 二、产品维度分析
|
||||
|
||||
### 2.1 场景匹配度评估
|
||||
|
||||
| IT服务台场景 | 对应火绒API | 匹配度 | 说明 |
|
||||
|-------------|-----------|--------|------|
|
||||
| 员工报修「电脑卡/慢」 | `_info2`(hardware) | ⭐⭐⭐⭐⭐ | 直接获取CPU/内存/磁盘,判断是否硬件瓶颈 |
|
||||
| 员工报修「中病毒了」 | `_virus_events` + `_list` | ⭐⭐⭐⭐⭐ | 精确查看该终端病毒事件及处理状态 |
|
||||
| 员工报修「软件安装问题」 | `_info2`(software) | ⭐⭐⭐⭐ | 查看已安装软件列表及版本 |
|
||||
| 坐席排查「网络问题」 | `_info2`(netconf) | ⭐⭐⭐⭐ | 查看IP/网关/DNS配置 |
|
||||
| 坐席排查「安全漏洞」 | `_leak` | ⭐⭐⭐⭐⭐ | 直接获取高危漏洞列表及修复状态 |
|
||||
| 安全应急「隔离中毒终端」 | `_create`(netctrl) | ⭐⭐⭐⭐⭐ | 一键隔离/解除,黄金场景 |
|
||||
| 安全巡检「批量扫描」 | `_create`(quick_scan) | ⭐⭐⭐ | 坐席可远程触发扫描 |
|
||||
| 软件合规审计 | `_search`(software.list) | ⭐⭐⭐ | 查询软件安装率和版本分布 |
|
||||
|
||||
### 2.2 集成功能规划(按优先级)
|
||||
|
||||
#### P0 — 核心查询能力(阶段三 3A-3B)
|
||||
|
||||
| 功能 | 用户侧效果 | 涉及API |
|
||||
|------|-----------|---------|
|
||||
| **终端安全画像** | 坐席打开会话→自动展示该员工终端的在线状态/系统版本/硬件概要/安全评分 | `_list` + `_info2` |
|
||||
| **漏洞预警卡片** | AI Wingman自动检测→推送高危漏洞提醒→坐席一键查看详情 | `_leak` |
|
||||
| **病毒事件看板** | 展示该终端病毒事件统计(已处理/未处理)| `_virus_events` |
|
||||
| **一键隔离/解除** | 安全事件→坐席在排查流程中点击按钮→火绒执行隔离 | `_create`(netctrl) |
|
||||
|
||||
#### P1 — 增强排查能力(阶段三 3C + 阶段四)
|
||||
|
||||
| 功能 | 用户侧效果 | 涉及API |
|
||||
|------|-----------|---------|
|
||||
| **远程触发扫描** | 坐席一键下发快速扫描→等待结果→自动回传 | `_create`(quick_scan) |
|
||||
| **发送安全通知** | 坐席向终端推送安全提醒(如「请尽快更新系统」)| `_create`(message) |
|
||||
| **软件清单查询** | 输入员工工号→自动列出已安装软件+版本 | `_search`(client.list) |
|
||||
| **终端上线检查** | 排查网络问题时检查终端是否在线 | `_online` |
|
||||
|
||||
#### P2 — 管理与运营(阶段四 4B)
|
||||
|
||||
| 功能 | 用户侧效果 | 涉及API |
|
||||
|------|-----------|---------|
|
||||
| **安全数据看板** | 管理后台展示公司终端安全态势(漏洞分布/病毒事件趋势/隔离终端数) | `_leak` + `_virus_events` + `_list` |
|
||||
| **软件合规报告** | 统计软件安装率、版本分布,发现违规软件 | `_search` |
|
||||
| **终端分组视图** | 按部门/分组展示终端安全状况 | `_group/_list` + `_list` |
|
||||
|
||||
### 2.3 产品建议
|
||||
|
||||
#### ✅ 推荐:分三步走集成策略
|
||||
|
||||
**第一步(P0,约2周)**:只做「查」——终端安全画像+漏洞/病毒查询
|
||||
- 坐席端集成:会话面板右侧新增「终端安全」标签页
|
||||
- 数据获取方式:**被动查询**(坐席点击查看 / AI Wingman自动推送),不主动定时同步
|
||||
- 理由:纯查询零风险,不影响火绒系统运行,且立刻为坐席提供关键信息
|
||||
|
||||
**第二步(P1,约2周)**:增加「控」——远程扫描+通知+隔离
|
||||
- 坐席端集成:排查流程图中新增「安全操作」节点
|
||||
- 操作需**二次确认**(尤其是隔离操作,需弹窗确认+记录审计日志)
|
||||
- 理由:控制类操作有安全风险,需坐席主动触发,不适合AI自动执行
|
||||
|
||||
**第三步(P2,约1周)**:做「看」——管理后台数据看板
|
||||
- 管理后台集成:新增「终端安全态势」页面
|
||||
- 数据获取方式:定时同步(每天凌晨增量拉取)
|
||||
- 理由:管理视角的聚合数据,时效性要求低,可用批处理
|
||||
|
||||
#### ⚠️ 关键产品约束
|
||||
|
||||
1. **隔离操作必须人工确认**:`net_isolation=true` 是高危操作,禁止AI自动执行,必须坐席点击确认
|
||||
2. **扫描任务需控制频率**:同一终端5分钟内不得重复下发扫描任务
|
||||
3. **数据展示需脱敏**:终端MAC/IP等敏感信息,在H5用户端不展示(仅坐席端可见)
|
||||
4. **API调用需降级容错**:火绒系统不可用时,终端安全标签页显示「暂不可用」,不影响主流程
|
||||
|
||||
---
|
||||
|
||||
## 三、开发维度分析
|
||||
|
||||
### 3.1 架构设计
|
||||
|
||||
#### 整体架构
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌──────────────┐ ┌──────────────────┐
|
||||
│ 坐席工作台 │────▶│ 后端 API │────▶│ 火绒 API │
|
||||
│ / 管理后台 │ │ (FastAPI) │ │ (内网:8080) │
|
||||
└─────────────┘ └──────────────┘ └──────────────────┘
|
||||
│
|
||||
┌─────┴──────┐
|
||||
│ Redis │
|
||||
│ 缓存层 │
|
||||
└────────────┘
|
||||
```
|
||||
|
||||
#### 后端模块设计
|
||||
|
||||
```
|
||||
backend/app/
|
||||
├── integrations/
|
||||
│ ├── __init__.py
|
||||
│ ├── huorong/ # 火绒集成模块
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── client.py # 火绒API客户端(签名+请求)
|
||||
│ │ ├── config.py # 配置(AccessKey/Secret/BaseUrl)
|
||||
│ │ ├── models.py # 数据模型(Pydantic)
|
||||
│ │ ├── cache.py # 缓存策略
|
||||
│ │ └── exceptions.py # 自定义异常
|
||||
│ └── base.py # 集成基类(供未来联软等复用)
|
||||
├── api/
|
||||
│ └── integrations.py # 新增:集成API路由
|
||||
└── services/
|
||||
└── integration_service.py # 新增:集成业务逻辑
|
||||
```
|
||||
|
||||
### 3.2 签名实现
|
||||
|
||||
火绒使用 HMAC-SHA1 签名,Python 实现要点:
|
||||
|
||||
```python
|
||||
import hmac
|
||||
import hashlib
|
||||
import base64
|
||||
import time
|
||||
import uuid
|
||||
|
||||
def sign_request(access_key_id: str, access_key_secret: str,
|
||||
method: str, path: str, body: str = "") -> dict:
|
||||
"""
|
||||
火绒API签名实现
|
||||
- method: HTTP方法(POST)
|
||||
- path: 请求路径(如 /api/clnts/_list)
|
||||
- body: 请求体JSON字符串
|
||||
返回: 需附加到请求的Header字典
|
||||
"""
|
||||
timestamp = str(int(time.time()))
|
||||
nonce = str(uuid.uuid4())
|
||||
|
||||
# 签名字符串 = Method + Path + Timestamp + Nonce + Body
|
||||
string_to_sign = f"{method}\n{path}\n{timestamp}\n{nonce}\n{body}"
|
||||
|
||||
# HMAC-SHA1签名
|
||||
signature = base64.b64encode(
|
||||
hmac.new(
|
||||
access_key_secret.encode("utf-8"),
|
||||
string_to_sign.encode("utf-8"),
|
||||
hashlib.sha1
|
||||
).digest()
|
||||
).decode("utf-8")
|
||||
|
||||
return {
|
||||
"X-Access-Key-Id": access_key_id,
|
||||
"X-Signature": signature,
|
||||
"X-Timestamp": timestamp,
|
||||
"X-Nonce": nonce,
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 缓存策略
|
||||
|
||||
火绒API属于**内部系统调用**,数据时效性与调用频率需平衡:
|
||||
|
||||
| 数据类型 | 缓存时间 | 理由 |
|
||||
|---------|---------|------|
|
||||
| 终端基本信息 (`_list`) | 5分钟 | 终端上下线状态变化较频繁 |
|
||||
| 终端详细信息 (`_info2`) | 10分钟 | 硬件/软件变化慢,但坐席可能实时查询 |
|
||||
| 漏洞信息 (`_leak`) | 30分钟 | 漏洞修复周期通常为天级 |
|
||||
| 病毒事件 (`_virus_events`) | 5分钟 | 安全事件需较实时展示 |
|
||||
| 分组信息 (`_group/_list`) | 1小时 | 分组变更极少 |
|
||||
| 软件信息 (`_swinfo/_search`) | 1小时 | 软件安装变化慢 |
|
||||
|
||||
**缓存Key设计**:
|
||||
```
|
||||
huorong:clnts:list:{group_id}:{page} # 终端列表
|
||||
huorong:clnts:info2:{client_id}:{fields} # 终端详情
|
||||
huorong:leak:{group_id}:{page} # 漏洞信息
|
||||
huorong:virus:{client_id} # 病毒事件
|
||||
```
|
||||
|
||||
### 3.4 员工→终端映射方案
|
||||
|
||||
火绒API以 `client_id`(40位十六进制)标识终端,而IT服务台以 `employee_id` 标识员工。需要映射:
|
||||
|
||||
#### 早期方案(仅考虑火绒)
|
||||
|
||||
| 方案 | 原理 | 优点 | 缺点 | 推荐 |
|
||||
|------|------|------|------|------|
|
||||
| **方案A:computer_name匹配** | 火绒`computer_name` = 公司电脑命名规则(如`DESKTOP-工号`或`姓名-部门`)| 无需额外数据源 | 依赖命名规范一致性 | ⭐⭐⭐ |
|
||||
| **方案B:eHR+火绒交叉匹配** | eHR取员工IP/MAC → 火绒按IP/MAC查终端 | 精确匹配 | 需eHR接口支持,IP可能变化 | ⭐⭐⭐⭐ |
|
||||
| **方案C:手动绑定** | 员工首次报修时坐席手动关联 | 最灵活 | 运营成本高,容易遗漏 | ⭐⭐ |
|
||||
|
||||
#### ⭐ 升级方案D:联软直接映射(推荐)
|
||||
|
||||
> **重大发现**(2026-06-11补充):联软LV7000的 `queryDevByParams` 接口直接返回 `strusername`(员工账号)+ `struserdes`(员工姓名),且总部员工必须安装联软安全助手,**天然具备最准确的员工↔终端映射**。
|
||||
|
||||
**新映射架构(多源融合)**:
|
||||
|
||||
```
|
||||
联软(主源)→ strusername 精确匹配员工账号 → 获取终端IP/MAC/计算机名
|
||||
↓ 用联软获取的IP/MAC去火绒查
|
||||
火绒(安全源)→ 按IP/MAC匹配client_id → 获取安全状态
|
||||
↓ 远程办公员工
|
||||
aTrust(VPN源)→ VPN登录账号匹配 → 获取VPN终端
|
||||
```
|
||||
|
||||
**实现流程**:
|
||||
1. 输入 `employee_id` → 联软 `queryDevByParams(strusername=employee_id)` → 获取终端列表
|
||||
2. 取终端IP/MAC → 火绒 `_list` 按IP查 `client_id` → 获取安全状态
|
||||
3. 建立映射 `employee_id → [{leagsoft终端信息, huorong安全信息}]`
|
||||
4. aTrust补全VPN终端(远程办公员工)
|
||||
|
||||
> 详见 `docs/联软终端安全系统集成分析.md` §4.4
|
||||
|
||||
### 3.5 前端集成设计
|
||||
|
||||
#### 坐席端新增
|
||||
|
||||
```
|
||||
坐席工作台
|
||||
└── 右侧面板
|
||||
└── 「终端安全」标签页(与AI推送区并列)
|
||||
├── 终端概要卡片
|
||||
│ ├── 在线状态 🟢/🔴
|
||||
│ ├── OS版本
|
||||
│ ├── 硬件概要(CPU/内存/磁盘)
|
||||
│ └── 安全评分(综合漏洞+病毒事件)
|
||||
├── 安全事件列表
|
||||
│ ├── 🔴 高危漏洞 (N个)
|
||||
│ ├── 🟡 未处理病毒事件 (N个)
|
||||
│ └── 🟢 安全状态正常
|
||||
└── 快速操作
|
||||
├── 📡 快速扫描
|
||||
├── 🔒 隔离终端 (需二次确认)
|
||||
├── 🔓 解除隔离
|
||||
└── 📢 发送通知
|
||||
```
|
||||
|
||||
#### 管理后台新增
|
||||
|
||||
```
|
||||
管理后台
|
||||
└── 终端安全态势页面(阶段四 P2)
|
||||
├── 全局指标卡片
|
||||
│ ├── 终端总数 / 在线数 / 离线数
|
||||
│ ├── 高危漏洞终端数
|
||||
│ └── 未处理病毒事件数
|
||||
├── 漏洞分布图(按等级/部门)
|
||||
├── 病毒事件趋势图(7天/30天)
|
||||
└── 隔离终端列表
|
||||
```
|
||||
|
||||
### 3.6 开发风险与应对
|
||||
|
||||
| 风险 | 影响 | 概率 | 应对措施 |
|
||||
|------|------|------|---------|
|
||||
| 火绒API签名实现有误 | 无法调用任何接口 | 中 | 先用Postman/curl验证签名逻辑,再编码 |
|
||||
| 内网地址不通 | 开发环境无法调试 | 高 | 需VPN或开发机部署在内网 |
|
||||
| AccessKey权限不足 | 部分API返回权限错误 | 中 | 提前与信息安全团队确认API账户权限范围 |
|
||||
| API响应超时 | 坐席端体验卡顿 | 低 | 所有火绒调用设3秒超时+异步加载+降级展示 |
|
||||
| 火绒版本升级API变更 | 集成失效 | 低 | 记录当前API版本号,抽象接口层便于适配 |
|
||||
| 并发查询量过大 | 触发火绒限流 | 低 | 缓存+合并查询+限制QPS≤10 |
|
||||
|
||||
---
|
||||
|
||||
## 四、安全维度分析
|
||||
|
||||
### 4.1 认证安全
|
||||
|
||||
| 风险项 | 等级 | 说明 | 建议 |
|
||||
|--------|------|------|------|
|
||||
| AccessKey Secret泄露 | **严重** | 泄露后可调用所有火绒API,包括隔离终端 | Secret必须存环境变量,**禁止**写入代码/配置文件 |
|
||||
| API签名重放攻击 | 中 | 同一请求可被截获重放 | 火绒已内置timestamp+nonce防重放,但需确认服务端校验 |
|
||||
| 传输层安全 | 中 | 内网HTTP明文传输 | 内网可接受;若跨网段需走HTTPS代理 |
|
||||
|
||||
**AccessKey管理建议**:
|
||||
```python
|
||||
# .env 配置(不提交Git)
|
||||
HUORONG_ACCESS_KEY_ID=你的AccessKeyID
|
||||
HUORONG_ACCESS_KEY_SECRET=你的AccessKeySecret
|
||||
HUORONG_BASE_URL=http://huorong.oa.servyou-it.com:8080
|
||||
```
|
||||
|
||||
### 4.2 操作安全
|
||||
|
||||
| 操作 | 风险等级 | 安全要求 |
|
||||
|------|---------|---------|
|
||||
| 查询终端信息 (`_list`/`_info2`) | 🟢 低 | 无特殊要求 |
|
||||
| 查询漏洞/病毒事件 (`_leak`/`_virus_events`) | 🟢 低 | 无特殊要求 |
|
||||
| 发送通知 (`_create` message) | 🟡 中 | 记录审计日志(谁发的、发给谁、内容) |
|
||||
| 远程扫描 (`_create` scan) | 🟡 中 | 记录审计日志;限制同一终端5分钟内不重复扫描 |
|
||||
| **隔离终端** (`_create` netctrl) | 🔴 **高** | **必须**二次确认弹窗 + 审计日志 + 管理员审批(阶段四后) |
|
||||
|
||||
**隔离操作安全流程**:
|
||||
```
|
||||
坐席点击「隔离终端」
|
||||
↓
|
||||
弹窗确认:⚠️ 确认隔离该终端?该操作将断开终端网络连接
|
||||
↓
|
||||
输入隔离原因(必填,≥10字)
|
||||
↓
|
||||
[执行隔离] → 调用API → 记录审计日志
|
||||
↓
|
||||
通知被隔离终端用户(通过企微消息)
|
||||
```
|
||||
|
||||
### 4.3 数据安全
|
||||
|
||||
| 风险项 | 说明 | 建议 |
|
||||
|--------|------|------|
|
||||
| 终端敏感信息泄露 | MAC/IP/资产编号等敏感数据 | H5用户端不展示,仅坐席端可见 |
|
||||
| 火绒数据本地存储 | 缓存数据包含终端信息 | Redis缓存设TTL自动过期,不落盘到数据库 |
|
||||
| API响应数据清洗 | 火绒返回全量字段 | 后端只透传必要字段,过滤内部敏感字段 |
|
||||
|
||||
### 4.4 权限设计
|
||||
|
||||
| 角色 | 查询终端 | 远程扫描 | 隔离终端 | 发送通知 |
|
||||
|------|---------|---------|---------|---------|
|
||||
| 普通坐席 (agent) | ✅ | ✅ | ❌ | ✅ |
|
||||
| 管理员 (admin) | ✅ | ✅ | ✅ | ✅ |
|
||||
| H5用户 | ❌ | ❌ | ❌ | ❌ |
|
||||
|
||||
> 隔离操作初期仅限admin角色,待流程成熟后可下放至agent(需配置审批流)
|
||||
|
||||
### 4.5 审计日志
|
||||
|
||||
所有火绒API调用(尤其是写入/控制操作)必须记录审计日志:
|
||||
|
||||
```python
|
||||
class HuorongAuditLog(Base):
|
||||
__tablename__ = "huorong_audit_logs"
|
||||
|
||||
id: Mapped[int] # 主键
|
||||
agent_id: Mapped[int] # 操作坐席ID
|
||||
action: Mapped[str] # 操作类型: query/scan/isolate/notify
|
||||
target_client_id: Mapped[str] # 目标终端client_id
|
||||
request_params: Mapped[dict] # 请求参数(JSON)
|
||||
response_code: Mapped[int] # 火绒返回码
|
||||
reason: Mapped[str] # 操作原因(隔离时必填)
|
||||
created_at: Mapped[datetime] # 操作时间
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、实施路线图
|
||||
|
||||
### 阶段一(P0,约2周)— 查询能力集成
|
||||
|
||||
```
|
||||
Week 1: 后端集成
|
||||
├── Day 1-2: 火绒API客户端开发(签名+请求+异常处理)
|
||||
├── Day 3-4: 缓存层+员工-终端映射逻辑
|
||||
└── Day 5: API端点开发+单元测试
|
||||
|
||||
Week 2: 前端集成
|
||||
├── Day 1-3: 坐席端「终端安全」标签页UI+数据对接
|
||||
├── Day 4: AI Wingman接入漏洞/病毒推送
|
||||
└── Day 5: 集成测试+Bug修复
|
||||
```
|
||||
|
||||
**前置条件**:
|
||||
- [ ] 联系信息安全团队获取AccessKey ID/Secret
|
||||
- [ ] 确认开发环境可访问火绒内网地址
|
||||
- [ ] 确认API账户权限范围(是否包含所有端点)
|
||||
|
||||
### 阶段二(P1,约2周)— 控制能力集成
|
||||
|
||||
```
|
||||
Week 3: 后端开发
|
||||
├── Day 1-2: 任务下发API(扫描/通知/隔离)
|
||||
├── Day 3: 审计日志模块
|
||||
└── Day 4-5: 权限控制+二次确认流程
|
||||
|
||||
Week 4: 前端+联调
|
||||
├── Day 1-3: 安全操作按钮+确认流程UI
|
||||
├── Day 4: 排查流程图新增安全节点
|
||||
└── Day 5: 端到端测试
|
||||
```
|
||||
|
||||
### 阶段三(P2,约1周)— 管理看板
|
||||
|
||||
```
|
||||
Week 5: 数据看板
|
||||
├── Day 1-2: 定时同步任务+数据聚合
|
||||
├── Day 3-4: 管理后台安全态势页面
|
||||
└── Day 5: 验收测试
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、与现有系统的协同
|
||||
|
||||
### 6.1 与eHR集成协同
|
||||
|
||||
| 维度 | eHR提供 | 火绒提供 | 协同效果 |
|
||||
|------|---------|---------|---------|
|
||||
| 员工身份 | 姓名/工号/部门 | 计算机名/MAC/IP | 建立员工↔终端映射 |
|
||||
| 资产信息 | 资产编号/领用日期 | 硬件配置/序列号 | 交叉验证资产归属 |
|
||||
| 任职信息 | 岗位/入职日期 | 无直接关联 | 判断安全基线适用范围 |
|
||||
|
||||
**关键映射逻辑**:
|
||||
```
|
||||
eHR: employee_id → 办公IP/资产编号
|
||||
火绒: IP/MAC → client_id → 安全状态
|
||||
IT服务台: employee_id → conversation → 坐席 → 查看安全状态
|
||||
```
|
||||
|
||||
### 6.2 与AI Wingman协同
|
||||
|
||||
| AI推送场景 | 触发条件 | 推送内容 |
|
||||
|-----------|---------|---------|
|
||||
| 漏洞预警 | 员工终端有高危漏洞未修复 | 「⚠️ 该员工终端存在N个高危漏洞,建议优先处理」 |
|
||||
| 病毒预警 | 员工终端有未处理病毒事件 | 「🔴 该终端检测到N个未处理病毒事件,建议立即排查」 |
|
||||
| 离线提醒 | 员工终端不在线 | 「💡 该终端当前离线,部分远程操作不可用」 |
|
||||
| 安全评分 | 综合漏洞+病毒+在线状态 | 「终端安全评分:72/100,主要扣分项:3个高危漏洞」 |
|
||||
|
||||
### 6.3 与排查流程图协同
|
||||
|
||||
在阶段三的排查流程图中新增安全相关节点:
|
||||
|
||||
```
|
||||
[开始] → [确认员工身份] → [检查终端在线状态]
|
||||
↓ (在线)
|
||||
[安全基线检查] → 有高危漏洞?→ [推送漏洞详情] → [建议隔离/扫描]
|
||||
↓ (无漏洞)
|
||||
[病毒事件检查] → 有未处理事件?→ [推送病毒详情] → [触发扫描]
|
||||
↓ (安全)
|
||||
[继续常规排查...]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、对接前准备清单
|
||||
|
||||
### 必须完成(阻塞性)
|
||||
|
||||
- [ ] **获取AccessKey**:联系信息安全团队(潘工/信息安全组),申请API调用权限的AccessKey ID + Secret
|
||||
- [ ] **网络可达**:确认部署服务器/开发机可访问 `huorong.oa.servyou-it.com:8080`
|
||||
- [ ] **权限范围确认**:确认API账户可调用哪些端点(尤其是隔离操作的权限)
|
||||
- [ ] **电脑命名规范确认**:了解公司电脑命名规则(是否包含工号/姓名),影响员工↔终端映射方案
|
||||
|
||||
### 建议完成(非阻塞)
|
||||
|
||||
- [ ] **火绒版本确认**:确认当前火绒版本是否与API文档一致
|
||||
- [ ] **限流策略确认**:确认API调用频率限制(QPS/每日调用上限)
|
||||
- [ ] **联软集成调研**:同步了解联软安全系统的API开放性,为未来双系统接入做准备
|
||||
- [ ] **测试终端准备**:准备1-2台测试用终端,用于开发调试
|
||||
|
||||
---
|
||||
|
||||
## 八、总结
|
||||
|
||||
### 8.1 核心结论
|
||||
|
||||
1. **火绒API成熟可用**:17个端点覆盖终端查询/控制/软件管理,签名机制标准,响应格式统一
|
||||
2. **与IT服务台场景高度匹配**:员工报修场景中80%涉及终端问题,火绒数据可直接赋能坐席
|
||||
3. **安全隔离是杀手级功能**:坐席一键隔离中毒终端,将安全事件响应从「小时级」缩短到「秒级」
|
||||
4. **实现成本低**:纯REST API集成,无需安装agent或修改现有系统架构
|
||||
5. **安全风险可控**:查询类零风险,控制类通过权限+审计+二次确认三层防护
|
||||
|
||||
### 8.2 投入产出比
|
||||
|
||||
| 维度 | 评估 |
|
||||
|------|------|
|
||||
| 开发投入 | 约5周(含P0+P1+P2) |
|
||||
| 坐席效率提升 | 安全类工单处理时间预计降低40%+ |
|
||||
| 安全响应提速 | 终端隔离从小时级→秒级 |
|
||||
| 数据价值 | 首次将终端安全数据引入IT服务流程 |
|
||||
| 风险 | 低(纯API集成,不修改火绒系统本身) |
|
||||
|
||||
### 8.3 一句话总结
|
||||
|
||||
> 火绒集成是智能IT支持服务台从「被动响应」走向「主动安全」的关键一步,建议优先推进P0查询能力,2周内可上线见效。
|
||||
@@ -0,0 +1,797 @@
|
||||
# 联软LV7000终端安全管理系统集成分析
|
||||
|
||||
> 基于联软LV7000系列LeagView5版本API接口说明文档(202210SP v1.1)
|
||||
> 分析日期:2026-06-11
|
||||
> 分析人:智能IT支持服务台项目组
|
||||
|
||||
---
|
||||
|
||||
## 一、联软API全景概览
|
||||
|
||||
### 1.1 认证机制
|
||||
|
||||
联软提供**三层认证**,灵活度高于火绒:
|
||||
|
||||
| 认证模式 | 说明 | 适用场景 |
|
||||
|---------|------|---------|
|
||||
| **白名单IP验证**(默认启用) | 配置`WhiteListServerIp`允许的调用方IP | 内网系统间调用 |
|
||||
| **用户名密码验证** | 配置`ApiAccount`+`ApiPassword`,调用时传`apiAccount`+`apiPassword`+`validatekey` | 跨网段调用 |
|
||||
| **一次性Token验证** | 先调`/token?act=getToken`获取token(默认30分钟有效),业务接口带`token`参数 | 安全要求高的场景 |
|
||||
|
||||
**端口**:`30098`(所有API统一端口)
|
||||
|
||||
**响应格式统一**:
|
||||
```json
|
||||
{
|
||||
"status": "SUCCESS | ERROR | INVALID | Exceed",
|
||||
"msg": "描述信息",
|
||||
"rows": [...], // 数据列表(部分接口用"row")
|
||||
"total": 100 // 总记录数
|
||||
}
|
||||
```
|
||||
|
||||
- `SUCCESS`:成功
|
||||
- `ERROR`:参数错误/业务失败
|
||||
- `INVALID`:无权限(IP不在白名单)
|
||||
- `Exceed`:数据量超限(仅仿冒设备接口)
|
||||
|
||||
### 1.2 API端点分类总览(68个)
|
||||
|
||||
| 大类 | 数量 | 核心端点 | 对IT服务台价值 |
|
||||
|------|------|---------|--------------|
|
||||
| **终端设备** | 8 | `queryDevByParams`, `getDevAllInfo`, `querysoftwarebydev` | ⭐⭐⭐⭐⭐ 极高 |
|
||||
| **准入控制** | 7 | `existOnlineUser`, `onlineUserList`, `forcedOffline`, `queryAccessLog` | ⭐⭐⭐⭐ 高 |
|
||||
| **组织架构/用户** | 8 | `getUserInfo`, `getUserInfoByAccount`, `getAllOrgInfo`, `getDeptInfo` | ⭐⭐⭐⭐⭐ 极高(映射) |
|
||||
| **审计查询** | 4 | `queryCommonAuditInfo`, `queryClientPatchAuditInfo` | ⭐⭐⭐ 中 |
|
||||
| **安全策略** | 5 | `getSecScopeByName`, `addSecpolicyScope` 等 | ⭐⭐ 低 |
|
||||
| **审批流程** | 12 | `queryapprovallist`, `doapproval`, `endapproval` 等 | ⭐⭐ 低 |
|
||||
| **免检设备** | 6 | `addByMac`, `delByMac`, `queryCheckDevList` | ⭐⭐ 低 |
|
||||
| **访客/外协** | 8 | `getGuestAccount`, `create`, `find`, `applyOutsource` | ⭐⭐ 低 |
|
||||
| **其他** | 10 | `noticeAgentMsg`, `remoteWakeUp`, `queryCounterfeitList` 等 | ⭐⭐⭐ 中 |
|
||||
|
||||
### 1.3 核心API详解(对IT服务台有价值的端点)
|
||||
|
||||
#### 1.3.1 🔴 P0级 — 终端设备查询
|
||||
|
||||
**查询指定终端设备** `queryDevByParams`
|
||||
- URL: `http://{IP}:30098/terminal?act=queryDevByParams`
|
||||
- **这是最核心的接口**!返回字段包含:
|
||||
```
|
||||
istatus // 终端状态(在线/离线)
|
||||
strdevname // 计算机名
|
||||
strdevip // IP地址
|
||||
strmac // MAC地址
|
||||
strdeptname // 所属部门名
|
||||
strusername // ⭐ 使用该终端的用户账号
|
||||
struserdes // ⭐ 用户姓名/描述
|
||||
strswitchname // 接入交换机名
|
||||
strifname // 交换机接口名
|
||||
strmail // 用户邮箱
|
||||
strphone // 用户电话
|
||||
```
|
||||
|
||||
> **关键发现**:`strusername` + `struserdes` 字段**直接提供员工账号→终端的映射**!无需通过IP交叉匹配,这是联软相比火绒的最大优势。
|
||||
|
||||
**设备概要详细信息** `getDevAllInfo`
|
||||
- URL: `http://{IP}:30098/devallinfoshowwithpaging?act=getDevAllInfo`
|
||||
- 返回**极其详细**的设备信息:
|
||||
```
|
||||
equipment:
|
||||
strdevname, strip1, strmac, strnatip, macverdor, strdevtype
|
||||
strdeptname, strusername, struserdes
|
||||
dtdevuptime, dtdevdowntime, dtdevfirstfoundtime
|
||||
stros, strdomain, strserialnumber, strmainboardtype
|
||||
|
||||
equipmentdetail:
|
||||
devdetail:
|
||||
strverofuaagent // 安全助手版本
|
||||
istatus // 在线状态
|
||||
uniaccessagentstatus // UniAccess助手状态
|
||||
devassetno // 设备资产号
|
||||
devgroup // 设备所属设备组
|
||||
mainboardInformation[] // 主板(厂商/型号/序列号)
|
||||
CPUInformation[] // CPU(型号/核心/频率/缓存)
|
||||
MemoryInformation[] // 内存(最大/当前/插槽数)
|
||||
HardDiskInformation[] // 硬盘(类型/容量/型号/序列号)
|
||||
LogicalDiskInformation[] // 逻辑盘(卷标/文件系统/总量/可用/使用率)
|
||||
GraphicsCardInformation[] // 显卡
|
||||
NetworkCardInformation[] // 网卡(名称/是否无线/厂商/MAC)
|
||||
DisplayInformation[] // 显示器(厂商/型号/序列号/尺寸)
|
||||
PCIInformation[] // PCI设备
|
||||
MemoryModuleDetails[] // 内存条详情
|
||||
SoundCardInformation[] // 声卡
|
||||
OperatingSystemInformation[] // 操作系统详情(语言/补丁/安装时间)
|
||||
```
|
||||
|
||||
> 比火绒的`_info2`更详细!尤其是**逻辑磁盘使用率**(可直接判断磁盘满导致卡慢)和**显示器信息**(多屏配置排查)。
|
||||
|
||||
**设备安装软件信息** `querysoftwarebydev`
|
||||
- URL: `http://{IP}:30098/software?act=querysoftwarebydev`
|
||||
- 返回:
|
||||
```
|
||||
strdevname, strdevip, strmac, strdomain, strusername
|
||||
softwares: [
|
||||
{ strsoftware, strversion, strvendor, installdate }
|
||||
]
|
||||
```
|
||||
|
||||
#### 1.3.2 🔴 P0级 — 组织架构/用户
|
||||
|
||||
**查询用户信息** `getUserInfo`
|
||||
- URL: `http://{IP}:30098/querydeptuser?act=getUserInfo`
|
||||
- 返回:`deptid, userid, useraccount, username`
|
||||
|
||||
**用户账号查询** `getUserInfoByAccount`
|
||||
- URL: `http://{IP}:30098/querydeptuser?act=getUserInfoByAccount`
|
||||
- **直接通过账号查用户**,映射核心接口!
|
||||
|
||||
**查询部门和用户信息** `getAllOrgInfo`
|
||||
- URL: `http://{IP}:30098/querydeptuser?act=getAllOrgInfo`
|
||||
- 一次性获取所有部门+用户,可做全量同步
|
||||
|
||||
**查询部门信息** `getDeptInfo`
|
||||
- URL: `http://{IP}:30098/querydeptuser?act=getDeptInfo`
|
||||
- 返回所有部门(含父子关系)
|
||||
|
||||
#### 1.3.3 🟡 P1级 — 准入控制
|
||||
|
||||
**查询终端用户是否在线** `existOnlineUser`
|
||||
- URL: `http://{IP}:30098/access/onlineUser?act=existOnlineUser`
|
||||
- 参数:`username` + `strdevip`
|
||||
- 返回:`data: 0`(不在线)/ `1`(在线)
|
||||
- **可精确判断某员工在某IP是否当前在线**
|
||||
|
||||
**查询用户在线列表** `onlineUserList`
|
||||
- URL: `http://{IP}:30098/onlineUser?act=onlineUserList`
|
||||
- 按时间范围查在线用户(间隔不超过3个月)
|
||||
|
||||
**强制下线** `forcedOffline`
|
||||
- URL: `http://{IP}:30098/nac?act=forcedOffline`
|
||||
- ⚠️ 高危操作!将终端从网络强制断开
|
||||
- 与火绒的`netctrl`隔离功能类似但机制不同
|
||||
|
||||
**终端入网日志** `queryAccessLog`
|
||||
- URL: `http://{IP}:30098/access/queryInfo?act=queryAccessLog`
|
||||
- 可查看终端入网历史
|
||||
|
||||
#### 1.3.4 🟡 P1级 — 终端操作
|
||||
|
||||
**通知助手弹出消息** `noticeAgentMsg`
|
||||
- URL: `http://{IP}:30098/terminal?act=noticeAgentMsg`
|
||||
- 向终端安全助手推送弹窗消息
|
||||
|
||||
**设备远程唤醒** `remoteWakeUp`
|
||||
- URL: `http://{IP}:30098/terminal?act=remoteWakeUp`
|
||||
- 通过IP+MAC唤醒关机/休眠的终端
|
||||
|
||||
**补丁安装审计** `queryClientPatchAuditInfo`
|
||||
- URL: `http://{IP}:30098/terminalaudit?act=queryClientPatchAuditInfo`
|
||||
- 查看终端补丁安装状态(补丁名/KB号/安装时间/是否成功)
|
||||
|
||||
**查询助手安装率** `queryAgentInstallRate`
|
||||
- URL: `http://{IP}:30098/terminal?act=queryAgentInstallRate`
|
||||
- 返回Windows/macOS分别的安装率
|
||||
|
||||
**查询终端补丁安装率** `queryAllMspatchInstallRate`
|
||||
- URL: `http://{IP}:30098/terminal?act=queryAllMspatchInstallRate`
|
||||
- 返回全公司补丁安装率
|
||||
|
||||
#### 1.3.5 🟢 P2级 — 审计与安全
|
||||
|
||||
**通用审计信息查询** `queryCommonAuditInfo`
|
||||
- URL: `http://{IP}:30098/auditinfo?act=queryCommonAuditInfo`
|
||||
- 支持所有通用审计类型(文件操作/进程控制等)
|
||||
|
||||
**仿冒设备查询** `queryCounterfeitList`
|
||||
- URL: `http://{IP}:30098/access/queryInfo?act=queryCounterfeitList`
|
||||
- 查询准入仿冒信息(发现仿冒设备告警)
|
||||
|
||||
**屏幕录像审计** `listScreenAuditInfo`
|
||||
- URL: `http://{IP}:30098/auditinfo?act=listScreenAuditInfo`
|
||||
- 获取终端屏幕录像审计信息
|
||||
|
||||
**Syslog推送**
|
||||
- 联软支持将审计日志通过Syslog推送到第三方平台(UDP 514)
|
||||
- 吞吐量:约100条/秒
|
||||
- 支持:文件读写审计、非授权外联审计、打印审计、漏洞审计、进程检测审计、安全U盘审计
|
||||
|
||||
---
|
||||
|
||||
## 二、与火绒的能力对比
|
||||
|
||||
### 2.1 功能矩阵对比
|
||||
|
||||
| 能力维度 | 火绒 | 联软 | 对IT服务台价值 |
|
||||
|---------|------|------|--------------|
|
||||
| **员工↔终端映射** | ❌ 只有computer_name | ✅ **strusername+struserdes** | 🔴 **最关键差异** |
|
||||
| **终端基本信息** | ✅ `_list`(client_id/name/ip/mac/在线) | ✅ `queryDevByParams`(更丰富) | 相当 |
|
||||
| **终端详细信息** | ✅ `_info2`(硬件+软件+资产+网络) | ✅ `getDevAllInfo`(**更详细**:含磁盘使用率/显示器/内存条详情) | 联软胜 |
|
||||
| **病毒事件** | ✅ `_virus_events`(病毒统计+处理状态) | ❌ 无专门接口 | **火绒独有** |
|
||||
| **高危漏洞** | ✅ `_leak`(漏洞等级+详情) | ✅ `queryClientPatchAuditInfo`(补丁审计) | 火绒更直观 |
|
||||
| **终端隔离** | ✅ `netctrl`(网络隔离/解除) | ✅ `forcedOffline`(强制下线) | 火绒更精细(可隔离+解除) |
|
||||
| **远程扫描** | ✅ `_create`(快速/全盘/自定义扫描) | ❌ 无 | **火绒独有** |
|
||||
| **远程唤醒** | ❌ 无 | ✅ `remoteWakeUp` | **联软独有** |
|
||||
| **消息推送** | ✅ `_create`(message) | ✅ `noticeAgentMsg` | 相当 |
|
||||
| **准入控制** | ❌ 无 | ✅ `existOnlineUser`+`forcedOffline`+`queryAccessLog` | **联软独有** |
|
||||
| **软件管理** | ✅ `_search`(软件安装率/版本分布) | ✅ `querysoftwarebydev`(按设备查软件) | 联软更实用 |
|
||||
| **组织架构** | ❌ 无 | ✅ SCIM同步+部门/用户查询 | **联软独有** |
|
||||
| **审计日志** | ❌ 无 | ✅ Syslog推送+通用审计查询 | **联软独有** |
|
||||
| **仿冒设备** | ❌ 无 | ✅ `queryCounterfeitList` | **联软独有** |
|
||||
| **审批流程** | ❌ 无 | ✅ 完整审批流程API | 低价值 |
|
||||
| **屏幕录像** | ❌ 无 | ✅ 审计信息+图片导出 | 低价值 |
|
||||
|
||||
### 2.2 核心结论
|
||||
|
||||
> **火绒 = 安全防护**(杀毒+漏洞+隔离+扫描)
|
||||
> **联软 = 终端管理**(准入+硬件+软件+映射+审计)
|
||||
>
|
||||
> **两者高度互补,不存在替代关系,应双系统集成!**
|
||||
|
||||
---
|
||||
|
||||
## 三、产品维度分析
|
||||
|
||||
### 3.1 联软独有高价值场景
|
||||
|
||||
| 场景 | 联软API | 用户体验 |
|
||||
|------|---------|---------|
|
||||
| **员工报修「电脑卡/慢」** | `getDevAllInfo` | 坐席直接看到磁盘使用率34%→11%、内存16GB/32GB、CPU负载,一秒定位瓶颈 |
|
||||
| **员工报修「网络连不上」** | `existOnlineUser` + `queryAccessLog` | 坐席查看该员工终端当前是否准入在线、最近入网记录、是否被策略阻断 |
|
||||
| **员工报修「电脑开不了机」** | `remoteWakeUp` | 坐席远程唤醒终端(WOL),员工无需等待IT到现场 |
|
||||
| **员工问「我装了什么软件」** | `querysoftwarebydev` | 输入员工账号→自动列出已安装软件+版本+安装日期 |
|
||||
| **IT查「谁用了这个IP」** | `queryDevByParams` | 按IP反查使用人、部门、MAC,网络冲突排查利器 |
|
||||
| **安全巡检「补丁安装率」** | `queryAllMspatchInstallRate` + `queryClientPatchAuditInfo` | 管理后台展示补丁合规率 |
|
||||
|
||||
### 3.2 联软+火绒联合场景
|
||||
|
||||
| 场景 | 联软提供 | 火绒提供 | 联合效果 |
|
||||
|------|---------|---------|---------|
|
||||
| **坐席打开会话** | 员工→终端映射(strusername) | 终端安全画像(漏洞+病毒) | 一键获知「谁的电脑+什么安全状态」 |
|
||||
| **安全事件响应** | `forcedOffline`快速断网 | `netctrl`精细隔离 | 双重保障:先联软断网→火绒隔离 |
|
||||
| **磁盘满排查** | `getDevAllInfo`磁盘使用率 | `_info2`软件列表 | 联软看磁盘空间→火绒查大文件软件 |
|
||||
| **补丁管理** | `queryClientPatchAuditInfo`安装审计 | `_leak`高危漏洞列表 | 联软看补丁安装结果→火绒看漏洞风险 |
|
||||
| **终端画像** | 硬件详情+准入状态+资产号 | 安全评分+病毒事件 | 360°终端全景 |
|
||||
|
||||
### 3.3 集成功能规划(按优先级)
|
||||
|
||||
#### P0 — 核心查询+映射(阶段三 3A-3B)
|
||||
|
||||
| 功能 | 用户侧效果 | 涉及API |
|
||||
|------|-----------|---------|
|
||||
| **员工→终端映射服务** | 输入员工账号→返回终端列表(IP/MAC/计算机名/部门/在线状态) | `queryDevByParams` + `getUserInfoByAccount` |
|
||||
| **终端详细信息卡片** | 坐席打开会话→自动展示该员工终端的完整硬件+软件信息 | `getDevAllInfo` + `querysoftwarebydev` |
|
||||
| **终端在线状态查询** | AI Wingman自动检测→提示终端是否在线 | `existOnlineUser` |
|
||||
|
||||
#### P1 — 操作+控制(阶段三 3C + 阶段四)
|
||||
|
||||
| 功能 | 用户侧效果 | 涉及API |
|
||||
|------|-----------|---------|
|
||||
| **远程唤醒终端** | 坐席一键唤醒休眠/关机的终端 | `remoteWakeUp` |
|
||||
| **推送助手消息** | 向员工终端弹窗通知(如「请重启电脑安装补丁」) | `noticeAgentMsg` |
|
||||
| **强制下线** | 安全事件→坐席一键断网(与火绒隔离互为补充) | `forcedOffline` |
|
||||
| **补丁审计查询** | 查看某终端补丁安装状态 | `queryClientPatchAuditInfo` |
|
||||
| **入网日志查询** | 排查网络问题时查看终端入网历史 | `queryAccessLog` |
|
||||
|
||||
#### P2 — 管理与运营(阶段四 4B)
|
||||
|
||||
| 功能 | 用户侧效果 | 涉及API |
|
||||
|------|-----------|---------|
|
||||
| **助手安装率看板** | 管理后台展示公司安全助手安装率 | `queryAgentInstallRate` |
|
||||
| **补丁合规率看板** | 管理后台展示补丁安装率 | `queryAllMspatchInstallRate` |
|
||||
| **仿冒设备告警** | 发现仿冒设备自动推送告警 | `queryCounterfeitList` |
|
||||
| **Syslog审计日志** | 联软审计事件实时推送到IT服务台 | Syslog接口 |
|
||||
|
||||
---
|
||||
|
||||
## 四、开发维度分析
|
||||
|
||||
### 4.1 后端模块设计
|
||||
|
||||
```
|
||||
backend/app/
|
||||
├── integrations/
|
||||
│ ├── __init__.py
|
||||
│ ├── base.py # 集成基类
|
||||
│ ├── huorong/ # 火绒集成(已有设计)
|
||||
│ │ ├── client.py
|
||||
│ │ ├── config.py
|
||||
│ │ ├── models.py
|
||||
│ │ ├── cache.py
|
||||
│ │ └── exceptions.py
|
||||
│ ├── leagsoft/ # 联软集成模块
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── client.py # 联软API客户端(认证+请求)
|
||||
│ │ ├── config.py # 配置(BaseUrl/账号密码/Token)
|
||||
│ │ ├── models.py # 数据模型(Pydantic)
|
||||
│ │ ├── cache.py # 缓存策略
|
||||
│ │ └── exceptions.py # 自定义异常
|
||||
│ └── mapping/ # 🆕 统一映射服务
|
||||
│ ├── __init__.py
|
||||
│ ├── service.py # 员工→终端映射核心逻辑
|
||||
│ └── models.py # 映射数据模型
|
||||
├── api/
|
||||
│ └── integrations.py # 集成API路由
|
||||
└── services/
|
||||
└── integration_service.py # 集成业务逻辑
|
||||
```
|
||||
|
||||
### 4.2 认证实现
|
||||
|
||||
联软推荐使用**一次性Token模式**(安全性最高):
|
||||
|
||||
```python
|
||||
import httpx
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
class LeagsoftClient:
|
||||
"""联软API客户端"""
|
||||
|
||||
def __init__(self, base_url: str, api_account: str, api_password: str):
|
||||
self.base_url = base_url # 如 http://leagsoft.oa.servyou-it.com:30098
|
||||
self.api_account = api_account
|
||||
self.api_password = api_password
|
||||
self._token: str | None = None
|
||||
self._token_expire: datetime | None = None
|
||||
|
||||
async def _ensure_token(self) -> str:
|
||||
"""
|
||||
确保token有效,过期则重新获取
|
||||
- 联软token默认30分钟有效
|
||||
- 提前5分钟刷新,避免临界过期
|
||||
"""
|
||||
if self._token and self._token_expire and datetime.now() < self._token_expire - timedelta(minutes=5):
|
||||
return self._token
|
||||
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
resp = await client.get(f"{self.base_url}/token", params={"act": "getToken"})
|
||||
data = resp.json()
|
||||
# 解析token(具体字段需根据实际返回确认)
|
||||
self._token = data.get("token", "")
|
||||
self._token_expire = datetime.now() + timedelta(minutes=25) # 保守25分钟
|
||||
|
||||
return self._token
|
||||
|
||||
async def query_dev_by_params(self, username: str = None, devip: str = None,
|
||||
mac: str = None, devname: str = None) -> list[dict]:
|
||||
"""
|
||||
查询终端设备
|
||||
- username: 员工账号(映射核心参数)
|
||||
- devip: 终端IP
|
||||
- mac: MAC地址
|
||||
- devname: 计算机名
|
||||
- 支持多条件组合查询
|
||||
"""
|
||||
token = await self._ensure_token()
|
||||
params = {"act": "queryDevByParams", "token": token}
|
||||
form_data = {}
|
||||
if username:
|
||||
form_data["strusername"] = username
|
||||
if devip:
|
||||
form_data["strdevip"] = devip
|
||||
if mac:
|
||||
form_data["strmac"] = mac
|
||||
if devname:
|
||||
form_data["strdevname"] = devname
|
||||
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
resp = await client.post(
|
||||
f"{self.base_url}/terminal",
|
||||
params=params,
|
||||
data=form_data
|
||||
)
|
||||
result = resp.json()
|
||||
|
||||
if result.get("status") != "SUCCESS":
|
||||
raise LeagsoftAPIError(result.get("msg", "未知错误"))
|
||||
|
||||
return result.get("rows", [])
|
||||
```
|
||||
|
||||
### 4.3 缓存策略
|
||||
|
||||
| 数据类型 | 缓存时间 | 理由 |
|
||||
|---------|---------|------|
|
||||
| 终端基本信息 (`queryDevByParams`) | 5分钟 | 终端上下线变化较频繁 |
|
||||
| 终端详细信息 (`getDevAllInfo`) | 30分钟 | 硬件信息极少变化 |
|
||||
| 软件安装信息 (`querysoftwarebydev`) | 1小时 | 软件安装变化慢 |
|
||||
| 在线状态 (`existOnlineUser`) | 1分钟 | 需较实时 |
|
||||
| 组织架构 (`getAllOrgInfo`) | 24小时 | 组织变更极少 |
|
||||
| 用户信息 (`getUserInfoByAccount`) | 24小时 | 用户信息变更少 |
|
||||
| 补丁审计 (`queryClientPatchAuditInfo`) | 1小时 | 补丁安装周期为天级 |
|
||||
|
||||
### 4.4 员工→终端映射方案(重大升级)
|
||||
|
||||
#### 原方案回顾
|
||||
|
||||
之前火绒集成分析中,因火绒API只有`computer_name`无员工账号,提出了三种映射方案:
|
||||
|
||||
| 方案 | 原理 | 缺点 |
|
||||
|------|------|------|
|
||||
| A. computer_name匹配 | 依赖命名规范 | 不稳定 |
|
||||
| B. eHR+火绒IP交叉匹配 | eHR取IP→火绒查终端 | 需eHR接口,IP可能变化 |
|
||||
| C. 手动绑定 | 坐席手动关联 | 运营成本高 |
|
||||
|
||||
#### 新方案:联软直接映射(方案D)⭐推荐
|
||||
|
||||
**核心发现**:联软 `queryDevByParams` 接口直接返回 `strusername`(员工账号)和 `struserdes`(员工姓名),且总部员工必须安装联软安全助手,因此**联软拥有最准确的员工↔终端映射数据**。
|
||||
|
||||
```
|
||||
联软 queryDevByParams(strusername="songxian")
|
||||
↓ 返回
|
||||
[
|
||||
{strdevname: "DESKTOP-SX001", strdevip: "10.8.11.21", strmac: "0C:C4:7A:0C:75:B5",
|
||||
strusername: "songxian", struserdes: "宋献", strdeptname: "IT部", istatus: "在线"},
|
||||
{strdevname: "DESKTOP-SX002", strdevip: "10.8.11.22", strmac: "0C:C4:7A:0C:75:B6",
|
||||
strusername: "songxian", struserdes: "宋献", strdeptname: "IT部", istatus: "离线"}
|
||||
]
|
||||
```
|
||||
|
||||
#### 映射架构(多源融合)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────┐
|
||||
│ IT服务台 统一映射服务 │
|
||||
│ (mapping/service.py) │
|
||||
└───────┬──────────┬──────────┬────────────┘
|
||||
│ │ │
|
||||
┌────────▼──┐ ┌────▼─────┐ ┌─▼──────────┐
|
||||
│ 联软 │ │ aTrust │ │ eHR │
|
||||
│ (主源) │ │ (VPN源) │ │ (辅助源) │
|
||||
└───────────┘ └──────────┘ └────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
总部终端映射 远程办公映射 人员基础信息
|
||||
(最准确) (VPN连接时准确) (无终端信息)
|
||||
```
|
||||
|
||||
**映射优先级策略**:
|
||||
|
||||
| 场景 | 数据源 | 匹配键 | 准确度 | 说明 |
|
||||
|------|--------|--------|--------|------|
|
||||
| 总部办公终端 | **联软** | `strusername` = 员工账号 | ⭐⭐⭐⭐⭐ | 最准确,安全助手必装 |
|
||||
| 外网VPN终端 | **aTrust** | VPN登录账号 = 员工账号 | ⭐⭐⭐⭐⭐ | 远程办公时最准确 |
|
||||
| eHR补充 | eHR | 员工工号 = 员工账号 | ⭐⭐⭐ | 无终端映射,仅人员信息 |
|
||||
| 火绒安全数据 | 火绒 | 通过联软映射获得`client_id`→查安全 | ⭐⭐⭐⭐ | 依赖联软映射做桥梁 |
|
||||
|
||||
**映射实现**:
|
||||
|
||||
```python
|
||||
class TerminalMappingService:
|
||||
"""
|
||||
统一员工→终端映射服务
|
||||
优先级: 联软(主) > aTrust(VPN) > 手动绑定
|
||||
"""
|
||||
|
||||
async def get_employee_terminals(self, employee_id: str) -> list[TerminalInfo]:
|
||||
"""
|
||||
根据员工ID获取关联的终端列表
|
||||
|
||||
策略:
|
||||
1. 先查联软(最准确,覆盖总部终端)
|
||||
2. 联软无结果 → 查aTrust(覆盖VPN终端)
|
||||
3. 都无结果 → 返回空,标记为「未发现终端」
|
||||
"""
|
||||
# Step 1: 联软查询
|
||||
leagsoft_terminals = await self.leagsoft_client.query_dev_by_params(
|
||||
username=employee_id
|
||||
)
|
||||
if leagsoft_terminals:
|
||||
return [self._parse_leagsoft_terminal(t) for t in leagsoft_terminals]
|
||||
|
||||
# Step 2: aTrust查询(后续实现)
|
||||
# atrust_terminals = await self.atrust_client.query_user_devices(employee_id)
|
||||
# if atrust_terminals:
|
||||
# return atrust_terminals
|
||||
|
||||
# Step 3: 无结果
|
||||
return []
|
||||
|
||||
async def get_terminal_security(self, employee_id: str) -> TerminalSecurityInfo:
|
||||
"""
|
||||
获取员工终端的安全信息(跨系统聚合)
|
||||
|
||||
流程:
|
||||
1. 联软获取终端列表 → 得到strdevip/strmac
|
||||
2. 用strdevip去火绒查安全状态
|
||||
3. 聚合联软硬件+火绒安全 → 完整画像
|
||||
"""
|
||||
# Step 1: 联软获取终端
|
||||
terminals = await self.get_employee_terminals(employee_id)
|
||||
if not terminals:
|
||||
return TerminalSecurityInfo(available=False, reason="未发现关联终端")
|
||||
|
||||
terminal = terminals[0] # 取主终端
|
||||
|
||||
# Step 2: 火绒查安全(用IP或computer_name匹配)
|
||||
huorong_info = await self.huorong_client.query_by_ip(terminal.ip)
|
||||
|
||||
# Step 3: 聚合
|
||||
return TerminalSecurityInfo(
|
||||
terminal=terminal, # 联软硬件信息
|
||||
security=huorong_info, # 火绒安全信息
|
||||
available=True
|
||||
)
|
||||
```
|
||||
|
||||
### 4.5 前端集成设计
|
||||
|
||||
#### 坐席端新增
|
||||
|
||||
```
|
||||
坐席工作台
|
||||
└── 右侧面板
|
||||
└── 「终端信息」标签页(替代原「终端安全」,合并联软+火绒)
|
||||
├── 终端概要卡片(联软数据)
|
||||
│ ├── 在线状态 🟢/🔴 + IP地址
|
||||
│ ├── 计算机名 + 员工账号
|
||||
│ ├── 操作系统版本
|
||||
│ ├── 硬件概要(CPU/内存/磁盘使用率)
|
||||
│ ├── 设备资产号
|
||||
│ └── 安全助手版本
|
||||
├── 安全状态卡片(火绒数据)
|
||||
│ ├── 安全评分
|
||||
│ ├── 🔴 高危漏洞 (N个)
|
||||
│ ├── 🟡 未处理病毒事件 (N个)
|
||||
│ └── 🟢 安全状态正常
|
||||
├── 软件列表(联软数据)
|
||||
│ └── 已安装软件 + 版本 + 安装日期
|
||||
└── 快速操作
|
||||
├── 📡 远程唤醒 (联软)
|
||||
├── 📢 推送消息 (联软/火绒)
|
||||
├── 🛡️ 快速扫描 (火绒)
|
||||
├── 🔒 强制下线 (联软) / 隔离终端 (火绒)
|
||||
└── 🔓 解除隔离 (火绒)
|
||||
```
|
||||
|
||||
### 4.6 开发风险与应对
|
||||
|
||||
| 风险 | 影响 | 概率 | 应对措施 |
|
||||
|------|------|------|---------|
|
||||
| 联软API账户/密码未申请 | 无法调用任何接口 | 中 | 提前联系信息安全/终端安全团队 |
|
||||
| 内网地址不通 | 开发环境无法调试 | 中 | 需VPN或开发机部署在内网 |
|
||||
| Token过期处理 | 长时间运行后API调用失败 | 中 | 实现自动刷新token,提前5分钟续期 |
|
||||
| IP白名单未配置 | 返回INVALID | 中 | 确认部署服务器IP加入白名单 |
|
||||
| API字段名不一致 | 部分接口返回字段与文档不符 | 低 | 先用Postman验证,编写适配层 |
|
||||
| 联软版本差异 | API端点可能不存在 | 低 | 确认当前版本是否为202210SP |
|
||||
| 查询数据量过大 | 部分接口有数据量限制(仿冒设备默认1万条) | 低 | 分页查询+限制时间范围 |
|
||||
|
||||
---
|
||||
|
||||
## 五、安全维度分析
|
||||
|
||||
### 5.1 认证安全
|
||||
|
||||
| 风险项 | 等级 | 说明 | 建议 |
|
||||
|--------|------|------|------|
|
||||
| API账户密码泄露 | **严重** | 泄露后可调用所有联软API,包括强制下线 | 密码存环境变量,**禁止**写入代码 |
|
||||
| Token泄露 | 高 | Token有效期内可被冒用 | 使用HTTPS(如有);Token存储在内存不落盘 |
|
||||
| IP白名单过宽 | 中 | 白名单IP范围过大增加攻击面 | 仅添加必要的服务器IP |
|
||||
|
||||
**认证方式建议**:
|
||||
|
||||
| 场景 | 推荐认证方式 | 理由 |
|
||||
|------|------------|------|
|
||||
| 内网服务器间调用 | IP白名单 + 一次性Token | 安全性最高 |
|
||||
| 开发调试 | IP白名单 + 用户名密码 | 方便调试 |
|
||||
| 生产环境 | **三种全部启用** | 纵深防御 |
|
||||
|
||||
### 5.2 操作安全
|
||||
|
||||
| 操作 | 风险等级 | 安全要求 |
|
||||
|------|---------|---------|
|
||||
| 查询终端设备 (`queryDevByParams`) | 🟢 低 | 无特殊要求 |
|
||||
| 查询终端详情 (`getDevAllInfo`) | 🟢 低 | 无特殊要求 |
|
||||
| 查询在线状态 (`existOnlineUser`) | 🟢 低 | 无特殊要求 |
|
||||
| 推送助手消息 (`noticeAgentMsg`) | 🟡 中 | 记录审计日志;限制频率(同终端5分钟1条) |
|
||||
| 远程唤醒 (`remoteWakeUp`) | 🟡 中 | 记录审计日志;仅坐席可操作 |
|
||||
| **强制下线** (`forcedOffline`) | 🔴 **高** | **必须**二次确认 + 审计日志 + 仅admin角色 |
|
||||
| Syslog推送 | 🟢 低 | 只读,无风险 |
|
||||
|
||||
**强制下线 vs 火绒隔离的区别**:
|
||||
|
||||
| 维度 | 联软强制下线 | 火绒网络隔离 |
|
||||
|------|-----------|-----------|
|
||||
| 机制 | 准入控制断网(802.1X) | 终端agent执行隔离 |
|
||||
| 彻底性 | ⭐⭐⭐⭐⭐ 非常彻底(交换机层面断网) | ⭐⭐⭐⭐ 较彻底(终端层面断网) |
|
||||
| 恢复 | 需重新认证入网 | 调用API即可解除 |
|
||||
| 影响范围 | 该终端所有网络 | 可配置例外(如仅隔离外网) |
|
||||
| 推荐场景 | 确认中毒/仿冒,紧急切断 | 可疑行为,需隔离观察 |
|
||||
|
||||
### 5.3 数据安全
|
||||
|
||||
| 风险项 | 说明 | 建议 |
|
||||
|--------|------|------|
|
||||
| 员工账号信息 | 联软返回员工账号/姓名/邮箱/电话 | H5用户端不展示;坐席端仅展示必要信息 |
|
||||
| 终端敏感信息 | MAC/IP/序列号等 | 同火绒策略:坐席端可见,用户端不可见 |
|
||||
| 硬件详情 | 包含主板序列号等资产信息 | 后端过滤后再传前端,不暴露内部序列号 |
|
||||
| 组织架构 | 全量部门+用户数据 | 仅同步必要字段,不存储完整组织架构 |
|
||||
|
||||
---
|
||||
|
||||
## 六、aTrust集成方案
|
||||
|
||||
> ✅ aTrust OpenAPI V3文档已获取并完成分析(2026-06-11),详见 `docs/aTrust零信任系统集成分析.md`
|
||||
|
||||
### 6.1 系统信息
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 产品 | 深信服aTrust零信任访问控制系统 |
|
||||
| API版本 | OpenAPI V3(适用于≥2.4.10版本) |
|
||||
| 端点数 | **104个**(10大类) |
|
||||
| 认证方式 | HMAC-SHA256签名(4个必填Header: x-ca-sign/key/timestamp/nonce) |
|
||||
| 默认端口 | 4433(HTTPS) |
|
||||
| IP白名单 | 支持 |
|
||||
|
||||
### 6.2 核心P0接口
|
||||
|
||||
| 接口 | 路径 | 方法 | 核心价值 |
|
||||
|------|------|------|---------|
|
||||
| **查询在线用户** | /api/v1/monitor/getUserStatus | GET | VPN在线状态+remoteIp+vips(虚拟IP)+os+browser |
|
||||
| **查询全量终端** | /api/v1/device/queryAll | POST | 按绑定用户查询终端(bindUserList过滤) |
|
||||
| **查询单个终端** | /api/v1/device/query | GET | 终端详情+bindUsers(绑定用户列表)+macList |
|
||||
|
||||
### 6.3 核心P1接口
|
||||
|
||||
| 接口 | 路径 | 方法 | 安全等级 |
|
||||
|------|------|------|---------|
|
||||
| **踢出在线用户** | /api/v1/monitor/kickoutUsers | POST | 🔴 高危(需二次确认+审计) |
|
||||
| **终端绑定用户** | /api/v1/device/assignUser | POST | 🟡 中 |
|
||||
| **查询用户详情** | /api/v3/user/queryByName | GET | 🟢 低 |
|
||||
|
||||
### 6.4 aTrust映射字段
|
||||
|
||||
| 映射路径 | 字段 | 说明 |
|
||||
|---------|------|------|
|
||||
| **在线用户→员工** | `name`(用户名) | 如果与公司域账号一致,直接映射employee_id |
|
||||
| **在线用户→虚拟IP** | `vips[].ip` | VPN分配的内网IP,可用于火绒交叉匹配 |
|
||||
| **终端→绑定用户** | `bindUsers[].bindUser` | 终端绑定的用户名 |
|
||||
| **用户→外部ID** | `externalId` | **可设置为工号,实现直接映射** |
|
||||
| **终端→MAC** | `macList` | MAC地址列表,与联软交叉匹配 |
|
||||
|
||||
### 6.5 与联软的互补关系
|
||||
|
||||
| 能力 | 联软(内网主源) | aTrust(VPN源) | 互补效果 |
|
||||
|------|---------------|---------------|---------|
|
||||
| 内网终端映射 | ⭐⭐⭐⭐⭐ strusername | ⭐⭐⭐ 部分覆盖 | 联软主导 |
|
||||
| 远程/VPN终端 | ⚠️ 可能未覆盖 | ⭐⭐⭐⭐⭐ 核心覆盖 | aTrust补全 |
|
||||
| VPN会话数据 | ❌ 无 | ✅ 唯一数据源 | 不可替代 |
|
||||
| 踢出能力 | forcedOffline(准入下线) | kickoutUsers(VPN踢出) | 双通道 |
|
||||
| 终端授信 | ❌ 无 | ✅ trusted字段 | aTrust独有 |
|
||||
|
||||
### 6.6 集成优先级
|
||||
|
||||
aTrust集成为**P1优先级**(联软P0之后),因为:
|
||||
1. VPN连接问题是IT服务台高频场景
|
||||
2. aTrust的`vips`虚拟IP可用于火绒交叉匹配,补全远程终端安全画像
|
||||
3. aTrust API文档已获取,可直接开发
|
||||
4. 104个端点中仅需3-5个P0接口,开发量可控
|
||||
|
||||
---
|
||||
|
||||
## 七、三系统集成总览
|
||||
|
||||
### 7.1 系统定位
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ 智能IT支持服务台 │
|
||||
│ (统一集成层) │
|
||||
│ │
|
||||
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ 联软 │ │ 火绒 │ │ aTrust │ │
|
||||
│ │ 终端管理 │ │ 终端安全 │ │ 远程接入 │ │
|
||||
│ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │
|
||||
│ │ │ │ │
|
||||
│ 员工↔终端映射 安全态势+隔离 VPN状态+远程IP │
|
||||
│ 硬件+软件详情 病毒+漏洞+扫描 VPN连接审计 │
|
||||
│ 准入+远程唤醒 网络隔离/解除 认证状态 │
|
||||
│ 补丁+审计日志 软件合规统计 虚拟IP分配 │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 7.2 集成优先级与排程
|
||||
|
||||
| 阶段 | 系统 | 功能 | 预计周期 | 前置条件 |
|
||||
|------|------|------|---------|---------|
|
||||
| **P0** | 联软 | 员工→终端映射 + 终端详情查询 | ~2周 | 联软API账户+白名单 |
|
||||
| **P0** | 火绒 | 终端安全画像 + 漏洞/病毒查询 | ~2周 | 火绒AccessKey |
|
||||
| **P1** | 联软 | 远程唤醒 + 消息推送 + 准入查询 | ~1周 | P0已完成 |
|
||||
| **P1** | 火绒 | 远程扫描 + 隔离/解除 | ~1周 | P0已完成 |
|
||||
| **P1** | aTrust | VPN状态 + 远程终端映射 | ~2周 | aTrust API文档 |
|
||||
| **P2** | 联软+火绒 | 管理后台安全态势看板 | ~1周 | P0+P1已完成 |
|
||||
| **P2** | 联软 | Syslog审计日志对接 | ~1周 | 联软Syslog配置 |
|
||||
|
||||
### 7.3 统一数据模型
|
||||
|
||||
```python
|
||||
class UnifiedTerminalInfo:
|
||||
"""统一终端信息模型(聚合联软+火绒+aTrust)"""
|
||||
|
||||
# 基础标识(联软提供)
|
||||
computer_name: str # 计算机名
|
||||
ip: str # IP地址
|
||||
mac: str # MAC地址
|
||||
employee_id: str # 使用人账号
|
||||
employee_name: str # 使用人姓名
|
||||
department: str # 所属部门
|
||||
|
||||
# 在线状态(联软+aTrust)
|
||||
is_online: bool # 是否在线
|
||||
online_source: str # "leagsoft" | "atrust" | "offline"
|
||||
last_online_time: str # 最后在线时间
|
||||
|
||||
# 硬件信息(联软 getDevAllInfo)
|
||||
os_version: str # 操作系统版本
|
||||
cpu: str # CPU型号
|
||||
memory_gb: int # 内存(GB)
|
||||
disk_total_gb: float # 磁盘总量(GB)
|
||||
disk_usage_pct: float # 磁盘使用率(%)
|
||||
|
||||
# 安全信息(火绒提供)
|
||||
security_score: int | None # 安全评分
|
||||
high_risk_leaks: int | None # 高危漏洞数
|
||||
uncleaned_virus: int | None # 未处理病毒数
|
||||
last_scan_time: str | None # 最近扫描时间
|
||||
|
||||
# 准入信息(联软提供)
|
||||
agent_version: str | None # 安全助手版本
|
||||
patch_install_rate: float | None # 补丁安装率
|
||||
|
||||
# VPN信息(aTrust提供)
|
||||
vpn_online: bool | None # VPN是否在线
|
||||
vpn_virtual_ip: str | None # VPN虚拟IP
|
||||
vpn_last_connect: str | None # 最近VPN连接时间
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、对接前准备清单
|
||||
|
||||
### 联软
|
||||
|
||||
#### 必须完成(阻塞性)
|
||||
|
||||
- [ ] **申请API账户**:联系终端安全团队,获取`ApiAccount` + `ApiPassword`
|
||||
- [ ] **配置IP白名单**:将IT服务台服务器IP加入联软白名单
|
||||
- [ ] **确认网络可达**:确认开发/部署服务器可访问联软系统(端口30098)
|
||||
- [ ] **确认联软版本**:确认当前版本是否为202210SP,API文档是否匹配
|
||||
|
||||
#### 建议完成(非阻塞)
|
||||
|
||||
- [ ] **确认员工账号映射**:验证联软中`strusername`字段是否为公司企微/域账号
|
||||
- [ ] **确认数据量级**:了解联软管理的终端数量,评估查询性能
|
||||
- [ ] **确认安全助手安装率**:了解公司终端安全助手安装覆盖率
|
||||
- [ ] **准备测试终端**:准备1-2台测试终端用于开发调试
|
||||
|
||||
### aTrust
|
||||
|
||||
- [ ] **获取API文档**:联系网络/信息安全团队获取aTrust API文档
|
||||
- [ ] **确认认证方式**:了解aTrust API认证机制
|
||||
- [ ] **确认映射数据格式**:了解aTrust中员工↔终端的映射字段
|
||||
|
||||
---
|
||||
|
||||
## 九、总结
|
||||
|
||||
### 9.1 核心结论
|
||||
|
||||
1. **联软是终端映射的金钥匙**:`strusername`字段直接打通员工→终端的映射,这是火绒和eHR都无法提供的关键能力
|
||||
2. **联软+火绒高度互补**:联软管「终端画像+准入」,火绒管「安全态势+隔离」,无替代关系
|
||||
3. **aTrust补全远程办公**:联软覆盖内网终端,aTrust覆盖VPN终端,两者结合实现100%覆盖
|
||||
4. **三系统联合映射是最佳方案**:联软(主)+aTrust(VPN)+eHR(辅助),取代之前推荐的IP交叉匹配方案
|
||||
5. **实现成本低**:联软API为标准HTTP+JSON,认证简单,无需安装agent
|
||||
|
||||
### 9.2 映射策略升级总结
|
||||
|
||||
| 维度 | 旧方案(仅火绒) | 新方案(三系统) |
|
||||
|------|----------------|----------------|
|
||||
| 映射准确度 | ⭐⭐⭐(IP交叉匹配) | ⭐⭐⭐⭐⭐(联软直接映射) |
|
||||
| 覆盖范围 | 仅内网在线终端 | 内网+VPN全覆盖 |
|
||||
| 实现复杂度 | 需eHR+火绒双接口 | 仅联软单接口 |
|
||||
| 维护成本 | 高(IP变化需定期校验) | 低(联软实时更新) |
|
||||
| 前置依赖 | eHR接口+火绒接口 | 联软接口 |
|
||||
|
||||
### 9.3 一句话总结
|
||||
|
||||
> 联软是智能IT支持服务台打通「员工↔终端」映射的关键系统,与火绒形成「管理+安全」双引擎,加上aTrust补全远程办公,三系统集成将实现终端问题排查的360°全景视角。
|
||||
Reference in New Issue
Block a user