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 / 前端 / 运行时产物忽略规则
This commit is contained in:
2026-08-29 21:28:52 +08:00
commit f70b344c85
2 changed files with 347 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
# Go
/bin/
*.test
coverage.out
*.prof
# 前端构建产物
web/node_modules/
web/dist/
web/.vite/
# 运行时产物
*.log
data/
*.db
*.db-shm
*.db-wal
# 其他
.DS_Store
.env
config.toml
+325
View File
@@ -0,0 +1,325 @@
# 项目计划:服务器用户管理节点(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、前端脚手架