Files
cao.wangrenbo f70b344c85 chore: initial commit — 项目计划 v0.3
服务器用户管理节点(ws_usernode):
- docs/PLAN.md:需求交互确认后的完整项目计划(v0.3)
  - 单机节点 + RESTful API v1,后期可扩展多节点 agent
  - Go + Gin / Vue3 + Element Plus,GORM 双驱动(MySQL/SQLite)
  - 外部用户 OTP 双通道登录(邮件 + CLI)、sudoers 白名单系统操作
  - 账号生命周期、SSH 公钥管理、申请审批、审计归档
- .gitignore:Go / 前端 / 运行时产物忽略规则
2026-08-29 21:28:52 +08:00

326 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目计划:服务器用户管理节点(ws_usernode
> 版本:v0.3(需求已交互确认,含构建/配置细化)
> 日期:2026-08-29
> 状态:待评审
---
## 1. 项目概述
构建一个基于 Go 的**服务器用户管理节点**,提供 Web 前后端,用于对服务器上的**外部用户**(非本机管理员)进行集中化管理,覆盖:
- 系统账号的创建、禁用、删除、过期与回收(统一 `ext_` 前缀、`external` 组)
- 外部用户自助上传与管理 SSH 公钥,实时同步 `authorized_keys`
- 用户自助申请新账号 + 管理员审批流
- SMTP 邮件通知(OTP 登录验证码、审批结果、到期提醒、密码重置)
- 管理操作审计(保留 30 天 + 手动/每日定时归档)
**部署形态**:单机节点,一个 Go 二进制(前端产物 go:embed 嵌入),参考 Gitea 单二进制风格。所有能力通过 **RESTful API v1** 暴露,系统账号操作层接口化,为后期扩展"控制面 + 多节点 agent"预留空间。
---
## 2. 已确认决策汇总
### 2.1 架构与部署
| 决策点 | 结论 |
|---|---|
| 部署形态 | 单机节点;RESTful API v1;后期可扩展多节点 agent |
| 后端 | Go ≥ 1.22 + Gin |
| 前端 | Vue 3 + Vite + TypeScript + Element Plus + Pinia + Vue Router + Axios |
| 前端部署 | **go:embed 嵌入 Go 二进制**,单文件分发;开发期走 Vite dev server |
| 数据库 | GORM 抽象,**MySQL 生产 / SQLite 开发**,可平滑迁移 |
| 进程管理 | systemd 服务 |
| 系统账号操作权限 | 节点以**专有用户运行**(如 `usernode`,加入 sudo 组),通过 **sudoers 白名单**执行 `useradd/usermod/userdel/passwd` 等,便于审计程序行为 |
| 日志 | log/slog 结构化日志 |
| 构建环境 | **podman 容器**构建(多阶段);前端可单独拉取**前端 dev 镜像**热开发 |
| 配置格式 | **TOML**`config.example.toml`,支持环境变量覆盖) |
### 2.2 账号与认证
| 决策点 | 结论 |
|---|---|
| 外部用户系统账号 | 统一加入 `external` 组;用户名强制 `ext_` 前缀(如 `ext_zhangsan`);系统账号口令锁定(`passwd -l`),仅密钥登录 |
| 外部用户登录 | **图形验证码 + 6 位 OTP**OTP 支持**邮件**与 **CLI 子命令**双通道获取(同一验证码、同一 10 分钟有效期、同一 60s 冷却与失败限速,两通道对齐);会话 **cookie**HttpOnly/Secure/SameSite |
| 管理员登录 | 用户名 + 密码(bcrypt);密码可通过 **CLI 子命令**或**邮件重置**修改;不做 TOTP |
| 用户邮箱 | 仅管理员可修改(OTP 接收方);用户不可自助改 |
| 默认有效期 | 批准激活起默认 **90 天**(全局可配置),管理员创建/延期时可覆盖 |
### 2.3 功能范围
| 决策点 | 结论 |
|---|---|
| 管理范围 | 账号生命周期 + SSH 密钥(仅用户上传,管理员不代签) |
| 新账号申请 | 表单:**用户名、邮箱、挂靠老师、用途**;批准后**自动 useradd** 并邮件通知 |
| 审批范围 | **仅新账号申请**走审批流;延期由管理员操作 |
| 密钥来源 | 仅用户自行上传公钥;无管理员代签、无密钥对下发路径 |
| 过期回收 | 到期自动禁用(锁系统账号)+ 邮件通知;可配置**回收期**(默认 30 天),期内可延期恢复,超期自动删除系统账号与密钥(**保留审计**) |
| 用户自助范围 | 管理自己的公钥(上传/重命名/吊销)、查看账号信息与状态、查看自己的审计痕迹 |
| 审计 | 管理操作审计,append-only;保留 **30 天**自动清理;**手动 CSV 导出 + 每日定时归档**(文件存磁盘) |
| 批量导入 | 本期不做,接口预留(二期) |
---
## 3. 需求范围
### 3.1 功能需求(FR
**F1 认证与会话**
- 管理员:用户名+密码登录;bcrypt 存储;登录限速防暴力;忘记密码走 SMTP 邮件重置;CLI 子命令重置
- 外部用户:图形验证码(防机器人)→ 6 位 OTP → 验证登录;OTP 经**邮件**或 **CLI 子命令**获取(同一验证码、同一有效期与限速,两通道对齐);发送冷却 60s、单账号失败限速
- 会话:cookieHttpOnly/Secure/SameSite),默认 24h,可配置;登出即失效
- 外部用户无 Web 口令(系统账号口令锁定,纯密钥登录)
**F2 用户生命周期管理**
- 管理员创建外部用户:自动生成 `ext_<name>` 系统账号(`external` 组、锁定口令、默认 shell
- 启用 / 禁用 / 删除 / 延期;状态与系统账号一致性校验
- 到期自动禁用(锁账号)+ 邮件提醒;进入回收期(默认 30 天,可配置);回收期内可延期恢复;超期自动删除系统账号与密钥(保留审计)
- 用户模型记录:邮箱、挂靠老师、用途、过期时间、创建者、最近登录
**F3 SSH 密钥管理**
- 用户上传自己的公钥(格式校验:类型/长度/重复)
- 密钥列表、重命名、吊销、删除
- 有效密钥实时同步到 `~/.ssh/authorized_keys`(原子写 + 并发锁),吊销立即失效
**F4 申请与审批**
- 新账号申请单:用户名、邮箱、挂靠老师、用途
- 管理员审批(通过 / 拒绝 + 理由),结果邮件通知;通过后自动创建账号
- 拒绝后可重新提交
**F5 邮件通知(SMTP**
- OTP 登录验证码、审批结果、账号创建通知、到期提醒、回收提醒、管理员密码重置
- 发送队列 + 重试 + 失败记录(mail_logs);OTP 为双通道(邮件 + CLI),邮件失败不阻断登录
**F6 审计**
- 记录管理操作:操作者、动作、对象、详情(JSON)、IP、结果、时间;append-only
- 保留 30 天自动清理;手动 CSV 导出接口;每日定时归档到磁盘目录(归档后可安全清理)
**F7 系统设置**
- SMTP、默认有效期、回收期、会话时长、审计保留天数等(config + settings 表)
### 3.2 非功能需求(NFR
- **安全**:私钥不经系统(无代签);系统命令 sudoers 白名单、最小权限;OTP/图形验证码/限速;cookie 安全属性;审计不可篡改
- **兼容性**SQLite ↔ MySQL 迁移无忧(GORM 通用类型 + 迁移脚本)
- **部署**:单二进制 + go:embed 前端 + systemd + sudoers 配置;提供 Docker 可选
- **可观测**:结构化日志、审计表、状态接口
- **可扩展**:system 层接口化,预留多节点 agent;API 无状态化(cookie 会话 + DB session 存储)
---
## 4. 技术选型
| 层 | 选型 | 理由 |
|---|---|---|
| 语言 | Go ≥ 1.22 | 单二进制、并发好、部署简单 |
| Web 框架 | Gin | 生态成熟、中间件丰富 |
| ORM | GORM v2sqlite + mysql 驱动) | 一套模型双库支持 |
| 认证 | 服务端会话(DB 存储)+ cookiebcryptOTPcrypto/rand 生成) | 符合"cookie 会话"确认 |
| 图形验证码 | 内置生成(图像,随机字符),不依赖第三方服务 | 防机器人,自包含 |
| 定时任务 | robfig/cron/v3 | 过期扫描、回收、审计归档 |
| 邮件 | gomailnet/smtp 封装) | TLS 支持,队列+重试 |
| SSH 公钥 | crypto/ssh 解析校验 | 标准库,免依赖 |
| 前端 | Vue 3 + Vite + TS + Element Plus + Pinia + Router + Axios | 后台管理生态成熟 |
| 日志 | log/slog | 标准库结构化日志 |
| 配置 | TOMLgithub.com/BurntSushi/toml | 配置文件 + 环境变量覆盖 |
| 构建 | podman 多阶段容器构建 | Go 镜像 + 前端 dev 镜像 |
---
## 5. 总体架构
### 5.1 分层
```
┌───────────────────────────────────────────────┐
│ Web 前端 (Vue3 SPA, go:embed 进二进制) │
└───────────────┬───────────────────────────────┘
│ HTTPS / RESTful API v1
┌───────────────▼───────────────────────────────┐
│ HTTP 层 (Gin) 路由 / 中间件 / 参数校验 / 鉴权 │
├───────────────────────────────────────────────┤
│ 服务层 (service) │
│ auth │ user │ key │ approval │ mail │ audit │
├───────────────────────────────────────────────┤
│ 系统账号抽象层 (system) 接口: CreateUser / │
│ RemoveUser / SetLock / SyncAuthorizedKeys │
│ └── 本地实现: sudoers 白名单 + sudo -n 执行 │
│ useradd/usermod/userdel/passwd、原子写 │
│ authorized_keys ──(未来)──> 远程 agent │
├───────────────────────────────────────────────┤
│ 数据层 (GORM) users/ssh_keys/approvals/ │
│ audit_logs/sessions/settings │
│ ├─ MySQL(生产) │
│ └─ SQLite(开发/单机) │
└───────────────────────────────────────────────┘
```
### 5.2 目录结构(规划)
```
ws_usernode/
├── cmd/usernode/main.go # 入口 + CLI 子命令
│ ├── serve # 启动服务
│ ├── migrate # 数据库迁移
│ ├── admin create # 创建初始管理员
│ ├── admin reset-password # 重置管理员密码(CLI)
│ └── user otp # 外部用户 OTP 获取(与邮件对齐)
├── internal/
│ ├── config/ # 配置加载(yaml + 环境变量)
│ ├── server/ # HTTP server、优雅启停
│ ├── router/ # 路由注册
│ ├── api/ # handler 层(v1
│ ├── model/ # GORM 模型
│ ├── service/ # 业务逻辑
│ ├── system/ # 系统账号抽象层(sudoers 白名单实现)
│ ├── auth/ # 会话、bcrypt、OTP、图形验证码、限速
│ ├── cron/ # 过期扫描、回收、审计归档
│ └── pkg/ # 工具(随机、时间、校验)
├── web/ # Vue3 前端
│ ├── src/
│ └── Containerfile.dev # 前端 dev 镜像(podman 热开发)
├── docs/ # 本计划、API、数据库、部署
├── deploy/ # systemd 单元、sudoers 配置、迁移脚本、Containerfile
├── config.example.toml
├── go.mod
└── Makefile # build / test / dev 工作流
```
---
## 6. 数据库设计(核心表)
> GORM 定义;避免平台特有类型;时间统一 UTC;SQLite/MySQL 双兼容。
| 表 | 关键字段 | 说明 |
|---|---|---|
| `admin_users` | username, password_hash, email, role, status | 管理端账号(唯一) |
| `users` | username(`ext_` 强制前缀), email, supervisor, purpose, status(active/disabled/expired), expire_at, shell, created_by, last_login_at, recycled_at | 外部用户(1:1 系统账号) |
| `ssh_keys` | user_id, name, key_type, public_key, fingerprint, status(active/revoked), source(默认 user_uploaded), created_by, revoked_at | 仅用户上传公钥 |
| `approvals` | username_requested, email, supervisor, purpose, status(pending/approved/rejected), reviewer_id, reviewed_at, reason | 仅新账号申请 |
| `audit_logs` | actor_id, actor_name, action, resource_type, resource_id, detail(JSON), ip, result, created_at | append-only |
| `sessions` | session_id, user_type(admin/user), ref_id, expire_at, ip, user_agent | cookie 会话 |
| `settings` | key, value | SMTP、策略等 |
| `mail_logs` | to, subject, status, error, retry_count | 邮件队列/重试/失败记录 |
OTP 验证码与图形验证码:单实例可用内存缓存 + 限速计数(DB 或内存);文档注明多实例时需改为 DB/Redis。
---
## 7. API 设计(RESTful v1
统一前缀 `/api/v1`;会话基于 cookie(非 Bearer header)。
| 模块 | 方法 & 路径 | 说明 | 权限 |
|---|---|---|---|
| 认证 | `GET /auth/captcha` | 图形验证码 | - |
| 认证 | `POST /auth/otp/send` | 外部用户:用户名 + 图形验证码 → 发 OTP 邮件(60s 冷却) | - |
| 认证 | `POST /auth/otp/login` | 用户名 + 6 位 OTP → 建立 cookie 会话 | - |
| 认证 | `POST /auth/admin/login` | 管理员用户名+密码登录 | - |
| 认证 | `POST /auth/logout` / `GET /auth/me` | 登出 / 当前会话 | user/admin |
| 认证 | `POST /auth/admin/forgot` / `POST /auth/admin/reset` | 管理员邮件重置密码 | - |
| 用户 | `GET /users` | 列表(筛选/分页:状态、到期、挂靠老师) | admin |
| 用户 | `POST /users` | 管理员创建(自动 useradd | admin |
| 用户 | `GET /users/:id` / `PATCH /users/:id` | 详情 / 更新(含改邮箱) | admin |
| 用户 | `POST /users/:id/disable` / `enable` | 禁用 / 启用 | admin |
| 用户 | `POST /users/:id/extend` | 延长过期时间 | admin |
| 用户 | `DELETE /users/:id` | 删除并回收(系统账号+密钥,保留审计) | admin |
| 密钥 | `GET /me/keys` | 我的密钥列表 | user |
| 密钥 | `POST /me/keys` | 上传公钥 | user |
| 密钥 | `PATCH /me/keys/:id` | 重命名 | user |
| 密钥 | `DELETE /me/keys/:id` | 吊销 / 删除 | user |
| 密钥 | `GET /users/:id/keys` | 用户密钥列表(管理员) | admin |
| 审批 | `POST /approvals` | 提交新账号申请 | -(公开) |
| 审批 | `GET /approvals` | 申请单列表(按状态筛选) | admin |
| 审批 | `POST /approvals/:id/review` | 审批(通过→自动建号 / 拒绝+理由) | admin |
| 审计 | `GET /audit` | 查询(操作者/资源/时间范围/分页) | admin |
| 审计 | `GET /audit/export` | 手动 CSV 导出 | admin |
| 设置 | `GET /settings` / `PUT /settings` | 系统设置读写 | admin |
| 状态 | `GET /healthz` | 健康检查 | - |
---
## 8. 前端规划(Vue3
参考 Gitea 后台管理布局:
1. **登录页**:管理员(密码)与外部用户(图形验证码 + 邮件 OTP)双入口
2. **仪表盘**:用户数、待审批数、近期待过期账号、最近审计
3. **用户管理**(admin):列表筛选/分页、新建、详情、禁用/启用/延期/删除、改邮箱
4. **密钥管理**:我的密钥上传/重命名/吊销
5. **申请审批**(admin):待办列表、通过/拒绝 + 理由
6. **审计日志**admin):查询 + CSV 导出
7. **系统设置**(admin):SMTP、默认有效期、回收期、会话时长、审计保留
8. **个人中心**(user):账号信息(挂靠老师/用途/到期时间)、我的密钥、我的审计痕迹
技术栈:Vite + Vue Router + Pinia + Axios(统一拦截 401/错误码)+ Element Plus;构建产物 go:embed。
---
## 9. 安全设计
| 风险 | 对策 |
|---|---|
| 系统账号操作提权 | 节点以专有用户运行;**sudoers 白名单**仅允许固定命令(useradd/usermod/userdel/passwd/chsh)且禁任意 shell;命令参数程序内强校验;不在生产直接验证 |
| OTP 爆破 / 滥用 | 图形验证码前置、发送冷却 60s、单账号失败限速、OTP 一次性 + 10 分钟过期;CLI 通道受同一限速约束 |
| 会话劫持 | cookie HttpOnly/Secure/SameSite;会话过期;登出吊销;IP 变更可选提示 |
| 口令安全 | 管理员 bcrypt 存储、强度校验、登录限速 |
| authorized_keys 并发写 | 文件锁 + 原子写(tmp + rename);全量重写基于 DB 状态 |
| 审计不可篡改 | audit_logs 仅 INSERT30 天自动清理前先归档;定期备份 |
| 邮件安全 | SMTP TLS、邮件内容不含验证码明文记录、密码不出现在日志 |
| 外部用户口令 | 系统账号 `passwd -l` 锁定,仅密钥登录 |
---
## 10. 里程碑与排期
> 估算总周期约 3~4 周(单人),每阶段含测试。
| 阶段 | 内容 | 交付物 | 估算 |
|---|---|---|---|
| **M0 骨架** | Go 工程、CLI 子命令(含 OTP 获取)、TOML 配置、日志、GORM 双驱动、**podman 多阶段构建**、前端脚手架(Vite **dev 镜像** + go:embed 打通) | 可运行空壳 + 迁移 | 2–3 天 |
| **M1 认证 + 用户管理** | 管理员登录(密码/CLI/邮件重置)、外部用户 OTP 登录(图形验证码 + 邮件/CLI 双通道)、cookie 会话、用户 CRUD、system 层 sudoers 对接 | 可创建/禁用/删除真实系统用户 | 5–6 天 |
| **M2 SSH 密钥** | 公钥上传/管理、authorized_keys 原子同步、吊销即时失效 | 端到端密钥管理 | 3–4 天 |
| **M3 审批 + 邮件** | 新账号申请流(用户名/邮箱/挂靠老师/用途)、审批、SMTP 通知、拒绝重提 | 完整申请-审批闭环 | 3–4 天 |
| **M4 生命周期 + 审计** | 过期扫描 cron、回收期策略、审计(30 天 + 手动导出 + 每日归档) | 自动化回收 + 可查可导出审计 | 3–4 天 |
| **M5 前端完善 + 部署** | Vue3 全部页面、联调、podman 构建镜像、systemd + sudoers 部署、迁移脚本、文档 | 可交付部署版本 | 5–7 天 |
**验收标准(M5 完成时)**
- 管理员创建用户 → 用户图形验证码 + OTP(邮件或 CLI)登录 → 上传公钥 → 立即 SSH 登录服务器
- 吊销密钥立即失效;到期自动禁用并通知;回收期内可延期,超期自动回收(保留审计)
- 所有管理操作可审计、可查询、可 CSV 导出,每日归档
- SQLite 数据可迁移至 MySQL(提供脚本与验证)
- 单二进制部署(systemd + sudoers 白名单)通过;podman 容器构建通过(Go 镜像 + 前端 dev 镜像)
---
## 11. 风险与对策
| 风险 | 影响 | 对策 |
|---|---|---|
| 系统账号操作失误(误删/误改) | 高 | sudoers 白名单、命令参数强校验、回收二次确认、测试环境验证 |
| OTP/邮件链路故障 | 中 | 邮件队列 + 重试 + 失败告警;OTP 双通道(邮件 + CLI),邮件不可用时经 CLI 获取 |
| SQLite ↔ MySQL 迁移差异 | 中 | GORM 通用类型、迁移前校验、迁移脚本 + 演练 |
| 定时任务并发触发 | 中 | 单实例部署说明;预留分布式锁接口 |
| 管理员密码丢失 | 中 | CLI `admin reset-password` 兜底 |
| 多实例扩展(未来 agent) | 低(预留) | system 层接口化;会话/OTP 存储预留 DB 方案 |
---
## 12. 待定项 / 二期展望
- 多节点 agent 模式(system 层远程化)
- CSV 批量导入用户
- 用户自助延期申请(当前管理员操作)
- TOTP 双因素(当前不做)
- 审计归档格式扩展(当前 CSV;可加 JSON)
- 通知渠道扩展(Webhook/企业微信/钉钉)
---
## 13. 下一步
1. 评审本计划(v0.2),确认范围与里程碑
2. 评审通过后启动 **M0**:工程骨架、CLI、CI、前端脚手架