# 管理员双重验证(2026-11-11) 已部署本机 `http://127.0.0.1:5182`。入口为右上角锁形按钮「双重验证」,三个角色均可维护本人设置。既有账号默认不启用;root 密码和原登录方式保持,未为用户账号自动绑定验证器。 ## 使用 1. 点击「双重验证」,输入当前密码开始绑定。在身份验证器中手动添加账号,填入页面显示的密钥,选择基于时间、6 位、30 秒更新;本期提供手动密钥,不生成二维码。 2. 输入动态码并点击「验证并开启」。绑定准备有效 10 分钟,必须在发起绑定的同一登录中确认;确认前不启用。关闭准备窗口不会启用验证,重新开始会生成新密钥。 3. 保存只显示一次的 8 条恢复码,可下载文本。每条只能使用一次;关闭窗口后无法再次查看原码。开启后其他登录立即失效,当前登录保留。 4. 登录先验证用户名和密码,启用账号随后出现动态码/恢复码输入框。验证成功前不签发或保存登录令牌。动态码成功使用一次后需等待身份验证器刷新,服务端容许前后一个 30 秒时间步的时钟误差。 5. 更新恢复码或关闭验证,均须当前密码及有效动态码/恢复码,并二次确认。更新使全部旧恢复码作废,关闭恢复密码登录;两项操作均撤销其他登录,保留当前登录。 动态码遵循 [RFC 6238](https://www.rfc-editor.org/rfc/rfc6238),使用 SHA-1、6 位和 30 秒时间步。请保持验证器和服务器时间同步。丢失验证器和全部恢复码时需要受控运维恢复,当前网页不提供其他管理员代删 MFA、邮件找回或目录服务恢复;重置密码也不会绕过已有双重验证。 ## 接口与保护 | 接口 | 用途 | | --- | --- | | `GET /api/v1/auth/mfa` | 本人启用状态、剩余恢复码数量,不回显密钥 | | `POST /api/v1/auth/mfa/setup` | 当前密码验证后生成临时绑定密钥 | | `POST /api/v1/auth/mfa/confirm` | 验证动态码、启用并返回一次性恢复码 | | `POST /api/v1/auth/mfa/recovery` | 当前密码和验证代码校验后更新恢复码 | | `DELETE /api/v1/auth/mfa` | 当前密码和验证代码校验后关闭 | | `POST /api/v1/auth/login` | 新增可选 `code`;启用账号必须通过第二因素 | - 临时和启用密钥使用 AES-GCM 加密,独立用途密钥由 `VDI_JWT_SECRET` 派生,附加认证数据绑定管理员标识。备份恢复须保留对应环境密钥,不能只恢复数据库或随意替换 JWT 密钥。 - 恢复码使用 128 位随机值,库中仅保存哈希;消费、会话创建、恢复码轮换、设置修改及相关审计使用事务。TOTP 时间步单调递增,同一动态码及同一恢复码的并发请求仅一次成功。 - 账号行锁将登录与权限/密码/MFA 修改串行,事务内复核当前登录及最新账号状态。开启、关闭及恢复码更新分别记录 `enable_mfa/disable_mfa/rotate_mfa_recovery`,审计不保存密钥、明文恢复码、密码或验证码。 - MFA 设置验证和登录各使用独立持久化尝试预算:每账号每 5 分钟 10 次、每来源地址 30 次。失败尝试和成功尝试均计数;返回 429 与 Retry-After。错误密码不会暴露账号是否已启用 MFA,也不会消费恢复码。 - 密钥、恢复码和密码只在当前页面流程中短暂保留,关闭窗口清理,不写入 localStorage/sessionStorage。恢复码仅通过明确下载操作保存到用户本地。 新增 `admin_mfa/admin_mfa_recovery` 附表,不改原账号及会话表字段。关闭 MFA 删除本人附表记录及恢复码;删除临时管理员时级联清理。验证设置取消或过期后不激活,过期密文保留至下一次绑定覆盖或账号清理。 ## 验收 - 独立 PostgreSQL 的 Go 全量测试与 vet 通过;专项通过 RFC 六组 SHA-1 测试向量(含 2038 年之后)、时间窗口、动态码重复使用拒绝、恢复码重复使用拒绝、并发消费、密文账号绑定、三角色本人维护、准备过期/跨登录拒绝及尝试限制。另补充超级管理员重启后设置保留及改密仍需第二因素的验证,专项通过。 - 前端 lint/typecheck、3 项单元测试和生产构建通过;新增 6 项 MFA 页面测试通过,既有管理员/在线登录/界面 12 项回归通过。最初 3 条错误文案断言未包含请求编号,修正断言后全部通过,应用提示逻辑正常。 - 真实临时只读账号完成开启、原登录撤销、密码单独登录拒绝、真实当前时间动态码登录、动态码重复拒绝、恢复码登录及重复拒绝、恢复码更新及旧码失效、关闭后密码登录。设置变化审计不含秘密,临时账号及其 MFA/会话已清理。 - 真实浏览器操作完成绑定确认、恢复码下载、登录第二因素、恢复码登录及关闭;1440/390px 深浅主题通过,无脚本异常、非预期 API 错误或越界写请求。真实截图仅展示登录提示及启用状态,不保存有效密钥或恢复码。 - 原账号/密码指纹、用户授权、终端身份与配置、告警规则及三台 VM 配置摘要一致,盒子在线。验收期间盒子另行提交成功的 `gpu-prepare` 任务:业务任务 61→62,VM100 开机、VM101 关机,VM102 保持关机。该次设备任务和前后状态已完整保留,不把电源变化当作本轮 MFA 测试操作,也未强制恢复现场状态。 证据:[真实接口](evidence/cloud-mfa-20261111/live-verification.json)、[真实页面](evidence/cloud-mfa-20261111/browser-verification.json)。集成测试只使用一次性 `vdi-mfa-test-db`,未在业务库清空数据;隔离库为减少清理同步等待临时关闭 fsync,因此这些测试不证明数据库断电恢复能力,业务 PostgreSQL 配置未改动。 ## 部署与回退 部署前备份 `.local/live/backups/before-mfa-20261111-154828.dump`,原镜像标签 `before-mfa-20261111-154828`。独立构建基线二进制与部署前 SHA-256 一致:`ac04d8f5423b07aa2afa442013a4ddeaee55bbf0343a0876c50a8e829c6a4779`,保留已部署 GPU 功能及 Compose 配置。 旧 API 不检查新增 MFA 表,直接回退会绕过第二因素。只要存在已启用 MFA 的账号,就必须保留强制验证的兼容 API,不能直接回退到旧镜像;部署脚本的失败恢复也加入了启用账号检查。只有确认没有启用账号且没有后续部署时,才可恢复原镜像并沿用 `.local/mfa-review/compose.release.yml` 和原环境配置;附表可保留,不删除业务卷或恢复整库覆盖其他工作。 此项为可选的本人 TOTP 与恢复码,尚未实现组织强制策略、目录集成、硬件安全密钥或 WebAuthn。专用 VM、Windows 副本、额外磁盘及真实快照还原仍按用户要求暂缓。