145 lines
5.0 KiB
Markdown
145 lines
5.0 KiB
Markdown
|
|
# Nexus 设计标准
|
|||
|
|
|
|||
|
|
> 最后更新: 2026-05-22
|
|||
|
|
> 适用范围: 所有功能迭代、代码审查、安全审计
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 安全模型:信任管理员 + 防注入/XSS
|
|||
|
|
|
|||
|
|
**核心理念: 认证管理员是可信用户,但必须防御注入攻击和 XSS。**
|
|||
|
|
|
|||
|
|
运维管理后台的认证管理员即为可信用户,应用层不对操作内容做业务限制。
|
|||
|
|
但必须防御技术层面的攻击向量(命令注入、路径注入、XSS)。
|
|||
|
|
|
|||
|
|
**依据**:
|
|||
|
|
- 使用者均为经过认证的运维管理员
|
|||
|
|
- 绕过应用层限制的门槛极低(SSH 直连即可)
|
|||
|
|
- 应用层业务限制增加维护成本,假安全感大于实际安全收益
|
|||
|
|
- 但技术层面注入攻击是真实风险,必须防御
|
|||
|
|
|
|||
|
|
**实施要求**:
|
|||
|
|
- 所有 API 端点必须校验 JWT 令牌(`Depends(get_current_admin)`)
|
|||
|
|
- WebSocket 连接必须通过 JWT 查询参数认证(ADR-011)
|
|||
|
|
- Agent 通信使用 X-API-Key 认证
|
|||
|
|
- Shell 命令参数必须 `shlex.quote()` 防注入
|
|||
|
|
- 前端禁止在模板字面量中直接输出用户内容(防 XSS)
|
|||
|
|
- 数据库凭据加密存储(Fernet + AES-CBC)
|
|||
|
|
|
|||
|
|
**不做的事**:
|
|||
|
|
- ❌ 不在应用层限制上传文件类型
|
|||
|
|
- ❌ 不在应用层限制文件写入目标路径
|
|||
|
|
- ❌ 不在应用层限制执行命令的业务内容
|
|||
|
|
- ❌ 不做 RBAC 权限体系(保持"鉴权即信任")
|
|||
|
|
- ❌ 不做会话录像(asciicast)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 配置存储
|
|||
|
|
|
|||
|
|
| 层 | 存储 | 用途 |
|
|||
|
|
|----|------|------|
|
|||
|
|
| 热层 | Redis | 心跳、缓存、告警去重、Pub/Sub (TTL 管理) |
|
|||
|
|
| 持久层 | MySQL | 服务器、日志、设置、审计 |
|
|||
|
|
| 文件 | `.env` | 启动必需: DB连接、SECRET_KEY、API_KEY |
|
|||
|
|
|
|||
|
|
**配置加载优先级**: `.env` (启动) → MySQL settings 表 (动态覆盖)
|
|||
|
|
|
|||
|
|
**原则**: 可热配项走 MySQL settings(`load_settings_from_db` 启动时加载),不可热配项走 `.env`。
|
|||
|
|
|
|||
|
|
**不可改配置** (`.env` only):
|
|||
|
|
- SECRET_KEY, API_KEY, ENCRYPTION_KEY, DATABASE_URL
|
|||
|
|
|
|||
|
|
**可改配置** (MySQL settings 可覆盖):
|
|||
|
|
- system_name/title, pool_size/overflow, redis_url, 告警阈值, Telegram 配置
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 性能基线
|
|||
|
|
|
|||
|
|
| 指标 | 值 |
|
|||
|
|
|------|-----|
|
|||
|
|
| API 安全并发 | ≤50 |
|
|||
|
|
| P50 延迟 | <60ms |
|
|||
|
|
| P95 延迟 | <300ms |
|
|||
|
|
| 最大文件上传 | 500MB |
|
|||
|
|
| sync_logs 保留 | 30 天 |
|
|||
|
|
| 心跳批量刷到 MySQL | 每 10min |
|
|||
|
|
| SSH 连接池 | 最大 100 连接,空闲 5min 关闭 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 代码规范
|
|||
|
|
|
|||
|
|
### Python 后端
|
|||
|
|
|
|||
|
|
- 所有 API 路由使用 Pydantic 模型验证请求体(`server/api/schemas.py`)
|
|||
|
|
- 所有 API 路由使用 JWT 认证(`Depends(get_current_admin)`)
|
|||
|
|
- Shell 命令参数必须 `shlex.quote()` 防注入
|
|||
|
|
- DB session 由 DbSessionMiddleware 自动管理,通过 `request.state.db` 共享
|
|||
|
|
- Service 层通过依赖注入获取 Repository 实例(`server/api/dependencies.py`)
|
|||
|
|
- 加密使用 Fernet (Python) + AES-CBC (PHP 兼容),密钥统一从 API_KEY/SECRET_KEY 派生
|
|||
|
|
- Redis 操作使用 `redis.asyncio`,连接池为 `BlockingConnectionPool`
|
|||
|
|
|
|||
|
|
### 前端 (Tailwind + Alpine.js)
|
|||
|
|
|
|||
|
|
- 所有页面引入 `api.js` 共享模块(JWT 自动刷新、apiFetch 封装)
|
|||
|
|
- JWT 令牌存储在 localStorage,自动刷新(2分钟内到期触发)
|
|||
|
|
- 所有 API 调用使用 `apiFetch()` 自动附带 Authorization 头
|
|||
|
|
- CORS 由 `NEXUS_CORS_ORIGINS` 环境变量配置
|
|||
|
|
- 侧边栏和导航在所有页面保持一致
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 测试标准
|
|||
|
|
|
|||
|
|
**收尾前必修**: 部署完成后必须用浏览器直接访问页面验证核心流程。curl / API 测试不能替代浏览器验收。
|
|||
|
|
|
|||
|
|
**检查清单**:
|
|||
|
|
- [ ] 登录 → 仪表盘正常加载(含 WebSocket 实时更新)
|
|||
|
|
- [ ] 核心页面可交互(点击、表单提交有响应)
|
|||
|
|
- [ ] 浏览器 Console 无报错
|
|||
|
|
- [ ] 浏览器 Network 面板无 404/500
|
|||
|
|
- [ ] JWT 令牌自动刷新正常
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 架构模式
|
|||
|
|
|
|||
|
|
### 后台任务
|
|||
|
|
|
|||
|
|
| 任务 | 间隔 | 文件 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| heartbeat_flush | 10min | `server/background/heartbeat_flush.py` |
|
|||
|
|
| self_monitor | 30s | `server/background/self_monitor.py` |
|
|||
|
|
| schedule_runner | 60s | `server/background/schedule_runner.py` |
|
|||
|
|
| retry_runner | 5min | `server/background/retry_runner.py` |
|
|||
|
|
| asyncssh_cleanup | 60s | `server/infrastructure/ssh/asyncssh_pool.py` |
|
|||
|
|
|
|||
|
|
所有后台任务使用 `asyncio.create_task()` 在 `lifespan` 中启动,支持优雅关闭。
|
|||
|
|
|
|||
|
|
### SSH 连接池
|
|||
|
|
|
|||
|
|
引用计数模式 (ADR-004):
|
|||
|
|
- `ssh_pool.acquire(server)` → 获取/创建连接,ref_count +1
|
|||
|
|
- `ssh_pool.release(server_id)` → ref_count -1,空闲连接保留复用
|
|||
|
|
- 清理循环每 60s 扫描,空闲 >5min 的连接自动关闭
|
|||
|
|
|
|||
|
|
### WebSocket 两层架构 (ADR-010)
|
|||
|
|
|
|||
|
|
- Layer 1: 内存 `ConnectionManager` 管理本 worker 的 WebSocket 连接
|
|||
|
|
- Layer 2: Redis Pub/Sub `nexus:alerts` 频道中继跨 worker 消息
|
|||
|
|
- Ping/pong 心跳 30s,僵尸连接 60s 清理
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. 不做清单(永久)
|
|||
|
|
|
|||
|
|
- ❌ RBAC 权限体系 (保持"鉴权即信任")
|
|||
|
|
- ❌ 会话录像 asciicast (不需要)
|
|||
|
|
- ❌ 移动端适配 (运维不会用手机管理服务器)
|
|||
|
|
- ❌ MQTT 协议 (HTTP+Redis 足够 2000 台)
|
|||
|
|
- ❌ 文件差异比较
|
|||
|
|
- ❌ 图片/PDF 预览
|
|||
|
|
- ❌ 推送结果邮件报告
|