peaceharborco/swatter

GitHub: peaceharborco/swatter

Swatter 是一个基于 Bash/awk 的轻量级 Web 日志分析工具,能感知 Cloudflare 并在正确的防火墙层面自动封禁恶意 IP。

Stars: 1 | Forks: 0

# 🪰 Swatter [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/9b922ef198063930.svg)](https://github.com/peaceharborco/swatter/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) **一个感知 Cloudflare 的滥用行为击退工具,专为 cPanel + CSF 服务器设计。** Swatter 会读取您的 Web 服务器日志,根据加权的行为信号加上可选的威胁情报对每个 IP 进行评分, 并自动在正确的防火墙层面上封禁恶意 IP。分布式扫描器、凭据暴力破解、 `/.env` 和 `/.git` 探测、漏洞模糊测试工具以及请求洪水,在导致您的负载飙升之前就会被击退。 屡教不改者将面临永久封禁。 它作为一组小型的 Bash + awk 脚本在 cron 上运行。没有 agent,没有 daemon,没有 编译依赖,也没有任何外联通讯。 ``` $ swatter top IP SCORE OFFN TEMP PERM CHANNEL LAST 45.146.165.10 92 4 3 1 csf high_badpath_repeat 104.152.52.20 78 1 1 0 csf scanner_profile 193.32.162.40 85 2 2 0 cloudflare request_flood ``` ## 为什么它与众不同:它不会搞垮您的网站 大多数“针对 Apache 的 fail2ban”工具在由 Cloudflare 前置的服务器上都有一个致命的盲点。 您的日志显示了**真实的访客 IP**(通过 `mod_remoteip` / `CF-Connecting-IP`),但实际的 TCP socket 是一个 **Cloudflare 边缘 IP**。 在您的服务器防火墙上封禁该 socket,您就等于封禁了 Cloudflare — **该机器上的所有网站都会宕机。** Swatter 正是围绕这一点构建的。对于每个违规者,它会做出以下决定: - **直连源站**(访问了您的原始 IP 或 cPanel 服务端口,*或者*从非 Cloudflare 节点 针对您的 Web 端口保持了活跃的 TCP 连接 — 即使在持有有效的 `Host` 头的情况下也是如此,因为代理请求总是从 CF 边缘到达) → 在 **CSF** 处进行封禁。这是安全的:该 socket 确实属于攻击者。 - **通过 Cloudflare**(经由代理进入) → 通过 API 应用区域范围内的 **Cloudflare IP Access Rule**, 默认为 **managed challenge** — 因此误报意味着人类用户需要解决一个验证,而不是被锁定。 CSF 层面永远不会被触碰。 - **模糊不清** → 默认使用 Cloudflare 层面(更安全的那个)。 Cloudflare 自身的 IP 段被硬编码为**永不封禁**集合,并在每次执行封禁前立即重新检查。 如果 IP 段列表丢失或过期,Swatter 会**安全失效** — 它会完全停止下发 CSF 拒绝规则,而不是 冒着服务中断的风险。 四种 Cloudflare 防御姿态(`CF_MODE`): | 姿态 | 您的设置 | 经由 CF 的违规者 | 直连违规者 | |---|---|---|---| | `auto` | **默认** — 检测并适应 | 除非证实为裸露直连,否则使用安全层面 | CSF | | `direct` | 位于 CF 之后,Swatter 是防御主力 | 通过 API 进行验证/封禁 | CSF | | `skip` | 位于 CF 之后,**由您**运行自己的 WAF 规则栈 | 记录日志,交由您的边缘规则处理 | CSF | | `off` | 根本不在 Cloudflare 后面 | 不适用 — 全都是直连 | CSF | **开箱即用的二选一。** `auto`(默认模式)会检测服务器是否 位于 Cloudflare 之后 — 它会解析您自己的 vhost 并检查它们、您的 入站 socket 以及您的 Web 服务器配置是否与 Cloudflare IP 段匹配 — 然后 为您选择防御姿态。检测在安装、`refresh-feeds` 和 `test-config` 时运行,绝不会在每次扫描时运行,因此 cron 路径的成本很低。它是**偏向安全**的: 只有明确判定“这台机器不在 Cloudflare 后面”时才会关闭 CF 层面;任何不确定的情况都会保持分类功能开启(即安全层面),所以 `auto` 永远不会错误地将代理 socket 导向 CSF。因此,一台**完全没有 Cloudflare 域名**的机器 会作为一个纯粹的 CSF 自动封禁器运行,无需任何 Cloudflare 配置 — 而且它确实会下发封禁:安全失效机制(当 CF IP 段列表丢失时会禁用 CSF 拒绝)只会在由 CF 前置的机器上触发, 因为在这种机器上才需要通过列表来区分边缘 socket 和直连 socket。 检测每天重新运行一次(即 `refresh-feeds` cron),因此如果某台机器 加入或离开 Cloudflare,`auto` 也会做出适应。它从 domlog 文件名中识别该机器的域名,因此在**纯粹的(非 cPanel)Apache/nginx** 服务器上,如果只有一个共享的访问日志, 它无法证明该机器是裸露直连的,并会保持在安全的、开启分类的防御姿态。在这样一台不在 Cloudflare 后面的服务器上, 请显式设置 `CF_MODE="off"`(`test-config` 会告诉您它何时无法自行判断)。 如果您维护自己的 Cloudflare WAF 规则、速率限制和登录页面 保护,请显式设置 `skip`:Swatter 会避开您已经管理的领域,但仍然会在 CSF 处击退扫描原始 IP 的扫描器,并记录下它本应执行验证的内容。 **不要**在代理服务器上使用 `off` 来表示“不对 Cloudflare 进行干预” — `off` 会禁用层面分类本身,因此代理访客(包括您自己的客户) 可能会被错误地导向 CSF 拒绝规则,这不仅会阻断他们,还会切断他们的邮件和 cPanel 访问权限。(设置任何显式模式都会跳过检测。) 在理念上最接近的工具是 [CrowdSec](https://crowdsec.net) 及其 Cloudflare bouncer,它很好用 — 如果您想要一个 daemon、一个中心化信誉 网络以及针对每个关注点的独立组件。Swatter 则处于光谱的另一端: bash + gawk + cron,没有常驻进程,cPanel/CSF 原生,并且 将直连与代理的分类内建到每一个决策中,而不是作为 bouncer 事后附加。 ## 评分机制 每个 IP 都会在一个滑动窗口内,根据信号的**加权混合**获得 0–100 的评分: | 信号 | 捕获目标 | |---|---| | 请求速率 | 高频敲击 / 洪水攻击 | | 错误率 (4xx/5xx) | 扫描器会产生错误 | | 错误爆发 (403/404/444) | 暴力破解 & 路径模糊测试 | | URL 扇出 | 路径枚举 / 扫描 | | **恶意路径命中** | `/.env`, `/.git`, `wp-login.php`, `xmlrpc.php`, `/cgi-bin/`, shell 投放, … | | User-agent | `sqlmap`, `nikto`, `zgrab`, 空的 UAs | | POST 洪水 | 登录 / xmlrpc / 垃圾评论 | | 无 vhost / 裸 IP | 只有攻击者才会通过 IP 直接访问您 | | **信誉度** | AbuseIPDB, Spamhaus, IPsum (可选) | 单纯的平均值会淡化*针对性*的攻击(凭据暴力破解只会触发少数 信号),因此 Swatter 将混合评分与**决定性下限**配对: `/.env` 探测、持续的洪水攻击、广泛的扫描器特征,或对 敏感端点的重复命中,每一项都足以独立触发行动 — 而且每个决策都会将*原因*连同证据一起记录到 `/var/log/swatter/decisions.jsonl`。 可以在 `/etc/swatter/swatter.conf` 和 `/etc/swatter/badpaths.conf` 中调整所有内容(权重、阈值、恶意路径表)。 ## 威胁情报富化(可选,免费) 对于看起来已经形迹可疑的 IP,Swatter 可以在采取行动前对照外部 信誉源进行交叉验证: - **IPsum** — 聚合的阻止列表,不需要密钥。 - **Spamhaus DROP/EDROP** — 被劫持/犯罪的网段,不需要密钥。 - **AbuseIPDB** — 置信度评分,免费层级(每天 1,000 次检查),支持缓存并 有配额限制。在 `/etc/swatter/swatter.conf` 中设置 `ABUSEIPDB_KEY` 即可启用它。 信誉度只会在分数处于边缘时将其*提高* — 它永远不会单独执行封禁,并且 失败或离线的查询会被直接忽略。在 API 密钥数为**零**的情况下也能完全工作。 ## 每日摘要 — 击退错误*以及*恶意行为者 `swatter report` 会每晚发送一封摘要邮件,涵盖服务器健康的各个层面: - **恶意行为者** — 执行的封禁(永久/临时),按违规类型、恶意路径 类别和渠道(CSF vs Cloudflare)分组,外加供审查的允许列表豁免情况。 - **服务器错误** *(可选,`ERROR_DIGEST_ENABLE`)* — 来自 Apache、 PHP-FPM、各站点 PHP 和 MySQL 在同一时间窗口内的 FATAL/ERROR,过滤掉了已知的大量 噪音,其余部分按特征分组。可以直接指向日志,或者 复用现有的统一错误日志。 在真正安静的时段它会保持静默。发送方式是可插拔的 — `sendmail`(默认)、**SendGrid** 或 **Brevo** — 因此那些 IP 不是 From 域名授权发件人的主机 仍然可以进行发送。安装程序会安排它每晚运行;请根据您的时区设置 cron 小时。 ``` swatter report --test # force-send now, to verify delivery swatter report 7d # ad-hoc 7-day digest swatter report --print # print the digest to stdout, send nothing ``` ### 邮件发送设置 所有的控制开关都在 `/etc/swatter/swatter.conf` 中: 1. 设置 `REPORT_EMAIL`(留空则禁用摘要)和 `REPORT_FROM`。 2. 选择 `REPORT_METHOD`。`sendmail` 不需要其他设置。`sendgrid` 和 `brevo` 需要 `curl` + `jq` 以及一个 API 密钥 — 将其保存在仅限 root 访问的文件中,并让配置指向它: install -m 600 /dev/null /etc/swatter/sendgrid.key vi /etc/swatter/sendgrid.key # 粘贴 API 密钥,不要写其他内容 然后在 `swatter.conf` 中:`REPORT_METHOD="sendgrid"` + `SENDGRID_KEY_FILE="/etc/swatter/sendgrid.key"`,或者 `REPORT_METHOD="brevo"` + `BREVO_KEY_FILE="/etc/swatter/brevo.key"`。 3. 使用 `swatter report --test` 验证发送情况。 ## 安全第一 - **默认仅生成报告。** 开箱即用时,Swatter 只会评分并记录决策,但 不会触碰任何东西。观察它一周,然后将 `SWATTER_MODE` 切换为 `"enforce"`。 - **先临时后永久。** 第一次违规是临时封禁(TTL 阶梯为 1h → 6h → 24h → 72h)。永久封禁是通过屡次违规*赢得*的,绝不会因 单个时间窗口而触发 — 因此一次异常的爆发不会导致共享/CGNAT IP 被拉黑。 - **熔断器。** 对每次运行执行的封禁数量有硬性上限,且针对 灾难性的 CSF 渠道有一个单独的、更低的上限。日志解析 bug 不会导致数千个 IP 被封。 - **永不封禁的允许列表**,最后检查:Cloudflare IP 段、您的 `csf.allow`、 服务器自身的 IP、RFC1918、您的运维人员 IP(`swatter allow `)、 监控服务,以及**正向确认的**优秀爬虫(Googlebot/Bingbot 通过反向 + 正向 DNS 验证,因为仅靠 PTR 是可以伪造的)。 - **敏感路径需要失败证据。** 访问 `wp-login.php` 不是 罪过 — 您的客户每天都在这样做。暴力破解下限只会在 *重复的失败*尝试(错误或 POST 洪水)时触发,因此登录并在 wp-admin 中工作的网站所有者绝不会因此而被封禁。 - **完全审计 + 申诉。** `swatter why ` 会确切显示触发封禁的原因; `swatter unblock [--perm-allow]` 会在两个层面上同时撤销封禁。 ## 安装 要求:一台 cPanel/Apache + **CSF** 服务器 (AlmaLinux/CentOS/RHEL),`gawk`, `flock`。可选:`jq` + `curl`(威胁情报 & Cloudflare API),`sqlite3` (如果没有,则会回退到普通文件)。 ``` git clone https://github.com/peaceharborco/swatter.git cd swatter # 在服务器本身上(以 root 身份): sudo ./install/install.sh local # …或者通过 SSH 从你的工作站推送: ./install/install.sh remote root@your-server ``` 然后: ``` swatter refresh-feeds # pull Cloudflare ranges + intel feeds swatter test-config # sanity-check deps, paths, CF token swatter scan --dry-run # see what it WOULD do swatter top # review the worst offenders ``` **在您强制执行之前:教会它您是谁。** 在报告模式下运行一周,并 查看它*原本会*封禁哪些内容(`swatter top`、`swatter why `、 每日摘要)。该列表中实际上是您自己、您的员工或 客户的任何 IP,在第一次强制执行之前,都应该被加入永不封禁集合: ``` swatter allow 203.0.113.7 "office" swatter allow 198.51.100.0/24 "client X static range" ``` 任何自动封禁工具最令人尴尬的失败就是封禁客户 — 一个输错密码然后在 wp-admin 中操作的网站所有者,看起来可能非常 像一次攻击。Swatter 的评分机制旨在区分这两者,但您的 运维人员 IP 值得拥有绝对的保证,而不仅仅是一个概率。 当您信任它时,在 `/etc/swatter/swatter.conf` 中设置 `SWATTER_MODE="enforce"`。 安装程序会添加一个 cron 条目,每 5 分钟扫描一次,并每天刷新情报源。 ### 启用 Cloudflare 层面 要在 Cloudflare 处封禁代理攻击者(不仅仅是 CSF 直连攻击者),Swatter 需要为每个 CF 账户提供一个令牌,其权限范围**仅限** `Firewall Services: Edit`(区域 IP Access Rule — 这与 WAF Rulesets 是不同的产品,因此它永远不会与 您现有的 WAF 自动化发生冲突)。它会在攻击者命中的特定区域内封禁他们, 使用的是您的域名→账户映射。 1. 在 Cloudflare 仪表板中,为每个账户创建一个 API 令牌,仅包含针对该账户所在区域的单一权限 *Zone → Firewall Services → Edit*。 2. 构建一个仅限 root 访问的凭证文件(格式为 `accounttoken`,每个账户一行)以及一个 `cf-domains.conf` 风格的域名列表,然后从您的工作站部署这两者 — 不会提交任何密钥,并且只有权限最小的令牌会进入服务器: ./install/swatter-deploy-cf-creds.sh \ --creds /path/to/cloudflare.creds \ --domains /path/to/cf-domains.conf \ --host root@your-server 这会写入 `/etc/swatter/cloudflare.creds` (0600) 和 `/etc/swatter/cf-domains.map` (0644),然后运行 `test-config`。接着,预演模式将会 为代理违规者显示 `cloudflare block in `。(不在 Cloudflare 后面的主机可以完全跳过所有这些步骤 — 默认的 `CF_MODE="auto"` 会检测到裸露直连的机器并作为纯粹的 CSF 自动封禁器运行;设置 `CF_MODE="off"` 可以显式声明这一点并跳过检测。) ### 推荐:使 Cloudflare 分类绝对精确 默认情况下,Swatter 会从您的日志*以及*活跃的源站 socket 中推断是直连还是代理 — 在您的 Web 端口(`DIRECT_WEB_PORTS`,默认为 `80 443`)上的 TCP 对端,如果 不是 Cloudflare 边缘,那就可以证明是直连,这可以捕获绕过 Cloudflare 并带有有效 `Host` 头的正在进行中的洪水攻击。如果要在基于日志的事实判定中也做到精确, 请将 Cloudflare 的 ray ID 添加到您的 Apache 日志格式中 — 存在意味着 请求是通过 Cloudflare 经由代理传入的,不存在则意味着是直连: ``` LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" cfray=%{CF-Ray}i" swatter ``` (这是可选的 — 没有它,启发式算法也能工作。) ### 与您的边缘 WAF 的分工 如果您已经在 Cloudflare 边缘运行了严密的保护 — 比如在 `wp-login.php` 和 `wp-admin` 上设置了 受地理限制的 managed challenge — 想一想这会对 Swatter 在源站看到的流量产生什么影响: **这些路径上的恶意噪音根本就不会到达。** 剩下敲击您登录页面的流量 绝大多数是合法的(网站所有者和通过了您设置的验证的人),因此 源站基于路径的怀疑就会发生反转 — 它开始*挑选出*您的 客户,而不是抵御攻击者。 在这种设置下有两种运行方式: - **`CF_MODE="skip"`** — 最清晰的划分。您的边缘规则拥有代理 领域;Swatter 负责拥有边缘永远看不到的东西:来自 找到了您服务器真实 IP 的扫描器的直连源站流量。在那个层面上,恶意路径表 处于全功率状态,因为没有合法用户会通过您的原始 服务器 IP 登录 WordPress。(在这里请使用 `skip`,而**不是** `off`:这台机器*确实*位于 Cloudflare 之后,所以 分类必须保持开启,否则代理 socket 可能会被 CSF 拒绝。`skip` 保持分类开启,并简单地将经由 CF 的层面交由您的边缘规则处理。) - **保留 CF 层面,但在 `config/badpaths.conf` 中降低认证路径的权重** (例如将 `wp-login.php` 从 HIGH 降为 MEDIUM),如果您的边缘保护是部分性的 — 比如 一个仍然允许同国家机器人进入的地理验证。 无论哪种方式,暴力破解下限都要求有失败的证据(失败的 POST 请求 / 错误响应),因此即使是默认的调试规则,也不会因为网站所有者登录而封禁他们。 ## 命令 | 命令 | 用途 | |---|---| | `swatter scan [--dry-run\|--enforce]` | 摄入 → 评分 → 决策 → 执行 | | `swatter status` | 状态、模式、允许列表健康状况、计数 | | `swatter top [-n N]` | 最严重的跟踪违规者 | | `swatter why ` | 封禁背后的证据和历史 | | `swatter allow [note]` | 添加到永不封禁集合(涵盖两个层面) | | `swatter unblock [--perm-allow]` | 在两个层面上撤销封禁 | | `swatter list [temp\|perm\|cf\|allow]` | 当前的封禁 / 允许列表 | | `swatter report [WINDOW] [--test\|--print]` | 发送按违规和操作分组的摘要邮件 (`--print`: 仅输出到标准输出) | | `swatter refresh-feeds` | 更新 Cloudflare IP 段 + 情报源 | | `swatter test-config` | 验证配置和依赖项 | ## 运作机制 ``` logs ─▶ ingest ─▶ score.awk ─▶ [past WATCH?] ─▶ threat-intel ─▶ classify ─┬▶ CSF (direct) (domlogs, (weighted (per-IP (AbuseIPDB/ (direct └▶ Cloudflare WAF (proxied) access_log) signals + evidence) Spamhaus/ vs CF) decisive IPsum) │ floors) ▼ never-block check (last) + circuit breakers │ ▼ SQLite ledger + decisions.jsonl ``` 增量日志读取使用基于单个文件的字节游标(即使是最繁忙的日志也能精准处理, 在日志轮转中依然存活)。一切都在 `set -uo pipefail` 下运行,通过 `flock` 保证单实例运行, 并使用 awk 一次性完成解析 — 它可以扩展以应对高并发的访问高峰期,而无需为每一行进行 fork 操作。 ## 许可证 MIT © [Peace Harbor Studios](https://studios.peaceharbor.com)。详见 [LICENSE](LICENSE)。 欢迎贡献 — 新的防火墙后端(ipset/nftables)、一个 nginx 日志 解析器,以及额外的情报提供商,都可以无缝接入现有的适配器 接口。
标签:Web安全, 入侵防御, 威胁情报, 密码管理, 应用安全, 开发者工具, 数字取证, 自动化脚本, 蓝队分析, 运维工具, 防火墙策略