rfxn/brute-force-detection
GitHub: rfxn/brute-force-detection
一款基于指数衰减压力评分模型的 Linux 暴力破解检测与自动封禁工具,通过解析服务日志区分人为失误与自动化攻击,实现精准的 IP 封禁和多层告警。
Stars: 26 | Forks: 12
# 暴力破解检测 (BFD)
` 正则表达式模式;引擎负责处理日志读取、IP 提取、IPv6 规范化和验证。
传统的基于计数的工具会在固定的失败次数时进行封禁——这迫使运维人员在快速捕获攻击者或容忍合法失误之间做出选择——与此不同,BFD 使用 **指数衰减压力评分**。每次失败都会按服务严重程度增加权重不等的压力,并且压力会通过可配置的半衰期随时间衰减。一个在几分钟内多次输错密码的用户,其产生的压力会自然消退,远低于触发阈值。而不断猛烈攻击同一服务的机器人,其产生压力的速度会超过衰减速度,几乎立即触发阈值。这样既能减少对真实用户的误判,又能对实际攻击做出更快的响应。
BFD 使用日志跟踪系统,因此日志只会从上次读取的位置开始解析。这极大地提高了性能,因为我们不必持续读取相同的日志数据。日志跟踪系统兼容 syslog/logrotate 风格的日志轮转——它能在轮转发生时进行检测,并从新日志文件和已轮转的日志文件中获取最新数据。
**检测**
- 57 条带有兼容 fail2ban 的 `` 正则表达式模式的服务规则
- 指数衰减压力评分——人为的输入错误会逐渐消退,机器人的攻击会立即触发
- 按服务划分的严重性权重(SSH=3,控制面板=5,嘈杂的服务=1)
- 按规则和全局跨服务的压力触发阈值
- 可选的基于国家/地区的压力倍数
- 具有轮转感知的增量日志解析
**封禁**
- 带有自动过期和防火墙规则清理的临时封禁
- 累犯升级(线性、倍增或有上限的增长)
- 8 个带有自动检测功能的防火墙后端(APF, CSF, firewalld, UFW, nftables, iptables, route, custom)
- 具有完整状态跟踪的手动 ban/unban CLI
**IPv4/IPv6**
- 双栈检测和封禁,无需修改规则
- 为需要独立处理 IPv6 的防火墙 (ip6tables) 提供单独的 IPv6 封禁命令
- 自动排除两个地址系列的本地地址
**运维**
- 用于无损诊断的健康检查模式 (`bfd -c`)
- 用于在不封禁的情况下测试规则的 Dry-run 模式 (`bfd -d`)
- 用于详细的按规则和按封禁输出的 Verbose 模式 (`bfd --verbose`)
- 威胁活动报告,包含摘要、双间隔服务明细和封禁状态 (`bfd -a`)
- 每次运行的统计日志记录(检查的规则、解析的事件、执行的封禁)
- 批量邮件告警,包含丰富的上下文(封禁类型、持续时间、历史记录)和按规则的路由
- 手册页 (`man bfd`) 和 bash tab 补全会自动安装
### 1.1 支持的系统
BFD 可以在任何带有 bash 4.1+ 和标准 GNU 实用程序 的 Linux 发行版上运行。它已在以下系统上经过测试和支持:
**RHEL 系列:**
- CentOS 6, 7
- Rocky Linux 8, 9, 10
**Debian 系列:**
- Ubuntu 20.04, 24.04 (CI 测试);14.04 (通过 12.04 目标进行深度传统 CI 测试)
- Debian 12
日志路径在运行时自动检测:
- RHEL: `/var/log/secure`, `/var/log/messages`, `/var/log/maillog`
- Debian/Ubuntu: `/var/log/auth.log`, `/var/log/syslog`, `/var/log/mail.log`
BFD 需要 root 权限。除了基本系统之外,不需要任何额外的依赖。
## 2. 安装
包含的 `install.sh` 脚本会处理所有的安装任务:
```
./install.sh
```
这将:
- 将 BFD 安装到 `/usr/local/bfd`
- 将 `bfd` 命令放置在 `/usr/local/sbin/bfd`
- 自动启用 watch 模式,以实现约 10 秒的检测延迟(systemd 或 SysVinit)
- 安装一个 2 分钟的 cronjob 作为后备(在 watch 模式运行时跳过)
- 如果是升级,则从之前的安装中导入设置并重启 watch 模式
覆盖前会备份以前的安装。
- **安装路径:** `/usr/local/bfd`
- **可执行文件路径:** `/usr/local/sbin/bfd`
通过环境变量自定义安装路径:
```
INSTALL_PATH=/opt/bfd BIN_PATH=/usr/sbin/bfd ./install.sh
```
### 2.1 调度
**Watch 模式 (推荐):**
BFD 的 watch 模式作为持久化守护进程运行,每隔 `WATCH_INTERVAL` 秒(默认为 10 秒)轮询一次新的日志数据。检测延迟约为 10 秒,与 fail2ban 及其他基于守护进程的工具相当。
安装程序会在全新安装时自动启用并启动 watch 模式。
在升级时,现有的调度选择会被保留——如果 watch 模式之前正在
运行,它将被重启;如果启用了 `bfd.timer`,它将保持活动状态。
要手动管理 watch 模式:
```
# systemd (Rocky 8+, Debian 12, Ubuntu 20+)
systemctl enable --now bfd-watch.service
# SysVinit (CentOS 6/7, Ubuntu 14.04)
service bfd-watch start
chkconfig bfd-watch on # enable at boot (RHEL)
```
**Cron (自动后备):**
安装程序会在 `/etc/cron.d/bfd` 中放置一个 cronjob,在静默模式下每 2 分钟运行一次 BFD。这作为后备手段——当 watch 模式处于活动状态时,cron 运行会检测到锁并静默跳过。如果 watch 守护进程退出,cron 会在 2 分钟内自动恢复检测。
使用 watch 模式时无需移除 cron 条目。
### 2.2 升级
从以前的 BFD 安装(包括 v1.5-2)升级时,`install.sh` 会自动:
- 将现有安装备份到 `/usr/local/bfd.bk.DDMMYYYY-EPOCH`(符号链接为 `/usr/local/bfd.bk.last` 以便访问)
- 运行 `importconf` 来合并您的配置并保留状态
**importconf 自动迁移的内容:**
| 类别 | 详情 |
|----------|---------|
| 用户配置 | `conf.bfd` 的值合并到新模板上 |
| 旧版变量名 | `TRIG` → `PRESSURE_TRIP`, `TRIG_WINDOW` → `PRESSURE_HALF_LIFE`, `TRIG_GLOBAL` → `PRESSURE_TRIP_GLOBAL`, `BAN_DURATION` → `BAN_TTL`, `BAN_PERMANENT_*` → `BAN_ESCALATE_*` |
| 按规则覆盖 | 从之前的安装中保留 `pressure.conf`;旧的冒号格式自动转换为空格格式 |
| 防火墙后端 | 如果检测到 2.0.1 之前版本的 `BAN_COMMAND`,则设置为 `"custom"` |
| 封禁状态 | `bans.active`, `bans.history`, `pressure.dat` |
| 日志跟踪状态 | tlog 字节偏移量,journal 游标 |
| 告警模板 | `alert/` partials(保留用户修改过的部分),旧版 `alert.bfd` |
| 忽略列表 | `ignore.hosts` |
**升级后验证:**
```
bfd -c # health check and diagnostics
bfd --status # show current ban/event summary
```
**回滚:**
如果升级导致问题,请从备份中恢复:
```
bfd --flush-all # clear any new bans
rm -rf /usr/local/bfd
cp -a /usr/local/bfd.bk.last /usr/local/bfd
```
## 3. 配置
主配置文件是 `/usr/local/bfd/conf.bfd`。文件中每个选项的正上方都有描述性注释。首次运行前,请从头到尾查看该文件。
使用 `bfd -c` 可以验证您的配置而不会封禁任何内容。
### 3.1 压力模型 (检测)
BFD 使用指数衰减压力评分:每次失败的登录都会根据服务严重性增加不同权重的压力,并且压力会通过半衰期随时间衰减。当累积的压力超过触发阈值时,就会触发封禁。这可以自然地将人为失误与自动化攻击区分开来——几分钟内的几次输入错误会无害地衰减消失,而来自机器人的快速连续失败积累的速度远快于其衰减的速度。
```
pressure = SUM { weight * 2^(-(now - event_time) / half_life) }
```
**示例** (SSH 规则:=3, trip=15, half-life=300s):
- **Bot 攻击** — 1 秒内失败 7 次: pressure = 3×7 = 21.0 → 超过 15 → **封禁**
- **人为输入错误** — 4 分钟内失败 5 次: 早期的事件会衰减,总计 ≈ 11.1 → 低于 15 → **不封禁**
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `PRESSURE_TRIP` | `20` | 触发封禁所需的累积压力;按规则覆盖可在规则文件或 `pressure.conf` 中进行 |
| `PRESSURE_HALF_LIFE` | `300` | 半衰期,以秒为单位(压力衰减的速度);越短 = 越宽容 |
按规则的权重在 `pressure.conf`(集中配置)或各个规则文件中通过 `PRESSURE_WEIGHT` 进行配置。权重越高 = 压力积累越快。默认级别:5(控制面板),3(SSH/VPN/数据库/关键服务),2(邮件/FTP/Web),1(嘈杂/通用服务)。
`pressure.conf` 使用以空格分隔的 `key=value` 格式:
```
sshd weight=3 trip=20
postfix weight=2 trip=20 skip_alert=1
```
### 3.2 邮件告警
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `EMAIL_ALERTS` | `0` | 发送邮件告警 (0 = 关闭, 1 = 开启) |
| `EMAIL_ADDRESS` | `root` | 告警接收者,以逗号分隔 |
| `EMAIL_SUBJECT` | `Brute Force Warning for $HOSTNAME` | 主题行 (批量发送时自动附加 `(N bans)`) |
| `EMAIL_LOGLINES` | `5` | 告警正文和事件视图中每个主机的日志行数 |
| `EMAIL_FORMAT` | `text` | 邮件正文格式: `text` (纯文本), `html` (仅 HTML), `both` (多部分 text+HTML) |
| `EMAIL_DIGEST` | `cycle` | 摘要模式: `cycle` (每次运行发送一封邮件), `timed` (按间隔积累并发送) |
| `EMAIL_DIGEST_INTERVAL` | `900` | 当 `EMAIL_DIGEST="timed"` 时的摘要刷新间隔(以秒为单位,默认 15 分钟) |
| `EMAIL_REPUTATION_LINKS` | *(空)* | 告警中的 IP 信誉链接,以逗号分隔: `abuseipdb`, `shodan`, `virustotal`, `ipinfo`, `greynoise` |
告警是 **批量处理** 的:一个检查周期内的多个封禁会为每个收件人生成一封邮件,而不是每次封禁生成一封。每个告警包含主机、服务、带有阈值的压力分、带有持续时间和过期时间的封禁类型(临时/永久/升级)、国家/地区代码、累犯历史、IP 信誉链接、压力可视化条以及源日志行。
**摘要模式**:在 `cycle` 模式(默认)下,每次检测运行结束时发送一封邮件。在 `timed` 模式下,告警会在多次运行中积累,并在 `EMAIL_DIGEST_INTERVAL` 到期时刷新——这减少了活跃服务器上的邮件量。Watch 模式在每次迭代时检查假脱机队列;Cron 模式则在每次运行时检查。积累的告警会在关机和每日轮换时强制刷新。
**格式选项**:`text` 通过 `mail` 发送纯文本。`html` 通过 `sendmail` 发送 HTML。`both` 通过 `sendmail` 发送包含纯文本和 HTML 两部分的多部分 MIME 消息,以便收件人的电子邮件客户端显示其偏好的格式。如果 `sendmail` 不可用,`html` 和 `both` 将回退为通过 `mail` 仅发送纯文本。
邮件模板是可自定义的——参见 [第 3.10 节](#310-email-templates)。单个规则可以通过在规则文件中设置 `SKIP_ALERT="1"` 来抑制告警。在规则文件中设置 `RULE_EMAIL="addr"` 可以将该规则的告警路由到不同的收件人。
### 3.3 封禁
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `FIREWALL` | `auto` | 防火墙后端;参见 [第 5 节](#5-firewall) |
| `BAN_TTL` | `600` | 封禁持续时间,以秒为单位(0 = 永久)。临时封禁会自动过期 |
| `BAN_COMMAND` | APF deny | 当 `FIREWALL="custom"` 时执行的命令。参见 [第 5 节](#5-firewall) |
| `UNBAN_COMMAND` | APF unban | 反向操作命令。临时封禁自动过期所必需 |
在 ban/unban 命令中可以使用变量 `$ATTACK_HOST`、`$MOD`(服务名称)和 `$PORTS`(来自规则文件)。
### 3.4 累犯处理
这四个设置形成了一个管道:`BAN_ESCALATION` 控制封禁持续时间如何增长,`BAN_ESCALATION_CAP` 限制该增长,`BAN_ESCALATE_AFTER` 在达到计数后翻转为永久封禁,而 `BAN_ESCALATE_WINDOW` 是上述所有设定的回溯窗口。
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `BAN_ESCALATION` | `none` | 持续时间增长方式: `none` (固定), `linear` (10m, 20m, 30m...), `double` (10m, 20m, 40m, 80m...) |
| `BAN_ESCALATION_CAP` | `86400` | 最大升级持续时间,以秒为单位 (0 = 无上限) |
| `BAN_ESCALATE_AFTER` | `5` | 翻转为永久封禁前的临时封禁次数 (0 = 从不) |
| `BAN_ESCALATE_WINDOW` | `86400` | 用于计算重复违规的回溯窗口,以秒为单位 |
**示例** (当 `BAN_TTL="600"` 时):
- **固定 10m 封禁,第 5 次后永久:** `ESCALATION=none`, `ESCALATE_AFTER=5` → 10m, 10m, 10m, 10m, 10m → 永久
- **倍增封禁,第 5 次后永久:** `ESCALATION=double`, `ESCALATE_AFTER=5` → 10m, 20m, 40m, 80m, 160m → 永久
- **线性增长,有上限,永远不永久:** `ESCALATION=linear`, `CAP=3600`, `ESCALATE_AFTER=0` → 10m, 20m, 30m... 1h, 1h, 1h (始终临时)
### 3.5 IPv6
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `BAN_COMMAND_V6` | *(空)* | 特定于 IPv6 的封禁命令。当为空时,`BAN_COMMAND` 将用于这两种地址系列 |
| `UNBAN_COMMAND_V6` | *(空)* | 特定于 IPv6 的解封命令。当为空时,`UNBAN_COMMAND` 将用于这两种 |
当使用原生处理双协议的工具(带有 `inet` family 的 nft, APF, ip route)时,请保留为空。对于需要独立的 IPv4/IPv6 命令的工具 (iptables/ip6tables),请显式设置。参见 [IPv6](#11-ipv6)。
### 3.6 日志路径
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `AUTH_LOG_PATH` | `/var/log/secure` | 认证日志 (自动检测:Debian 上为 `/var/log/auth.log`) |
| `KERNEL_LOG_PATH` | `/var/log/messages` | 内核/syslog (自动检测:Debian 上为 `/var/log/syslog`) |
| `MAIL_LOG_PATH` | `/var/log/maillog` | 邮件日志 (自动检测:Debian 上为 `/var/log/mail.log`) |
| `BFD_LOG_PATH` | `/var/log/bfd/bfd.log` | BFD 自身的应用程序日志 |
日志路径会根据发行版自动检测。如果您的系统使用非标准路径,请在 `conf.bfd` 中覆盖。
### 3.7 高级
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `PRESSURE_TRIP_GLOBAL` | `0` | 跨服务聚合压力触发阈值 (0 = 禁用) |
| `SUBNET_TRIG` | `0` | 从同一子网触发子网封禁的唯一 IP 数 (0 = 禁用) |
| `SUBNET_MASK` | `24` | 用于分布式检测的 IPv4 子网掩码 |
| `SUBNET_MASK_V6` | `48` | IPv6 子网掩码 (必须是 16 的倍数) |
| `WATCH_INTERVAL` | `10` | Watch 模式的轮询间隔,以秒为单位 |
| `SCAN_MAX_LINES` | `50000` | 扫描模式下每个日志的最大行数;0 = 无限制 |
| `SCAN_TIMEOUT` | `120` | 扫描期间每个规则的 Journal 超时时间,以秒为单位 |
| `OUTPUT_SYSLOG` | `1` | 记录到 syslog (0 = 关闭, 1 = 开启) |
| `LOG_IDLE_SUPPRESS` | `1` | 抑制来自 syslog 的空闲 (0 事件) 运行完成消息 (0 = 关闭, 1 = 开启) |
| `LOG_FORMAT` | `classic` | 日志格式: `classic` (syslog 样式) 或 `json` (JSONL) |
| `LOG_LEVEL` | `1` | 最低严重性: 0=debug, 1=info, 2=warn, 3=error |
| `APOOL_RETENTION_DAYS` | `365` | 攻击池条目的最大保留天数 |
| `APOOL_MAX_LINES` | `500000` | 攻击池文件中的最大行数 |
附加变量(`LOG_SOURCE`、`LOCK_FILE_TIMEOUT`、`BAN_RETRY_COUNT`、`OUTPUT_SYSLOG_FILE`)在 `internals.conf` 中具有合理的默认值,可以通过将它们添加到 `conf.bfd` 来覆盖。
**JSON 日志格式**:当 `LOG_FORMAT="json"` 时,每条日志行都是一个 JSON 对象,包含的字段有:`ts` (ISO 8601)、`host`、`app`、`pid` (integer)、`level` (debug/info/warn/error/critical)、`tag` (服务名称,如果存在则从 `{tag}` 前缀中提取)、`msg`。示例:
```
{"ts":"2026-02-27T14:30:00+0000","host":"srv1","app":"bfd","pid":1234,"level":"info","tag":"sshd","msg":"192.0.2.1 exceeded login failures"}
```
### 3.8 国家/地区权重
当 `pressure-country.conf` 包含未注释的条目时,国家/地区权重会自动激活。无需切换——如果文件有条目,就会应用它们;如果所有条目都被注释掉(默认),国家/地区权重就会关闭。
国家/地区数据库将 IP 地址映射到 2 个字母的国家/地区代码。IPv4 范围存储在 `ipcountry.dat`(整数范围格式)中,IPv6 范围存储在 `ipcountry6.dat`(十六进制范围格式)中。两者都在安装时(后台)自动下载,并在数据超过 30 天时通过 `cron.daily` 刷新。可以使用 `update-ipcountry.sh` 手动更新,该脚本会从 ipverse.net(备用 ipdeny.com)下载按国家/地区的 CIDR 区域,并将它们转换为 BFD 的查找格式。IPv4 和 IPv6 地址都会被解析为国家/地区代码。
倍数文件(`pressure-country.conf`)使用以空格分隔的 `CC N` 格式,其中 N 是 weight×10(例如,`CN 20` 表示 2.0× 权重,`US 10` 表示 1.0× = 无变化)。未列出的国家/地区默认为 1.0×。为了向后兼容,也接受旧版的 `CC=N` 格式。
### 3.9 SMTP 中继
默认情况下,BFD 通过本地 MTA (`mail` 或 `sendmail`) 发送告警。对于没有本地 MTA 的服务器或通过外部邮件服务路由的服务,BFD 支持通过 `curl` 进行经过身份验证的 SMTP 中继。
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `SMTP_RELAY` | *(空)* | SMTP 中继服务器 URL (空 = 使用本地 MTA) |
| `SMTP_FROM` | *(空)* | 发件人地址 (设置 `SMTP_RELAY` 时必需) |
| `SMTP_USER` *(空)* | SMTP 认证用户名 |
| `SMTP_PASS` | *(空)* | SMTP 认证密码 |
**URL 格式:**
| 格式 | 示例 | 描述 |
|--------|---------|-------------|
| `smtps://host:465` | `smtps://smtp.gmail.com:465` | 隐式 TLS (Gmail 等) |
| `smtp://host:587` | `smtp://relay.example.com:587` | STARTTLS (大多数中继服务) |
| `smtp://host:25` | `smtp://relay.internal:25` | Plain (内部中继) |
**示例 — Gmail SMTP 中继:**
```
SMTP_RELAY="smtps://smtp.gmail.com:465"
SMTP_FROM="alerts@example.com"
SMTP_USER="alerts@example.com"
SMTP_PASS="app-password-here"
```
所有目标发行版上都存在 `curl`,并且原生处理 TLS/认证。凭据存储在 `conf.bfd` 中(权限 640,属于 root)。使用 `bfd -c` 验证中继配置。
### 3.10 邮件模板
告警邮件是从 BFD 安装路径(`/usr/local/bfd/alert/`)下的 `alert/` 目录中可自定义的模板片段渲染而来的。模板使用 `{{VAR}}` mustache 风格的占位符,在渲染时通过基于安全的 awk 引擎进行替换——没有执行 shell 代码,因此模板不会引入安全风险。
**模板文件:**
| 文件 | 描述 |
|------|-------------|
| `text.header.tpl` | 文本: 横幅,主机名,时间戳 |
| `text.entry.tpl` | 文本: 每次封禁的详细信息块 |
| `text.summary.tpl` | 文本: 摘要统计信息 |
| `text.footer.tpl` | 文本: 版本,项目链接 |
| `html.header.tpl` | HTML: 横幅和主机名栏 |
| `html.entry.tpl` | HTML: 带有压力详情的每次封禁卡片 |
| `html.summary.tpl` | HTML: 聚合统计表格 |
| `html.footer.tpl` | HTML: 页脚和结束标签 |
条目模板在每个封禁渲染一次。摘要模板仅当一封邮件中批量处理多个封禁时才会包含。页眉和页脚包裹整个消息。
要进行自定义,请将所需的片段复制到 `alert/custom.d/` 中并编辑副本。BFD 会首先为每个片段检查 `custom.d/`,如果不存在则回退到自带的默认设置。`custom.d/` 中的模板在升级时会自动保留。
**关键模板变量:**
| 变量 | 示例 | 描述 |
|----------|---------|-------------|
| `{{HOSTNAME}}` | `web01.example.com` | 系统主机名 |
| `{{HOST}}` | `192.0.2.1` | 被封禁的 IP 地址 |
| `{{SERVICE}}` | `sshd` | 服务名称 |
| `{{PRESSURE}}` | `21.4` | 压力分 |
| `{{FAIL_COUNT_DISPLAY}}` | `7` | 此周期检测到的登录失败尝试次数 |
| `{{BAN_TYPE}}` | `Temporary` | 封禁类型 (Temporary/Permanent/Escalated) |
| `{{BAN_DURATION_DETAIL}}` | `10m` | 人类可读的持续时间 |
| `{{COUNTRY_FLAG}}` | `CN` | 2 个字母的国家/地区代码 |
| `{{COUNTRY_DISPLAY}}` | `China (CN)` | 带有代码的国家/地区名称(降级为仅显示代码) |
| `{{SOURCE_LOGS_SECTION_TEXT}}` | *(日志行)* | 经过清理的源日志摘录 |
| `{{REPUTATION_SECTION_TEXT}}` | `AbuseIPDB: https://...` | 文本格式的信誉链接 |
有关完整的变量参考,请参见附带的模板文件。
消息渠道(Slack, Telegram, Discord)拥有它们自己的模板片段:
| 文件 | 描述 |
|------|-------------|
| `slack.message.tpl` | Slack Block Kit JSON 包装器 |
| `slack.entry.tpl` | Slack 每次封禁的部分 |
| `telegram.message.tpl` | Telegram MarkdownV2 包装器 |
| `telegram.entry.tpl` | Telegram 每次封禁的块 |
| `discord.message.tpl` | Discord embed JSON 包装器 |
| `discord.entry.tpl` | Discord 每次封禁的 embed 字段 |
定期报告(参见 [第 9 节](#9-periodic-reports)) 使用它们自己的模板片段:
| 文件 | 描述 |
|------|-------------|
| `report.html.header.tpl` | HTML 报告页眉和样式 |
| `report.html.body.tpl` | HTML 报告正文和表格 |
| `report.text.header.tpl` | 纯文本报告页眉 |
| `report.text.body.tpl` | 纯文本报告正文 |
| `report.slack.message.tpl` | Slack 报告摘要 |
| `report.telegram.message.tpl` | Telegram 报告摘要 |
| `report.discord.message.tpl` | Discord 报告摘要 |
### 3.11 Slack 告警
BFD 可以通过传入 Webhooks 或 Bot API 向 Slack 频道发送告警通知。
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `SLACK_ALERTS` | `0` | 启用 Slack 告警 (`1` = 启用) |
| `SLACK_MODE` | `webhook` | 投递模式: `webhook` 或 `bot` |
| `SLACK_WEBHOOK_URL` | *(空)* | 传入 Webhook URL (webhook 模式) |
| `SLACK_TOKEN` | *(空)* | Bot API token, `xoxb-...` (bot 模式) |
| `SLACK_CHANNEL` | *(空)* | 频道 ID 或 `#name` (bot 模式) |
**Webhook 模式** 更简单——在 [Slack Incoming Webhooks](https://api.slack.com/messaging/webhooks) 创建一个 Webhook 并粘贴 URL 即可。**Bot 模式** 支持文件上传并使用 [Slack Web API](https://api.slack.com/methods/chat.postMessage)。
要求 PATH 中包含 `curl`。使用 `bfd --test-alert slack` 进行测试。
### 3.12 Telegram 告警
BFD 可以通过 Telegram Bot API 发送告警通知。
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `TELEGRAM_ALERTS` | `0` | 启用 Telegram 告警 (`1` = 启用) |
| `TELEGRAM_BOT_TOKEN` | *(空)* | 来自 [@BotFather](https://t.me/BotFather) 的 Bot Token |
| `TELEGRAM_CHAT_ID` | *(空)* | 聊天、群组或频道 ID |
使用 `@userinfobot` 或 `getUpdates` API 端点查找您的聊天 ID。消息使用 MarkdownV2 格式发送。
要求 PATH 中包含 `curl`。使用 `bfd --test-alert telegram` 进行测试。
### 3.13 Discord 告警
BFD 可以通过 Webhooks 向 Discord 频道发送告警通知。
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `DISCORD_ALERTS` | `0` | 启用 Discord 告警 (`1` = 启用) |
| `DISCORD_WEBHOOK_URL` | *(空)* | 来自 服务器设置 > 整合 的 Webhook URL |
告警渲染为 Discord embeds,包含每次封禁的字段和摘要页脚。
要求 PATH 中包含 `curl`。使用 `bfd --test-alert discord` 进行测试。
## 4. 使用方法
`/usr/local/sbin/bfd` 命令提供以下选项:
```
usage: bfd [OPTION]
Run Modes:
-s, --standard run detection cycle with output
-q, --quiet run detection cycle silently
-d, --dryrun run detection without banning
-w, --watch run continuous watch mode (foreground)
Ban Management:
-b, --ban IP [SERVICE] manually ban an IP (permanent)
-u, --unban IP unban an IP address
--flush-temp remove all temporary bans
--flush-all remove all bans
Reporting: Supports: --json --csv
-l, --list list active bans
-a, --activity [IP|STR] threat activity and IP investigation
-e, --events [IP|CIDR] [N] event history, IP or subnet detail (N=log lines)
System:
-S, --status [SERVICE] operational status overview
-C, --config [VAR] show active configuration
-R, --rules [RULE] list rules or show rule detail (--active)
-c, --check health check diagnostics
Testing:
-T, --test RULE [FILE|-] test rule patterns against log or stdin
--test-pattern PAT [FILE|-] test raw pattern against log or stdin
--test-alert TYPE send test alert (email,slack,telegram,discord)
Scan Mode:
--scan [RULE] [-d] full-log scan (all rules or specific rule)
--max-lines=N max lines per log during scan (default 50000)
--scan-timeout=N journal timeout per rule during scan (default 120)
Output Modifiers:
--json JSON output (with -l, -e, -a)
--csv CSV output (with -l, -e, -a)
--sort=MODE sort events: count (default), time, ip
--limit=N max IPs to display (default 100, 0=all)
--24h events from last 24 hours (default)
--7d events from last 7 days
--30d events from last 30 days
--active show only active rules (with -R)
-V, --verbose detailed output (with -s, -d, -c, -S, --scan)
General:
-v, --version display version
-h, --help display this help (see bfd.1 for full docs)
```
**`-s|--standard`** 和 **`-q|--quiet`** 选项运行完整的检测和封禁周期。标准模式打印输出;静默模式抑制输出(由 cron 使用)。两者都会解析日志,根据触发阈值计算压力,并执行封禁。
### 4.1 Dry Run (试运行)
**`-d|--dryrun`** 选项运行完整的检测,但只记录“将要封禁”而不是执行封禁命令。使用此选项可以安全地测试规则,验证您的配置,并查看 BFD 会执行什么操作而不会影响生产环境。
```
bfd -d
```
### 4.2 健康检查
**`-c|--check`** 选项对您的整个 BFD 安装执行无损诊断检查:
- 验证配置(必需变量,合理的值)
- 检查日志文件路径是否存在且可读
- 验证封禁命令二进制文件是否存在且可执行
- 当 `BAN_TTL > 0` 时,如果 `UNBAN_COMMAND` 为空则发出警告
- 如果配置了 `BAN_COMMAND_V6`,则检查其二进制文件
- 扫描所有规则:报告活动与非活动状态、压力权重/触发阈值、端口、日志路径
- 显示压力模型摘要(半衰期,触发阈值,全局触发阈值)
- 验证 tlog(日志跟踪脚本)是否可执行
- 检查状态目录是否存在且权限正确
- 报告锁文件状态
- 计算活动封禁数
```
bfd -c
```
输出使用 `[PASS]`、`[WARN]`、`[FAIL]` 和 `[SKIP]`(非活动规则)指示符,并附带最终摘要。
### 4.3 威胁活动
**`-a|--activity`** 选项显示带有聚合摘要、热门威胁 IP 和按服务细分的威胁活动报告:
```
bfd -a # show threat activity report
bfd --activity # same as above
bfd -a 192.0.2 # search for a specific string
```
报告包括:
- **威胁活动摘要** — 24 小时和 7 天时间窗口内的唯一 IP 数和总计数,以及活动封禁数
- **Top 25 威胁 IP (24h)** — 事件计数,IP,压力,国家/地区,首次/最后一次出现时间,服务,封禁状态(活动封禁显示 `BANNED(perm)` 或 `BANNED(Xm)`,以前的封禁显示 `prev:N`)
- **Top 25 威胁 IP (7d)** — 相同格式,7 天时间窗口
- **按服务划分的威胁明细 (24h / 7d)** — 双间隔计数,唯一 IP,以及每个服务的热门国家/地区
### 4.4 Watch 模式
**`-w|--watch`** 选项将 BFD 作为连续守护进程运行,每隔 `WATCH_INTERVAL` 秒(默认为 10 秒)轮询一次新的日志数据。这是 **推荐的操作模式**——检测延迟约为 10 秒,与 fail2ban 及其他基于守护进程的工具相当。
```
bfd --watch
```
Watch 模式在其整个生命周期内持有一个锁,因此当 watch 模式处于活动状态时,基于 cron 的运行 (`bfd -q`) 将静默跳过。如果 watch 守护进程意外退出(OOM,崩溃),cron 会自动检测到失效的 PID 并在一个周期内(约 2 分钟)恢复检测。无需修改 cron。
**信号处理:**
| 信号 | 动作 |
|--------|--------|
| `SIGTERM` / `SIGINT` | 干净关闭(移除锁文件) |
| `SIGHUP` | 无需重启即可重新加载 `conf.bfd`、`internals.conf` 和 `pressure.conf`(允许更改 `WATCH_INTERVAL`、`PRESSURE_TRIP`、封禁命令等) |
**服务管理:**
```
# systemd (Rocky 8+, Debian 12, Ubuntu 20+)
systemctl enable --now bfd-watch.service
systemctl reload bfd-watch # send SIGHUP
systemctl status bfd-watch
```
这与 `bfd.timer` 冲突——systemd 阻止同时启用两者。
```
# SysVinit (CentOS 6/7, Ubuntu 14.04)
service bfd-watch start
service bfd-watch reload # send SIGHUP
service bfd-watch status
chkconfig bfd-watch on # enable at boot (RHEL)
```
**配置:**
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `WATCH_INTERVAL` | `10` | Watch 模式的轮询间隔,以秒为单位 |
### 4.5 清除封禁
一次性移除多个封禁:
```
bfd --flush-temp # remove all temporary bans (keep permanent)
bfd --flush-all # remove all bans (temporary + permanent)
```
被清除的封禁会记录在封禁历史中。
### 4.6 结构化输出
将 `--json` 或 `--csv` 与 `-l`、`-e` 或 `-a` 结合使用,以获取机器可读的输出:
```
bfd -l --json # active bans as JSON
bfd -l --csv # active bans as CSV
bfd -e --json # active events as JSON
bfd -e 192.0.2.1 --csv # per-IP events as CSV
bfd -a --json # threat activity as JSON
bfd -a 192.0.2.1 --csv # IP report as CSV
```
**JSON** 输出对象数组(或用于 IP 详情和 CIDR 的嵌套对象):
```
[{"ip": "...", "pressure": 18.4, "pressure_trip": 20, "events": 5, "services": ["sshd"], ...}]
```
**CSV** 输出带有标题行:
```
ip,pressure,pressure_trip,events,services,first_seen,last_seen,status
```
结构化输出中的时间戳使用 ISO 8601 格式 (`YYYY-MM-DDTHH:MM:SS`)。
JSON 中的压力值是无引号的数字。
### 4.7 扫描模式
扫描模式通过检测管道处理**完整的当前日志文件**,绕过增量的 tlog 读取器。这适用于:
- **首次安装:** 立即捕获现有的攻击者,而不是等待新的日志事件。
- **规则更改:** 将新规则追溯应用于现有日志数据。
- **恢复:** 在检测空白期(守护进程停机,cron 禁用)后处理遗漏的事件。
- **取证审查:** 结合 `-d` (dry-run) 使用,查看在不封禁的情况下能检测到什么。
```
bfd --scan # scan all active rules against full logs
bfd --scan sshd # scan a specific rule only
bfd --scan -d # dry-run: detect without banning or advancing cursors
bfd --scan --max-lines=100000 # override line limit
bfd --scan --scan-timeout=60 # override journal timeout
```
在非 dry-run 的扫描之后,tlog 游会前进到当前日志位置,以便下一次常规运行全新开始。在 dry-run 模式下,游标不会被修改。
**安全限制:** `SCAN_MAX_LINES`(默认 50000)限制了每个日志文件处理的行数。`SCAN_TIMEOUT`(默认 120 秒)限制了 journal 读取的时间。设置 `--max-lines=0` 可实现无限制(在处理大型日志时需谨慎)。两者均可在 `conf.bfd` 中配置或在命令行上覆盖。
**锁行为:** 扫描获取与常规运行相同的全局锁。如果 watch 模式正在运行,请先停止它 (`systemctl stop bfd-watch`),运行扫描,然后再重新启动。
### 4.8 事件与调查
`--events` 命令提供事件历史记录和 IP 调查,从记录了所有检测到的认证失败(包括触发封禁的和低于触发阈值的观察)的攻击池读取,并具有可配置的保留期(默认:365 天):
```
bfd --events # event list — top 100 IPs, last 24h
bfd --events --7d --sort=time # last 7 days, newest first
bfd --events --limit=0 --30d # all IPs from last 30 days
bfd --events 192.0.2.1 # IP investigation — history + pressure + logs
bfd --events 192.0.2.0/24 # CIDR report — subnet-scoped view
```
**事件列表**(无参数)显示具有失败次数、服务、国家/地区、首次/最后一次出现时间和封禁状态的 IP。默认:按计数降序排列的前 100 个 IP,24 小时时间窗口。使用 `--limit=N` 更改输出上限(`0` 表示无限制),使用 `--sort=time` 或 `--sort=ip` 更改排序,使用 `--7d` 或 `--30d` 扩大时间窗口。
**IP 调查** 显示一份综合报告:来自攻击池的历史失败计数(总计和按服务细分)、如果 IP 具有活动压力的实时压力详情,以及来自触发服务的日志样本。
**CIDR 模式** 过滤到子网(IPv4,掩码 8-32),并包含带有匹配计数、事件总数和封禁计数的汇总行。
所有三种模式都支持 `--json` 和 `--csv`:
```
bfd --events --json # event list as JSON array
bfd --events 192.0.2.1 --json # IP detail as single JSON object
bfd --events 10.0.0.0/8 --csv # CIDR as CSV
```
### 4.9 退出码
| 代码 | 含义 |
|------|---------|
| 0 | 成功 |
| 1 | 配置错误(无效配置,未知选项) |
| 2 | 锁错误(另一个实例正在运行) |
| 3 | 先决条件错误(缺少 tlog,规则目录) |
*权威参考请参见 `man bfd`(1) §EXIT CODES。*
## 5. 防火墙
BFD 支持通过 `FIREWALL="auto"`(默认)进行自动防火墙检测。当设置为 auto 时,BFD 会按优先级顺序探测已安装的防火墙:APF > CSF > firewalld > UFW > nftables > iptables > ip route。
| `FIREWALL` 值 | 描述 |
|------------------|-------------|
| `auto` | 自动检测 (默认) |
| `apf` | Advanced Policy Firewall |
| `csf` | ConfigServer Security & Firewall |
| `firewalld` | firewalld rich rules |
| `ufw` | Uncomplicated Firewall |
| `nftables` | nftables sets (inet bfd table) |
| `iptables` | iptables/ip6tables chains |
| `route` | ip route blackhole |
| `custom` | 用户自定义的 BAN_COMMAND/UNBAN_COMMAND |
当配置了 `FIREWALL="auto"` 或指定的后端时,BFD 会原生处理 ban/unban——`BAN_COMMAND` 和 `UNBAN_COMMAND` 仅在 `FIREWALL="custom"` 时使用。
以下示例适用于 `FIREWALL="custom"` 模式。变量 `$ATTACK_HOST` 会在封禁时替换为违规 IP 地址。对于临时封禁,还需将 `UNBAN_COMMAND` 设置为反向操作。
**APF:**
```
BAN_COMMAND="/etc/apf/apf -d $ATTACK_HOST {bfd.$MOD}"
UNBAN_COMMAND="/etc/apf/apf -u $ATTACK_HOST"
```
**iptables (CentOS 6/7, Ubuntu 14-20):**
```
BAN_COMMAND="/sbin/iptables -I INPUT -s $ATTACK_HOST -j DROP"
UNBAN_COMMAND="/sbin/iptables -D INPUT -s $ATTACK_HOST -j DROP"
```
**firewalld (Rocky 8+, CentOS 7):**
```
BAN_COMMAND="/usr/bin/firewall-cmd --add-rich-rule='rule family=ipv4 source address=$ATTACK_HOST drop'"
UNBAN_COMMAND="/usr/bin/firewall-cmd --remove-rich-rule='rule family=ipv4 source address=$ATTACK_HOST drop'"
```
**nftables (Rocky 9+, Debian 12, Ubuntu 22+):**
```
BAN_COMMAND="/usr/sbin/nft add rule inet filter input ip saddr $ATTACK_HOST drop"
UNBAN_COMMAND="/usr/sbin/nft delete rule inet filter input handle $(/usr/sbin/nft -a list chain inet filter input | grep $ATTACK_HOST | awk '{print $NF}')"
```
**ip route null-route (所有发行版):**
```
BAN_COMMAND="/sbin/ip route add blackhole $ATTACK_HOST/32"
UNBAN_COMMAND="/sbin/ip route del blackhole $ATTACK_HOST/32"
```
**特定端口阻止 (使用规则文件中的 `$PORTS`):**
```
BAN_COMMAND="/sbin/iptables -I INPUT -s $ATTACK_HOST -p tcp -m multiport --dports $PORTS -j DROP"
UNBAN_COMMAND="/sbin/iptables -D INPUT -s $ATTACK_HOST -p tcp -m multiport --dports $PORTS -j DROP"
```
**IPv6 防火墙命令** — 如果您的防火墙需要针对 IPv6 使用单独的命令,请设置 `BAN_COMMAND_V6`(对于处理双协议的工具,请保留为空):
```
BAN_COMMAND_V6="/sbin/ip6tables -I INPUT -s $ATTACK_HOST -j DROP"
UNBAN_COMMAND_V6="/sbin/ip6tables -D INPUT -s $ATTACK_HOST -j DROP"
```
## 6. 规则引擎
规则位于 `/usr/local/bfd/rules/`。每条规则都是一个 shell 片段,它声明了服务名称、必需的二进制文件、日志路径以及用于匹配身份验证失败的正则表达式模式。
每条规则会根据特定应用程序二进制文件 (`PREREQ`) 的存在而自动启用。例如,如果 `/usr/sbin/sshd` 存在,sshd 规则就会激活。无需手动激活——安装应用程序后,BFD 就会检测到它。
使用 `bfd -c` 可以查看您的系统上哪些规则处于活动状态。
### 6.1 规则目录
BFD 附带 57 条规则:
| 类别 | 规则 |
|----------|-------|
| **SSH** | sshd, dropbear |
| **Mail** | dovecot, courier, postfix, postscreen, sendmail, exim_authfail, exim_nxuser, vpopmail, cyrus-imap, sogo |
| **FTP** | vsftpd, vsftpd2, proftpd, pure-ftpd |
| **Web** | apache-auth, nginx-http-auth, mod_sec, wordpress, roundcube, http_401, lighttpd, phpmyadmin, gitea, nextcloud, vaultwarden, drupal, jellyfin |
| **Panel** | cpanel, plesk, webmin, directadmin, interworx, cockpit, gitlab, grafana, proxmox, guacamole |
| **Auth** | pam_generic, xrdp |
| **Database** | mysqld-auth, postgresql, mongodb |
| **DNS** | named, powerdns |
| **VPN** | openvpnas, openvpn |
| **VoIP** | asterisk_badauth, asterisk_iax, asterisk_nopeer, freeswitch |
| **Proxy** | haproxy, squid |
| **Messaging** | ejabberd |
| **Legacy** | rh_imapd, rh_ipop3d |
### 6.2 规则自定义
每个规则文件支持以下变量:
| 变量 | 描述 |
|----------|-------------|
| `PREREQ` | 必需的二进制文件或文件的路径。仅当此文件存在时规则才处于活动状态 |
| `LOG_FILE` | 要监控的日志文件路径(使用配置变量如 `$AUTH_LOG_PATH`) |
| `MATCHED_HOSTS` | 提取的 IP 列表 — 使用 `` 模式的 tlog + extract_hosts 管道 |
| `PRESSURE_WEIGHT` | 按服务的压力权重(覆盖 `pressure.conf`;越高 = 积累越快) |
| `PRESSURE_TRIP` | 按服务的触发阈值(覆盖 `pressure.conf` 和全局 `PRESSURE_TRIP`) |
| `PORTS` | 用于特定端口阻止的服务端口(例如,sshd 为 `"22"`) |
| `SKIP_ALERT` | 设置为 `"1"` 以抑制此服务的电子邮件告警 |
| `RULE_EMAIL` | 覆盖此规则告警的 `EMAIL_ADDRESS`(按规则路由) |
| `LOG_TAG` | 用于 tlog 状态文件命名和 journal 过滤器分发的跟踪标签(例如,`"sshd"`,`"dovecot"`) |
| `IGNOREREGEX` | 匹配此 ERE 模式的行将在 IP 提取之前被排除(兼容 fail2ban) |
要自定义规则的压力权重和触发阈值:
```
# 在 /usr/local/bfd/rules/sshd 中 — 使 SSH 失败计数增至三倍
PRESSURE_WEIGHT="3"
PRESSURE_TRIP="15"
```
集中式的按规则覆盖(无需编辑规则文件)放在 `/usr/local/bfd/pressure.conf` 中(以空格分隔的 `key=value` 格式;语法参见 [压力模型](#31-pressure-model-detection))。
## 7. 忽略列表
BFD 提供了两种机制来将地址排除在封禁之外:
- **`/usr/local/bfd/ignore.hosts`** — 永不封禁的 IP,每行一个。支持 IPv4 和 IPv6 地址。
- **`/usr/local/bfd/exclude.files`** — 包含要忽略的 IP 的附加文件。每行一个文件路径;每个引用的文件都包含要排除的 IP。
BFD 会自动检测本地 IPv4 和 IPv6 地址(包括 `::1`)并将它们排除在封禁之外。本地地址排除无需手动配置。
## 8. CDN / 受信代理
当 BFD 监控位于 CDN 或反向代理(例如,Cloudflare,AWS CloudFront)后面的服务时,日志可能包含代理的 IP 而不是真实的客户端 IP。如果不了解 CDN IP 范围,BFD 可能会封禁 CDN 基础设施——从而阻止所有代理的流量。
CDN 子系统会自动获取提供商的 IP 范围,并在检测期间应用按提供商设定的处理方式。
### 8.1 配置
CDN 默认会自动检测——只需在 `cdn-providers.conf` 中取消注释提供商即可。
要强制开启/关闭,请在 `conf.bfd` 中设置:
```
CDN_ENABLE="auto" # auto-detect from cdn-providers.conf (default)
CDN_ENABLE="1" # force on regardless of provider config
CDN_ENABLE="0" # force off
CDN_UPDATE_DAYS="7" # refresh interval (days); 0 = manual only
```
### 8.2 提供商配置
编辑 `cdn-providers.conf` 以启用提供商。以空格分隔的格式:
```
# 名称 处理方式 乘数 格式 URL_V4 URL_V6
cloudflare ignore 10 text https://www.cloudflare.com/ips-v4/ https://www.cloudflare.com/ips-v6/
aws-cloudfront derate 3 json https://d7uri8nf7uskq.cloudfront.net/tools/list-cloudfront-ips -
fastly exclude 10 json https://api.fastly.com/public-ip-list -
```
字段:
- **NAME**: 提供商标识符 (字母数字 + 连字符)
- **TREATMENT**: `ignore` (完全不可见), `exclude` (可见但从不封禁), `derate` (降低压力)
- **MULT**: 压力倍数 (10=1.0x, 5=0.5x, 3=0.3x) — 仅适用于 `derate`
- **FORMAT**: `text` (每行一个 CIDR) 或 `json` (自动提取 CIDRs)
- **URL_V4/URL_V6**: 提供商 IP 范围 URL (`-` 表示无)
### 8.3 处理模式
| 模式 | 检测 | 事件 | 压力 | 封禁 |
|------|-----------|--------|----------|-----|
| `ignore` | 跳过 | 无 | 无 | 否 |
| `exclude` | 已处理 | 已记录 (`cdn-exclude`) | 已积累 | 否 |
| `derate` | 已处理 | 已记录 | 通过 MULT 减少 | 是 (如果达到阈值) |
### 8.4 CLI 命令
```
bfd --cdn # list all providers with status
bfd --cdn cloudflare # show CIDRs for a provider
bfd --cdn update # force refresh all provider ranges
bfd --cdn check 172.70.34.1 # check if an IP matches a CDN range
bfd --cdn --json # JSON output
```
### 8.5 自动更新
`cron.daily` 会根据 `CDN_UPDATE_DAYS` 检查 CDN 数据库的期限,并在过期时刷新。手动刷新:`bfd --cdn update` 或直接运行 `update-cdn-providers.sh`。
### 8.6 添加自定义提供商
向 `cdn-providers.conf` 中添加一行,包含任何以纯文本 CIDRs 或 JSON 发布 IP 范围的提供商:
```
my-proxy derate 5 text https://example.com/ip-ranges.txt -
```
## 9. 定期报告
BFD 可以生成通过所有配置的告警渠道(电子邮件,Slack,Telegram,Discord)发送的定时威胁报告:
```
bfd --report # daily report (default)
bfd --report weekly # last 7 days
bfd --report monthly # last 30 days
```
报告包括:
- **威胁摘要:** 唯一 IP 数,事件总数,封禁数,活动封禁数
- **热门威胁 IP** 包含国家/地区代码,实时压力和封禁状态
- **按服务细分** 包含事件计数和热门国家/地区
- **趋势对比** 与之前等效的时间窗口相比
**定时发送** 在 `conf.bfd` 中配置:
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `REPORT_ENABLED` | `0` | 启用定期报告 (0 = 关闭, 1 = 开启) |
| `REPORT_INTERVALS` | `"daily"` | 以逗号分隔的间隔: `daily`, `weekly`, `monthly` |
| `REPORT_CHANNELS` | *(空)* | 投递渠道 (空 = 所有已启用的告警渠道) |
| `REPORT_EMAIL_ADDRESS` | *(空)* | 报告电子邮件收件人 (空 = `EMAIL_ADDRESS`) |
| `REPORT_EMAIL_SUBJECT` | *(模板)* | 电子邮件主题 (`{{INTERVAL}}` 和 `{{HOSTNAME}}` 在发送时展开) |
| `REPORT_TOP_N` | `25` | 报告表格中的最大 IP 数 |
当 `REPORT_ENABLED=1` 时,`cron.daily` 会每天触发日报,在周一触发周报,在 1 号触发月报。也可以随时从 CLI 按需生成报告。
## 10. 封禁管理
封禁可以是临时的(在 `BAN_TTL` 秒后自动过期)或永久的(`BAN_TTL=0`)。临时封禁需要设置 `UNBAN_COMMAND`,以便在过期时自动移除防火墙规则。
在 `B_ESCALATE_WINDOW` 秒内发生 `BAN_ESCALATE_AFTER` 次临时封禁的累犯将被升级为永久封禁。
**CLI 命令:**
```
bfd -l # list all active bans (service, ports, expiry)
bfd -u 192.0.2.1 # unban an IP address
bfd -b 192.0.2.1 # manually ban an IP permanently
bfd -b 192.0.2.1 sshd # manually ban with a service label
```
**状态文件** 位于 `/usr/local/bfd/tmp/`:
| 文件 | 描述 |
|------|-------------|
| `bans.active` | 当前活动的封禁 (时间戳,过期时间,IP,服务,端口) |
| `bans.history` | 所有 ban/unban 事件的追加式日志 |
| `pressure.dat` | 压力评分工作区 (时间戳,IP,服务,权重);每约 10 个半衰期修剪一次 |
**状态文件** 位于 `/usr/local/bfd/stats/`:
| 文件 | 描述 |
|------|-------------|
| `attack.pool` | 统一事件存储 — 所有检测到的认证失败(封禁,升级,观察)及结果 |
`bfd -a` (威胁活动) 和 `bfd -e` (事件) 都从攻击池读取。低于触发阈值的观察 (ACTION=observed) 会与封禁决定一起记录,以便事件历史记录在压力衰减周期中持续存在。每个 IP 都会显示其当前是否被封禁、其封禁类型(永久或剩余时间)以及历史封禁次数。
## 11. IPv6
BFD 自动检测并封禁 IPv4 和 IPv6 地址。规则无需修改——提取引擎可以处理这两种地址系列。IPv6 地址在计数和比较之前会进行规范化。
对于原生处理双协议的防火墙(带有 `inet` family 的 nft,APF,ip route),请将 `BAN_COMMAND_V6` 保留为空——所有地址将使用 `BAN_COMMAND`。
对于需要单独命令的防火墙,请设置 `BAN_COMMAND_V6` 和 `UNBAN_COMMAND_V6`:
```
BAN_COMMAND_V6="/sbin/ip6tables -I INPUT -s $ATTACK_HOST -j DROP"
UNBAN_COMMAND_V6="/sbin/ip6tables -D INPUT -s $ATTACK_HOST -j DROP"
```
本地 IPv6 地址(包括 `::1` 和所有链路本地地址)会被自动检测并排除在封禁之外。
## 集成
BFD 通过标准接口与现有基础设施集成。
**防火墙后端** — 从 8 个后端(APF, CSF, firewalld, UFW, nftables, iptables, route, custom)自动检测或由用户选择。参见 [防火墙](#5-firewall)。
**日志系统** — 通过 tlog 从 syslog 文件或 systemd journal 读取。轮转感知:检测 logrotate 并从当前文件和已轮转文件中获取尾部数据。
**告警渠道** — 电子邮件(本地 MTA 或 SMTP 中继),Slack(webhook 或 Bot API),Telegram(Bot API),Discord(webhooks)。所有渠道都支持批量摘要发送和可自定义的模板。
**结构化输出** — 在 `-l`、`-e` 和 `-a` 后附加 `--json` 和 `--csv`,以供 SIEM、日志聚合器或自动化管道消费。
**CDN 感知** — 带有可配置处理方式(ignore, exclude, derate)的按提供商 IP 范围数据库可防止封禁 CDN 基础设施。参见 [CDN / 受信代理](#8-cdn--trusted-proxy)。
**调度** — Watch 模式(systemd/SysVinit 守护进程,约 10 秒延迟)或 Cron(2 分钟后备)。两者通过锁协调安全共存。
## 故障排除
首先运行 `bfd -c`——它会在单次无损检查中验证配置、日志路径、防火墙二进制文件、规则状态、状态目录和活动封禁。
| 症状 | 原因与修复 |
|---------|-------------|
| "locked subsystem, already running?" | 上一次 BFD 运行仍处于活动状态或已被杀死。锁会在 300 秒后自动清除 |
| "BAN_COMMAND binary not found" | 配置的防火墙工具未安装。在 `conf.bfd` 中更新 `BAN_COMMAND` |
| 规则未触发 | 检查日志路径是否存在,以及应用程序是否正在写入预期的文件。使用 `bfd -d` 在不封禁的情况下测试检测。使用 `bfd -c` 查看哪些规则处于活动状态 |
| Debian/Ubuntu 上的日志路径错误 | 日志路径是自动检测的,但可以在 `conf.bfd` 中覆盖:`AUTH_LOG_PATH`, `MAIL_LOG_PATH`, `KERNEL_LOG_PATH` |
| "UNBAN_COMMAND is empty" | 在 `conf.bfd` 中设置 `UNBAN_COMMAND` 以便临时封禁在过期时自动移除防火墙规则 |
| IPv6 地址未被检测到 | IPv6 支持是自动的。如果使用 ip6tables,请在 `conf.bfd` 中设置 `BAN_COMMAND_V6` |
## 许可证
BFD 由 Ryan MacDonald [ryan@rfxn.com] 以志愿者形式开发和提供支持。
BFD (Brute Force Detection) 在 GNU 通用公共许可证 (GPL) 下分发,对使用或再分发没有限制。BFD 版权声明和 GNU GPL "COPYING.GPL" 包含在发行版的顶级目录中。衍生作品必须按照 GNU GPL 的要求注明出处。
## 支持
BFD 源代码仓库位于:https://github.com/rfxn/brute-force-detection
错误、功能请求和一般问题可以作为 GitHub issues 提交,或发送至 proj@rfxn.com。报告问题时,请附上 `bfd -c` 的输出以帮助诊断配置问题。
官方项目页面位于:https://www.rfxn.com/projects/brute-force-detection/
标签:Bot检测, CDN代理, Cutter, Elastic, GeoIP, IPv4/IPv6, IP封禁, Linux服务器, PB级数据处理, 免杀技术, 入侵防御, 压力评分, 告警系统, 威胁情报, 安全运维, 审计日志, 封禁升级, 应用安全, 开发者工具, 暴力破解检测, 红队行动, 网络安全, 自动化防御, 趋势报告, 防火墙后端, 隐私保护