Skip to content

trackers: опциональный обход Cloudflare через FlareSolverr - #81

Merged
mdevaev merged 1 commit into
mdevaev:masterfrom
MaksimShakavin:feature/flaresolverr-cloudflare-bypass
Sep 11, 2026
Merged

trackers: опциональный обход Cloudflare через FlareSolverr#81
mdevaev merged 1 commit into
mdevaev:masterfrom
MaksimShakavin:feature/flaresolverr-cloudflare-bypass

Conversation

@MaksimShakavin

Copy link
Copy Markdown

Проблема

Некоторые трекеры (в первую очередь rutracker.org) стали закрывать часть эндпоинтов интерстициалом Cloudflare («Just a moment…»). В частности, rutracker.org/forum/login.php теперь отдаёт HTTP 403 обычному urllib-клиенту emonoda, и каждый запуск падает на tracker.login() с NetworkError(HTTPError: HTTP Error 403: Forbidden). При этом index.php челленджем не закрыт — под защитой только часть эндпоинтов, включая логин.

Решение

Добавлена опциональная поддержка FlareSolverr — сервиса, который решает Cloudflare-челлендж в headless-браузере и возвращает cookie cf_clearance вместе с User-Agent, которым он пользовался.

Появились две новые пер-трекерные опции (в BaseTracker, поэтому доступны всем трекерам):

  • flaresolverr_url — URL инстанса FlareSolverr. По умолчанию "" (выключено).
  • flaresolverr_timeout — максимальное время решения челленджа, по умолчанию 60.0 секунд.

Логика встроена в единственную точку входа HTTP-запросов трекера (__read_url_nofe): лениво, только при получении 403 и только если flaresolverr_url задан. При срабатывании:

  1. запрос отправляется в FlareSolverr (cmd: request.get на /v1);
  2. emonoda подменяет свой User-Agent на тот, что вернул FlareSolverr;
  3. полученные cookie (прежде всего cf_clearance) добавляются в cookie jar через существующий механизм _set_cookie;
  4. исходный запрос повторяется напрямую один раз — Cloudflare его пропускает.

Ключевая деталь: cf_clearance привязан к UA + IP

Выданный Cloudflare cf_clearance действителен только для той пары User-Agent + внешний IP, которой был решён челлендж. Поэтому подмена UA — не деталь, а обязательное условие: без неё повторный прямой запрос снова получит 403. Соответственно, emonoda и FlareSolverr должны иметь один и тот же внешний IP (в моём случае оба сервиса ходят наружу через один NAT-адрес кластера). Это описано в документации к опции.

Обратная совместимость

  • Опция по умолчанию выключена (flaresolverr_url=""), при пустом значении поведение побайтово не меняется.
  • Ноль новых зависимостей: клиент FlareSolverr (emonoda/web/flaresolverr.py) — небольшой JSON-POST на stdlib, переиспользует web.read_url.
  • Реализация ложится в существующую архитектуру: новые опции в BaseTracker.get_options()/init автоматически прокидываются во все трекеры, HTTP идёт через тот же opener/cookie jar.

Изменения не затрагивают trackers/*.json (там только версия и fingerprint), так что make regen-trackers не требуется.

Известное ограничение

test() для проверки fingerprint создаёт отдельный opener без cookie. У rutracker fingerprint-URL — это index.php, который челленджем не закрыт, поэтому на практике в CF он не упирается; при желании обработку 403 можно позже распространить и на этот путь.

Как тестировалось

  • Статический анализ: make tox (flake8 / pylint 10.00/10 / mypy при disallow_untyped_defs / vulture) — проходит. Весь новый код типизирован, GPL-заголовок на месте.
  • Живой Cloudflare: против реального rutracker.org/forum/login.php через FlareSolverr v3.5.0 — челлендж решается (status: ok), возвращаются cf_clearance + bb_guid и UA; повторный прямой запрос отдаёт HTTP 200 с настоящей формой логина.
  • Полный прогон emupdate (--noop --test-mode) против рабочего qBittorrent и rutracker: клиент подключается, logging in … проходит без 403, для всех тестовых раздач скачивается свежий .torrent, Tracker errors: 0.

Some trackers (rutracker.org in particular) now put a Cloudflare
"Just a moment..." interstitial in front of endpoints such as
/forum/login.php, which returns HTTP 403 to the plain urllib client and
kills the run at tracker.login().

Add two opt-in per-tracker options, flaresolverr_url (default "", i.e.
disabled) and flaresolverr_timeout. When flaresolverr_url is set and a
request gets a 403, the request is routed once through a FlareSolverr
instance: emonoda adopts the exact User-Agent FlareSolverr used and
injects the returned cookies (notably cf_clearance) into its cookie jar,
then retries the request directly. The cf_clearance cookie is bound to
UA + egress IP, so adopting FlareSolverr's UA is mandatory and emonoda
must share the same egress IP as FlareSolverr.

The FlareSolverr client is a small stdlib-only JSON POST helper
(emonoda/web/flaresolverr.py), no new third-party dependencies. When
flaresolverr_url is empty the behaviour is byte-for-byte unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
@mdevaev

mdevaev commented Sep 11, 2026

Copy link
Copy Markdown
Owner

Шикарно, спасибо.

@mdevaev
mdevaev merged commit b16cc31 into mdevaev:master Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants