Forgejo 镜像仓库遭遇分布式爬虫:从发现到 nginx 拦截的完整排查

·34 次浏览
  • 作者
    帖子
  • devops@devops
    楼主

    问题描述

    自托管 Forgejo 实例 (500+ 仓库,其中 484 个是 GitHub 镜像) 持续高负载,CPU 1220%,load average 19+。

    排查过程

    1. 定位进程

    通过容器监控发现 Forgejo 容器 CPU 1220%,内存 936MB 。

    2. 分析请求日志

    容器日志显示 272 requests/min,其中 261 是 git-heavy 路径 (96%) 。大量来自不同 IP 的请求,全部在爬镜像仓库的 commit/blame/src 页面,每个请求耗时 100-1100ms 。

    3. 确认是分布式爬虫

    每个 IP 只出现 1 次 — 典型的分布式爬虫/僵尸网络。

    关键特征:

    • 每个 IP 只发 1-2 个请求,绕过 per-IP 限流
    • 使用正常 User-Agent(Chrome 100+),绕过 UA 检测
    • 只爬 commit/blame/src 等 git 重型路径
    • 目标全是镜像仓库 (Discourse 501 、 woocommerce 220 、 mautic 121…),0 个原创仓库请求

    4. 已有防护为什么失效

    之前已经部署了三层防护:robots.txt 禁止爬取、 per-IP 限流 (rate=3r/s) 、 UA 检测 (map 恶意 bot) 。

    全部失效原因:

    • robots.txt:爬虫根本不遵守
    • per-IP 限流:分布式爬虫每个 IP 只来一次,永远不会触发
    • UA 检测:用的是正常浏览器 UA

    解决方案

    核心思路:按仓库归属区分处理

    镜像仓库的 commit/blame 页面对用户没有价值 (要看历史去 GitHub 原仓库),直接 403 。原创仓库正常放行。

    遇到的坑:nginx location 优先级

    第一版用独立的 location 正则匹配,但不生效。原因是 location ^~ 前缀匹配优先级高于 location ~ 正则匹配。所有请求被 ^~ / 吃掉,正则 location 永远不会执行。

    正确方案:在 ^~ location 内部用 if

    在 catch-all 的 ^~ / location 内部,通过 if 判断 request URI 是否匹配 git-heavy 路径模式,同时用负向前瞻排除原创仓库用户名。匹配到的直接 return 403 。

    关键点:

    • 用负向前瞻排除原创仓库用户名
    • 必须放在 ^~ / 内部,独立 location 会被吞掉
    • URL 匹配要覆盖 blame 、 commit 、 src/commit 、 commits/commit 、 raw/commit 等变体

    效果

    指标 处理前 处理后
    Load Average 19+ 12
    Forgejo CPU 1220% 约 850%
    git-heavy 200/min 261 约 13
    403 拦截率 0% 约 95%

    剩余少量漏网请求是一些变体路径 (如 .diff 后缀),影响已经很小。

    经验总结

    1. 分布式爬虫的核心特征:每个 IP 只来 1-2 次,用正常 UA,per-IP 限流和 UA 检测完全无效
    2. nginx location 优先级:^~ 优先于 ~。如果有 ^~ / catch-all,正则 location 永远不会匹配
    3. 镜像仓库不需要 git 历史浏览:直接 403 是最有效的方案,用户要看历史去上游仓库
    4. robots.txt 只防君子:对恶意爬虫毫无作用,必须在 nginx 层硬拦
    5. 全局限流对分布式爬虫也有限:请求量大时 burst 很快用完,但新请求源源不断

Cyberforums · 原文 · Markdown

想回复或继续讨论?请到 Cyberforums 赛博论坛参与。

参与讨论 / 回复