# Fail2ban 安全检测与人工封禁平台设计方案
> 版本:V1.0(需求评审稿)
> 日期:2026-08-14
> 范围:运营端自研 UI、安全检测、阈值配置、告警处置、人工封禁与解封
> 边界:第一版不自动封禁、不开放任意 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 封禁。
- 保护名单、重新认证、权限、幂等、并发和操作审计全部通过。
- 只有回读执行器确认真实生效后,页面才显示“已封禁”。
- 不发送短信、不修改通道账号/密码/启停状态、企业余额或客户连接。