66 lines
5.9 KiB
Markdown
66 lines
5.9 KiB
Markdown
# 控制台登录保护
|
||
|
||
从 main 的数据库迁移 `000035` 开始启用。仅保护管理端账号密码登录,不改变业务 SDK Token、Worker 或结果订阅认证。保持单管理员,不引入角色管理系统。
|
||
|
||
## 默认行为
|
||
|
||
- 15 分钟失败窗口内连续失败 5 次,临时锁定 15 分钟。成功登录清空失败计数;失败窗口或锁定自然到期后重新计数。锁定期间尝试正确密码也不能登录,继续请求不会延长锁定。
|
||
- 同一账号每分钟最多受理 10 次,同一来源 IP 每分钟最多 30 次。固定窗口从首次尝试开始;成功登录不清空限流计数。任意一项触发就拒绝。
|
||
- 账号不存在、停用、密码错误或绑定凭据失效,正常拒绝均为 401。未知账号也参与相同限流/锁定,使用相同成本的 bcrypt 校验。既有账号按规范用户 ID 计数,避免等价 Unicode 登录名绕过限制。
|
||
- 触发锁定或限流返回 429,响应包含 `Retry-After` 秒数。登录页显示原因和倒计时;刷新页面不能绕过服务端限制。已登录会话不受一次新登录锁定影响,不中断业务服务。
|
||
- 数据库或审计提交失败返回 503,不签发 Cookie。跨站登录请求为 403;非法/过大的 JSON 为 400。
|
||
|
||
所有保护状态存放 MySQL,多节点共用,重启不归零。昂贵的密码校验在事务外执行:先用短事务占用请求预算,校验结束后再次锁定账号保护行;失败计数、锁定、成功会话和最终审计在同一事务提交。并发结果不能越过已经生效的账号锁定。校验进程异常退出的审计保留 PENDING,超过 10 分钟由清理器标 ERROR;不将未知结果当作密码错误。
|
||
|
||
## 部署配置
|
||
|
||
所有 Orbit 节点必须使用一致配置;不允许用 0 关闭保护。修改部署配置并重启服务后生效,不重置已有计数或已记录的锁定截止。降低阈值后按新阈值检查。
|
||
|
||
| 环境变量 | 默认值 | 约束 / 含义 |
|
||
| --- | --- | --- |
|
||
| `ORBIT_CONSOLE_LOGIN_FAILURE_LIMIT` | `5` | 1–100 次,触发临时锁定 |
|
||
| `ORBIT_CONSOLE_LOGIN_FAILURE_WINDOW` | `15m` | 1s–24h,连续失败窗口 |
|
||
| `ORBIT_CONSOLE_LOGIN_LOCK_DURATION` | `15m` | 1s–24h,新触发锁定时长 |
|
||
| `ORBIT_CONSOLE_LOGIN_ACCOUNT_LIMIT` | `10` | 1–1000 次 / 账号 / 限流窗口 |
|
||
| `ORBIT_CONSOLE_LOGIN_IP_LIMIT` | `30` | 1–10000 次 / IP / 限流窗口 |
|
||
| `ORBIT_CONSOLE_LOGIN_RATE_WINDOW` | `1m` | 1s–24h,固定限流窗口 |
|
||
| `ORBIT_CONSOLE_LOGIN_AUDIT_DAYS` | `90` | 1–3650 天,登录审计独立保留期 |
|
||
| `ORBIT_CONSOLE_TRUSTED_PROXIES` | 空 | 明确信任的代理 CIDR,逗号分隔 |
|
||
|
||
普通 Compose 和 HA Compose 均透传这些变量。本地测试环境在 `.local/compose-test/compose.env` 修改所需配置,再执行 `python3 scripts/orbit-local.py up`。此文件含其他私密配置,不提交仓库;不能仅在启动脚本外 export,脚本会隔离外部项目环境变量。
|
||
|
||
默认只使用 TCP 对端 IP,忽略客户端自带的 X-Forwarded-For、X-Real-IP。部署域名反向代理时,将实际代理的地址或精确网段填入 `ORBIT_CONSOLE_TRUSTED_PROXIES`,例如同机代理 `127.0.0.1/32,::1/128`。只在直连对端可信时解析 X-Forwarded-For,从右向左剥离可信代理;遇到第一个非可信地址停止。格式非法或跳数过多时退回 TCP 对端。代理应覆盖或按可信链追加转发头,不能把所有互联网地址都配置为受信代理。未配置时所有经代理的请求共用代理 IP 配额,账号维度仍独立生效。
|
||
|
||
## 查看审计
|
||
|
||
管理端右上角“登录审计”,仅部署指定的 `ORBIT_CONSOLE_APPLICATION_ADMIN` 可查看,默认每页 10 条。显示时间、登录名、来源 IP、结果和可重试时间;记录用户名不代表该账号一定存在。SDK Bearer、未登录或普通控制台账号无权读取全局记录。
|
||
|
||
接口:`GET /console/auth/audit?before=<上页next>`,认证为有效 HttpOnly 管理员会话 Cookie。响应:
|
||
|
||
```json
|
||
{"data":{"items":[{"id":"42","username":"operator","remote_ip":"192.0.2.1","user_agent":"browser","outcome":"LOCKED","created_at":"2026-09-29T12:00:00Z","finished_at":"2026-09-29T12:00:00Z","retry_at":"2026-09-29T12:15:00Z"}],"next":"42"}}
|
||
```
|
||
|
||
`next` 仅在还有下一页时提供,是倒序分页的排他上界;无全库计数、不承诺跨页快照。结果类型:SUCCESS、INVALID、LOCKED、ACCOUNT_LIMIT、IP_LIMIT、PENDING、ERROR。审计从不保存密码、Cookie、Token 或这些秘密的摘要,浏览器列表不会自动拉取接入 Token。
|
||
|
||
登录拒绝示例:
|
||
|
||
```http
|
||
HTTP/1.1 429 Too Many Requests
|
||
Retry-After: 900
|
||
Cache-Control: no-store
|
||
Content-Type: application/json
|
||
|
||
```
|
||
|
||
限流代码为 `LOGIN_RATE_LIMITED`。不要让客户端自动循环重试账号密码登录。
|
||
|
||
## 保留与升级
|
||
|
||
迁移只新增 `orbit_console_login_guards` 与 `orbit_console_login_audit`,无外键,不更改已有用户、密码、会话、凭据及业务记录。先备份,停止旧节点,再前向迁移和统一部署;旧版没有保护,不能和新版混跑。
|
||
|
||
每分钟清理一次,每种语句最多处理 500 行:48 小时未更新的保护状态、超过配置天数的登录审计,以及超时 PENDING 的状态归类。有效保护窗口最长 24 小时,48 小时非活动清理不会提前解除有效锁定。清理有索引、有截止时间,失败保留记录并写错误日志;这不会缩小数据库已分配磁盘,也不替代备份。业务“保留策略”管理的是命名空间历史,登录审计的全局保留时间由上述独立配置管理。
|
||
|
||
锁定自然到期后使用正确密码登录即可;不自动改密码、撤销业务 Token 或踢出已有会话。若持续遭受攻击,管理员应按审计中的来源限制访问并核对反向代理配置,不能通过关闭计数绕过保护。
|