Files
lislgosms/docs/fail2ban-assisted-blocking-design-20260814.md
T

379 lines
19 KiB
Markdown
Raw 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.
# Fail2ban 安全检测与人工封禁平台设计方案
> 版本:V1.0(需求评审稿)<br>
> 日期:2026-08-14<br>
> 范围:运营端自研 UI、安全检测、阈值配置、告警处置、人工封禁与解封<br>
> 边界:第一版不自动封禁、不开放任意 Fail2ban/防火墙命令、不在线编辑正则表达式
## 1. 建设目标
在运营端“安全控制”下建设自研安全检测面板,统一接收 SSH、运营端登录、客户端登录、CMPP 入站和 HTTP API 的异常行为,按照可配置规则聚合为安全告警。平台管理员查看证据后,可人工执行固定时长封禁、解封、忽略或加入保护名单。
第一版采用“检测与执行分离”原则:
1. Fail2ban 和应用侧检测器只生成事件及告警,不自动修改防火墙。
2. 人工点击“封禁”后,后端重新校验告警、真实 IP、保护名单、执行目标和操作权限。
3. 只有受限安全代理可以执行系统级封禁;NestJS 不直接获得 root、通用 sudo 或任意 shell 能力。
4. PostgreSQL 保存配置、事件、告警、封禁事实和操作审计;页面不得使用 mock、静态数据或 localStorage 伪造状态。
## 2. 第一版范围
### 2.1 检测类型
| 规则编码 | 检测对象 | 事件来源 | 第一版默认建议值 | 默认风险 |
| --- | --- | --- | --- | --- |
| `admin_login_failure` | 运营端账号登录失败 | NestJS 结构化安全事件 | 同一 IP 10 分钟 8 次 | 中 |
| `client_login_failure` | 客户端账号登录失败 | NestJS 结构化安全事件 | 同一 IP 10 分钟 8 次 | 中 |
| `ssh_auth_failure` | SSH 12022 认证失败 | journald + Fail2ban filter | 同一 IP 10 分钟 6 次 | 高 |
| `cmpp_auth_failure` | CMPP 17890 未知账号、认证失败或不允许 IP | Gateway 结构化安全事件 | 同一 IP 5 分钟 5 次 | 高 |
| `cmpp_protocol_abuse` | 非法协议包、异常版本或高频无效连接 | Gateway 结构化安全事件 | 同一 IP 1 分钟 20 次 | 高 |
| `http_invalid_api_key` | HTTP API 错误或未知访问密钥 | NestJS HTTP API 鉴权事件 | 同一 IP 5 分钟 10 次 | 高 |
| `http_signature_failure` | HTTP API 签名缺失、格式错误或验签失败 | NestJS HTTP API 验签事件 | 同一 IP 5 分钟 10 次 | 高 |
| `http_replay_attempt` | nonce、时间戳或幂等凭证重放 | NestJS HTTP API 验签事件 | 同一 IP 10 分钟 3 次 | 严重 |
| `http_malicious_scan` | 扫描敏感路径、跨路径高频 404、明显漏洞探测 | Nginx 结构化日志 + Fail2ban filter | 同一 IP 1 分钟 20 次且涉及至少 8 个不同路径 | 高 |
表中数值只是首次安装默认值,必须保存到真实数据库并可在运营端配置。业务 400、正常参数校验失败、合法客户偶发时钟偏差、普通 404、供应商连接失败不得直接归类为攻击。
### 2.2 处置能力
- 查看检测总览、趋势、来源、风险等级、Top IP 和待处置告警。
- 查看告警详情、脱敏日志证据、规则快照和历史处置。
- 人工封禁固定时长:10 分钟、1 小时、24 小时、7 天。
- 人工解封、忽略告警、加入保护名单。
- 查看当前真实封禁、执行目标、到期时间和同步状态。
- 配置检测规则阈值、时间窗口、冷却时间、风险等级和启停状态。
- 查看 Fail2ban、事件采集器、安全代理、规则版本及封禁执行器健康状态。
### 2.3 第一版不做
- 自动封禁。
- 永久封禁或前端输入任意封禁秒数。
- 在线编辑 Fail2ban regex、日志路径、action、iptables/nftables 或 Nginx 配置文本。
- 从页面执行任意 shell、`fail2ban-client``systemctl` 或防火墙命令。
- 自动将外部威胁情报加入封禁。
- 删除原始告警和处置历史。
## 3. 总体架构
```text
sshd journal ── Fail2ban 检测 jail ─┐
Nginx access/error ─ Fail2ban jail ─┤
NestJS 登录/HTTP 鉴权安全事件 ──────┤
Go Gateway CMPP 安全事件 ───────────┤
Security Event Collector
PostgreSQL 事件、规则、告警、封禁
NestJS 运营端安全 API
自研运营端 UI
↓ 人工确认
Root Security Agent (Unix Socket)
↓ ↓
nftables/manual jail Nginx real-IP deny
```
### 3.1 组件职责
#### Fail2ban
- 读取 sshd 和 Nginx 等系统日志。
- 使用固定、随版本发布的 filter 识别失败行为。
- 根据已生效规则计算窗口与阈值。
- 触发“告警事件 action”,但不直接封禁。
- 不作为平台告警历史和真实封禁状态的唯一数据源。
#### 应用侧安全事件
NestJS 和 Gateway 对其掌握真实业务语义的事件直接产生结构化安全事件,避免只靠文本正则猜测:
- 登录入口、失败类型和真实请求 IP。
- HTTP API 密钥查找失败、签名失败、重放拒绝。
- CMPP 账号、AuthenticatorSource 校验、IP 白名单和协议异常。
任何安全事件不得记录明文密码、完整 API 密钥、签名密钥、AuthenticatorSource 或完整短信内容;账号、密钥标识只保存脱敏值或不可逆指纹。
#### Security Event Collector
- 通过受限 Unix Socket 或 root 写入、collector 只读的事件目录接收 Fail2ban 事件。
- 校验事件版本、规则编码、IP、时间和来源。
- 写入 PostgreSQL,并依据数据库规则做幂等聚合。
- 维护最后事件时间、丢弃数量和解析失败指标。
#### Root Security Agent
- 独立于 NestJS,以最小 root 权限运行。
- 仅监听本机 Unix Socket,不监听 TCP 公网端口。
- 只接受固定 JSON 协议:`block``unblock``status``apply_rule_version`
- 对规则编码、执行器、IPv4/IPv6、时长和幂等键做白名单校验。
- 使用无 shell 参数数组或原生库执行,不拼接命令。
- 原子生成平台专属配置文件,校验后才 reload;失败保留旧版本。
#### NestJS
- 读取真实 PostgreSQL 告警和配置。
- 执行 RBAC、近期重新认证、保护名单和状态校验。
- 创建封禁操作记录并调用本地安全代理。
- 根据代理回读结果确认真实封禁状态。
- 不以 root 运行,不直接写 `/etc/fail2ban/*` 或防火墙。
## 4. Cloudflare 与执行器选择
恢复 `CF-Connecting-IP` 只能让 Nginx/应用识别访客真实 IP,并不会改变到达服务器的 TCP 源地址。对 `sms.lisglo.com` 的 Cloudflare 橙云流量,nftables 封禁访客真实 IP 无效,误封 Cloudflare 节点反而可能中断全站。
第一版按入口固定执行器:
| 入口 | 网络形态 | 检测 IP | 人工封禁执行器 |
| --- | --- | --- | --- |
| 运营端、客户端 | Cloudflare 橙云 | 经过可信 Cloudflare 网段恢复的真实 IP | Nginx real-IP deny;未来可扩展 Cloudflare API |
| `api.lisglo.com` | 灰云直连 | TCP 源 IP | nftables/manual jail |
| SSH 12022 | 公网直连 | TCP 源 IP | nftables/manual jail |
| CMPP 17890 | 公网直连 | Gateway TCP 远端 IP | nftables/manual jail |
只有当请求 TCP 来源属于定期同步的 Cloudflare 官方网段时,才信任 `CF-Connecting-IP`。客户端直连源站时提供的同名 Header 必须忽略。配置上线前必须用真实请求证明日志、事件和页面展示 IP 一致。
## 5. 可配置规则
### 5.1 可配置字段
- 启用状态 `enabled`
- 统计窗口 `windowSeconds`
- 触发阈值 `threshold`
- 不同目标数量阈值 `distinctTargetThreshold`,仅恶意扫描等规则使用。
- 告警冷却时间 `cooldownSeconds`
- 风险等级 `severity`
- 聚合维度,只能从规则预置集合选择,例如 `ip``ip+accountFingerprint`
- 默认封禁时长和允许的最大封禁时长。
- 是否允许人工封禁;只读检测规则可以关闭封禁按钮。
### 5.2 不可由页面配置的字段
- filter 正则表达式。
- 日志文件路径和 journal unit。
- shell 命令、Fail2ban action、nftables 表/链。
- Unix Socket 路径、systemd 服务名。
- Cloudflare 可信网段来源。
- 规则到执行器的映射。
这些内容属于发布资产,必须通过代码评审、自动化测试和标准部署更新。
### 5.3 配置保护
- 每类规则设置服务端最小值、最大值和允许枚举;前端约束不能替代后端校验。
- 阈值修改使用乐观锁版本号,防止多人覆盖。
- 保存后生成新规则版本,安全代理先语法校验,再原子切换并 reload。
- reload 失败时数据库状态标记 `apply_failed`,保留旧生效版本,页面明确展示“已保存但未生效”。
- 每次变更保存操作人、原因、旧值、新值、生效版本、代理回执和时间。
- 配置修改要求 `security.rule.manage` 权限和近期重新认证。
## 6. 数据模型
### 6.1 `SecurityDetectionRule`
- `id``code`(唯一)、`name``category``description`
- `enabled``windowSeconds``threshold``distinctTargetThreshold`
- `cooldownSeconds``severity``groupingMode`
- `manualBlockAllowed``defaultBlockDurationSeconds``maxBlockDurationSeconds`
- `configVersion``effectiveVersion``applyStatus``lastApplyError`
- `updatedById``createdAt``updatedAt`
### 6.2 `SecurityDetectionEvent`
- `id``eventKey`(唯一幂等键)、`ruleCode``sourceType`
- `sourceIp``accountFingerprint``targetFingerprint``requestPathNormalized`
- `occurredAt``receivedAt``evidenceSummary``metadata`
- `collectorInstanceId``ruleVersion`
`metadata` 使用后端安全 DTO,仅保存允许字段;禁止保存密钥和完整认证材料。
### 6.3 `SecurityAlert`
- `id``alertNo``fingerprint``ruleId``ruleSnapshot`
- `sourceIp``status``severity`
- `firstOccurredAt``lastOccurredAt``eventCount``distinctTargetCount`
- `cooldownUntil``assignedToId``handledById``handledAt``handleReason`
- `blockId``createdAt``updatedAt`
同一规则、IP、聚合维度和窗口桶使用唯一 fingerprint,重复采集只增加计数,不重复创建告警。
### 6.4 `SecurityBlock`
- `id``operationKey`(唯一)、`alertId``sourceIp`
- `executorType``executorTarget``durationSeconds`
- `status``requested/applying/blocked/unblock_requested/unblocked/expired/failed`
- `startedAt``expiresAt``verifiedAt``errorMessage`
- `requestedById``requestReason``unblockedById``unblockReason`
- `agentOperationId``createdAt``updatedAt`
### 6.5 `SecurityProtectedNetwork`
- IP/CIDR、名称、类型、适用入口、启停状态、来源和备注。
- 系统内置保护项不可从页面删除,只允许通过受控发布更新。
- 人工保护项新增、修改和停用均要求重新认证和审计。
## 7. 状态机与并发控制
### 7.1 告警状态
```text
pending ──→ block_requested ──→ blocked ──→ unblocked
│ └──────────→ block_failed
├──→ ignored
├──→ whitelisted
└──→ expired
```
- `pending` 仅表示达到检测阈值,绝不代表已被防火墙封禁。
- 封禁按钮通过数据库条件更新原子认领,只有一名操作人能进入 `block_requested`
- 代理执行成功后必须回读执行器状态,确认存在真实规则才写 `blocked`
- 网络超时导致结果不确定时先查询 `operationKey`,不得盲目重复封禁。
- 忽略、保护名单和封禁互斥;状态变化后旧页面操作返回 409。
### 7.2 封禁到期
- 执行器负责真实到期解除;平台定时回读并同步 `expired`
- 平台任务只做状态对账,不能仅靠数据库时间把记录标记为已解封。
- 对账发现执行器缺失、额外规则或到期未解除时生成系统告警。
## 8. 后端接口
```text
GET /api/admin/security-detection/overview
GET /api/admin/security-detection/trends
GET /api/admin/security-detection/alerts
GET /api/admin/security-detection/alerts/:id
POST /api/admin/security-detection/alerts/:id/block
POST /api/admin/security-detection/alerts/:id/ignore
POST /api/admin/security-detection/alerts/:id/protect
GET /api/admin/security-detection/blocks
POST /api/admin/security-detection/blocks/:id/unblock
GET /api/admin/security-detection/rules
PUT /api/admin/security-detection/rules/:id
GET /api/admin/security-detection/protected-networks
GET /api/admin/security-detection/health
```
封禁请求只接受固定时长枚举和原因。IP、规则、入口和执行器全部从告警及服务端映射读取,禁止前端重传或覆盖。
## 9. 权限与审计
| 权限 | 能力 |
| --- | --- |
| `security.alert.read` | 查看面板、告警和脱敏证据 |
| `security.alert.handle` | 忽略告警、分派和填写处置说明 |
| `security.block.manage` | 人工封禁与解封 |
| `security.rule.manage` | 修改阈值、启停规则和保护名单 |
封禁、解封、修改规则和保护名单必须要求近期重新认证;所有动作写入 `OperationLog`,记录资源、操作人、IP、原因、旧值、新值、代理回执和最终结果。读取完整证据也应记录访问审计。
## 10. 自研 UI 信息架构
菜单位置:`安全控制 / 安全检测`
### 10.1 总览
- 待处置、高风险、当前真实封禁、24 小时攻击 IP、封禁失败五个指标。
- 24 小时/7 天检测事件与告警趋势。
- 按规则类型、入口和风险等级分布。
- Top 攻击 IP、Top 扫描路径、Top 被尝试账号指纹。
- Fail2ban、collector、agent、规则版本和执行器健康卡片。
### 10.2 告警中心
- 按关键词、IP、规则、风险、状态、入口和日期筛选,真实后端分页。
- 列表展示风险、IP、类型、触发数、不同目标数、首次/最近时间、状态和操作。
- 详情抽屉展示规则快照、聚合时间线、脱敏证据、关联告警和处置历史。
- 封禁确认弹窗展示执行器、影响入口、时长、保护名单结果、近期合法访问提示和必填原因。
### 10.3 规则配置
- 使用平台现有自研 Card、Table、Tag、Modal、Form、Pagination 和图表体系,不嵌入 Fail2ban 第三方面板。
- 每项配置展示当前生效值、待生效值、最后修改人和应用状态。
- 数字输入同时展示单位、允许范围和默认建议值。
- 保存前展示变更对比;应用失败不能显示成功 toast。
### 10.4 封禁与保护名单
- 独立展示真实封禁状态、执行器、到期时间、来源告警和操作人。
- 保护名单命中时封禁按钮禁用并说明原因。
- 不同执行器使用明确标签,避免把 Nginx deny 误称为防火墙封禁。
## 11. 检测准确性要求
### 11.1 HTTP API
- 错误密钥:只记录不可逆密钥指纹,不记录完整 Header 或密钥。
- 签名错误:区分缺失、格式错误、算法不支持、验签失败和时间偏差。
- 重放:只有 nonce/幂等凭证已被真实使用或时间戳明显重复时计入;正常幂等重试按既有接口语义处理。
- 恶意扫描:使用标准化路径,不保存 query 中的敏感值;规则需要“次数 + 不同路径数”双阈值,避免单个合法 404 被判攻击。
- 反向代理真实 IP 必须经过可信代理链验证,禁止直接信任客户端 Header。
### 11.2 CMPP
- 未知账号、错误 AuthenticatorSource、不允许 IP、停用企业/应用和协议异常分别分类。
- 不记录明文密码、完整 AuthenticatorSource 或平台配置密钥。
- 正常断线、心跳超时、最大连接数限制和服务重启恢复不计为恶意认证。
### 11.3 登录
- 账号锁定仍由现有账户安全逻辑负责,安全检测面板不替代账号锁定。
- 图形验证码错误、账号错误、密码错误和角色入口错误分别保存分类,但页面证据统一脱敏。
- 一个入口的登录失败不得清理另一个入口的有效会话。
## 12. 保留与隐私
- 原始检测事件默认在线保留 30 天,聚合告警、封禁记录和操作审计默认保留 180 天;最终期限在上线前由安全和运营确认。
- 清理使用小批量、可恢复任务;不得删除仍关联活动封禁、未完成处置或审计保留期内的数据。
- 页面和导出默认脱敏账号、路径参数、User-Agent 中的可识别信息。
- 第一版不提供原始日志全文导出。
## 13. 可用性与降级
- Fail2ban 不可用:页面显示检测源异常,已有告警仍可查看;相关来源不允许宣称“无攻击”。
- Collector 不可用:健康状态告警并记录事件积压;恢复后按事件键幂等补录。
- Security Agent 不可用:封禁按钮返回明确失败,不修改告警为已封禁。
- PostgreSQL 不可用:不允许执行无法审计的封禁操作。
- 规则应用失败:继续使用上一生效版本,页面显示失败版本与原因。
- Nginx 或 nftables 回读不一致:封禁状态标记异常并产生系统告警。
## 14. 部署前置条件
1.`cmpp-api.service` 改为专用非 root 用户并完成文件、日志、MinIO/local storage 权限回归。
2. 安装 Fail2ban,固定版本并使用 nftables 兼容 action。
3. 为 Nginx、sshd、Gateway 和 NestJS 建立结构化、脱敏且可测试的事件格式。
4. 验证 Cloudflare 可信 IP 网段、`real_ip_header` 和源站绕过防护。
5. 建立 root security agent、Unix Socket 权限、systemd 加固和固定协议。
6. 发布前备份 PostgreSQL、运行源码、环境文件、Fail2ban/Nginx 平台生成配置和 nftables 当前规则。
## 15. 分阶段实施建议
### 阶段 A:检测与只读面板
- 数据模型、默认规则、结构化事件、collector、Fail2ban alert-only jail。
- 总览、告警列表、详情、规则只读展示和健康状态。
- 使用真实日志、真实 PostgreSQL 和真实接口验收。
### 阶段 B:阈值配置
- 规则编辑、版本、后端边界、受控配置编译、校验、原子应用和回滚。
- 配置变更对比、近期认证和审计。
### 阶段 C:人工封禁
- Security Agent、执行器、固定时长封禁、解封、保护名单、幂等和状态对账。
- Cloudflare/Nginx 与直连/nftables 分入口验收。
阶段 A、B、C 可以作为同一第一版需求连续交付,但验收必须逐阶段通过,不能为了展示按钮而跳过权限隔离和真实执行验证。
## 16. 第一版完成标准
- 九类检测全部有真实事件来源、默认规则、可配置阈值和原子测试。
- 自研 UI 通过真实 API 展示面板、告警、规则、封禁和健康数据。
- HTTP 错误密钥、签名错误、重放和恶意扫描均纳入第一版。
- NestJS 非 root,无法执行任意系统命令或编辑 Fail2ban 配置。
- 人工封禁按入口选择正确执行器,Cloudflare 场景不使用无效的访客 IP nftables 封禁。
- 保护名单、重新认证、权限、幂等、并发和操作审计全部通过。
- 只有回读执行器确认真实生效后,页面才显示“已封禁”。
- 不发送短信、不修改通道账号/密码/启停状态、企业余额或客户连接。