271 lines
18 KiB
Markdown
Executable File
271 lines
18 KiB
Markdown
Executable File
---
|
||
title: Prowlarr FlareSolverr — Cloudflare-обход и починка индексеров
|
||
created: '2026-09-20'
|
||
updated: '2026-09-24'
|
||
type: tech
|
||
namespace: personal
|
||
tags: [prowlarr, flaresolverr, cloudflare, rutracker, kinozal, torrent, arr, kraken, infra]
|
||
related:
|
||
- "[[family/how-to/arr-stack-kraken]]"
|
||
- "[[personal/projects/media-pipeline-pitfalls]]"
|
||
---
|
||
|
||
# Prowlarr + FlareSolverr — обход Cloudflare
|
||
|
||
Зеркало ключевых питфоллов скилла `torrent-agent-pipeline` (§ Питфоллы).
|
||
Контекст: 2026-09-20, Kraken. RuTracker / Kinozal / Kinozal (M) были «красными» в Prowlarr.
|
||
|
||
## Схема
|
||
|
||
```
|
||
Prowlarr (Kraken:9696) → FlareSolverr (Kraken:8191) → Cloudflare-защищённый трекер
|
||
⚠️ прокси применяется ТОЛЬКО при одинаковом tag на прокси И на индексере
|
||
```
|
||
|
||
Все контейнеры — в общей внешней сети `media_net`: radarr, sonarr, prowlarr, transmission, flaresolverr.
|
||
Проверять фактическую сеть, а не compose-файл:
|
||
```bash
|
||
ssh kraken 'sudo docker inspect -f "{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}" radarr sonarr prowlarr transmission flaresolverr'
|
||
```
|
||
|
||
## 🔴 Главная засада: прокси без tag молча не применяется
|
||
|
||
Созданный прокси (`POST /api/v1/indexerproxy`) с `tags: []` **не используется вообще** — в UI и логе Prowlarr об этом ни слова, запросы продолжают идти напрямую в Cloudflare. Прокси применяется к индексерам только когда у прокси И у каждого индексера стоит **один и тот же tag id**.
|
||
|
||
```bash
|
||
KEY=$(grep -oP "(?<=<ApiKey>)[^<]+" /srv/.../docker/prowlarr/config/config.xml)
|
||
|
||
# 1. создать тег → получить id (обычно 1)
|
||
curl -X POST -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
|
||
-d '{"label":"flaresolverr"}' http://localhost:9696/prowlarr/api/v1/tag
|
||
|
||
# 2. повесить tag на ПРОКСИ: GET целиком → jq .tags=[1] → PUT
|
||
curl -s -H "X-Api-Key: $KEY" http://localhost:9696/prowlarr/api/v1/indexerproxy/1 | jq ".tags=[1]" > /tmp/p.json
|
||
curl -X PUT -H "X-Api-Key: $KEY" -H "Content-Type: application/json" -d @/tmp/p.json \
|
||
http://localhost:9696/prowlarr/api/v1/indexerproxy/1
|
||
|
||
# 3. повесить ТОТ ЖЕ tag на индексера (RuTracker=1, Kinozal=5, Kinozal M=6)
|
||
curl -s -H "X-Api-Key: $KEY" http://localhost:9696/prowlarr/api/v1/indexer/1 | jq ".tags=[1]" > /tmp/i.json
|
||
curl -X PUT -H "X-Api-Key: $KEY" -H "Content-Type: application/json" -d @/tmp/i.json \
|
||
http://localhost:9696/prowlarr/api/v1/indexer/1
|
||
```
|
||
|
||
**Пруф работы — только лог FlareSolverr, не лог и не UI Prowlarr:**
|
||
```bash
|
||
ssh kraken 'sudo docker logs --tail 30 flaresolverr'
|
||
# → Challenge detected. Title found: Just a moment...
|
||
# → Challenge solved!
|
||
```
|
||
Непустой `GET /api/v1/indexerproxy` **не означает**, что прокси работает.
|
||
Требуется Prowlarr ≥ 2.6 (в 2.5.2 прокси-модель только вводилась).
|
||
|
||
## 🔴 Блокировка индексера в SQLite — рестарт НЕ снимает
|
||
|
||
Prowlarr сам выключает упавший индексер на 24ч (`Temporarily ignoring indexer X till +24h`), состояние в таблице `IndexerStatus` в `prowlarr.db`. `docker restart prowlarr` его **не сбрасывает** — запрос продолжает отдавать `429 Indexer is disabled till ...`.
|
||
|
||
```bash
|
||
# бэкап перед правкой БД
|
||
cp /srv/.../docker/prowlarr/config/prowlarr.db <backup>/
|
||
ssh kraken 'sudo docker stop prowlarr'
|
||
sqlite3 /srv/.../docker/prowlarr/config/prowlarr.db "DELETE FROM IndexerStatus WHERE ProviderId IN (1,5,6);"
|
||
ssh kraken 'sudo docker start prowlarr'
|
||
```
|
||
`ProviderId` = id индексера. **В контейнерах `sqlite3` НЕТ** — на хосте Kraken `/usr/bin/sqlite3` ЕСТЬ.
|
||
|
||
## ⚠️ Безопасность: FlareSolverr логирует пароли в plaintext
|
||
|
||
На `LOG_LEVEL=info` его лог печатает `postData` запроса, т.е. `login_username=...&login_password=...` **открытым текстом**. Ставить `LOG_LEVEL=warning` в compose.
|
||
Проверка после понижения: `docker logs --since 5m flaresolverr | grep -c login_password` → `0`.
|
||
|
||
```yaml
|
||
flaresolverr:
|
||
image: ghcr.io/flaresolverr/flaresolverr:latest
|
||
environment:
|
||
- LOG_LEVEL=warning # ← не info: info пишет пароли трекеров
|
||
- BROWSER_TIMEOUT=60000
|
||
ports:
|
||
- 8191:8191
|
||
networks:
|
||
- media_net
|
||
```
|
||
|
||
## RuTor не фильтрует по категориям
|
||
|
||
В caps заявлены `2000(Movies)/5000(TV)/8000(Other)`, но с `cat=5000` отдаёт **0** результатов, без `cat` — 100. Prowlarr сам пишет в полях индексера: *«RuTor does not return categories in its search results. To sync to your apps, include 8000(Other)»*. В Sonarr/Radarr ставить категорию RuTor = `8000`, не `5000`.
|
||
|
||
## 429 в Sonarr/Radarr от упавшего индексера
|
||
|
||
`HTTP request failed: [429:TooManyRequests] [GET] at [http://prowlarr:9696/prowlarr/N/api?t=caps...]` — это **не** rate-limit трекера, а самоисключение Prowlarr. Не «ждать и повторять» — снимать `IndexerStatus`.
|
||
|
||
## Диагностические грабли
|
||
|
||
- **`POST /api/v3/indexer/test` с `{"indexerId": N}` не работает** (Radarr v6.1.1, Sonarr, Prowlarr v2.5.2) → HTTP 400 с `validationFailures` (`'Name' must not be empty`, … `'Priority' must be between 1 and 50`). Эндпоинт валидирует тело как **новый** индексер. Ответ — массив, поэтому `jq '.validationFailures'` падает с `Cannot index array with string`.
|
||
Рабочая альтернатива — `testall`:
|
||
```bash
|
||
curl -s -X POST -H "X-Api-Key: $KEY" "http://localhost:7878/radarr/api/v3/indexer/testall" \
|
||
| jq -r '.[] | "\(.id)\t\(.isValid)\t\(.validationFailures[0].errorMessage // "")"'
|
||
# Sonarr: порт 8989, UrlBase /sonarr
|
||
```
|
||
- **Prowlarr маскирует упавшие индексеры** — `GET /api/v1/health` и `/api/v1/indexerstatus` могут быть **пустыми**, пока индексер красный. Диагноз снимать из лога `<config>/logs/prowlarr.debug.txt` (НЕ `logs/prowlarr.txt`). Искать: `grep -iE "rutracker|kinozal|Cloudflare|429"`.
|
||
- **«Кто живой» быстрее всего — поиском**, не caps-тестом:
|
||
```bash
|
||
curl -s -H "X-Api-Key: $KEY" "http://localhost:9696/prowlarr/api/v1/search?query=matrix&type=search" \
|
||
| jq -r 'group_by(.indexer)[] | "\(.[0].indexer): \(length)"'
|
||
```
|
||
По умолчанию до 100 результатов на индексер — «156 всего» ≠ «по 26 на каждый».
|
||
|
||
## 🔴 2026-09-24: FlareSolverr в crash-loop — Chrome не поднимается под chromedriver
|
||
|
||
**Повод:** Alex — «Мастерская сатаны: в торрентах ничего не нашлось, по каким трекерам
|
||
ищет и можно ли влиять через Prowlarr?» Симптом оказался двухслойным: сверху пустая
|
||
выдача PidTor, под ней — мёртвый FlareSolverr.
|
||
|
||
### Слой 1 (верхний): PidTor отдаёт 503 из кэша, jac.red — 0 результатов
|
||
|
||
```
|
||
GET /lite/pidtor?title=Мастерская сатаны&year=2025
|
||
→ HTTP 503, Content-Length: 0, X-Invoke-Cache: HIT, t=0.007s ← кэш
|
||
|
||
GET http://jac.red/api/v2.0/indexers/all/results?title=Мастерская сатаны&year=2025
|
||
→ HTTP 200, {"Results":[],"jacred":true} ← ЧЕСТНЫЙ ноль
|
||
GET …/results?title=Матрица&year=1999
|
||
→ HTTP 200, 132 раздачи ← контроль: живой
|
||
```
|
||
|
||
**jac.red знает 25 индексеров:** `anibelka anidub anifilm aniliberty animelayer anistar
|
||
baibako bitru kinozal korsars leproduction lostfilm mazepa megapeer nnmclub rudub rutor
|
||
rutracker selezen subsplease toloka torrentby ultradox viruseproject`.
|
||
|
||
🔴 **Список индексеров jac.red:** `GET http://jac.red/api/v2.0/indexers?configured=true`
|
||
→ 200, ~4 КБ, массив с `name`. Рабочий способ посмотреть, по каким трекерам реально
|
||
идёт поиск — не догадываться.
|
||
|
||
🔴 **jac.red отдаёт 429 при частых запросах** (nginx): пауза `sleep 2-3` между вызовами
|
||
обязательна. Формулировка ответа: nginx-страница `429 Too Many Requests`, НЕ JSON.
|
||
|
||
🔴 **`title=` должен быть URL-кодирован целиком.** `%20` в значении даёт **HTTP 400** —
|
||
использовать `curl -G --data-urlencode "title=Мастерская сатаны"`. Ручная сборка
|
||
query-строки с `%20` в середине ломается.
|
||
|
||
**Вывод по «Мастерская сатаны»:** у фильма реально нет раздач на 25 трекерах jac.red.
|
||
Механизм PidTor исправен (контроль на «Матрице» — 132 раздачи). Диагноз «сломан поиск»
|
||
был бы неверным.
|
||
|
||
⚠️ Одноимённый фильм 2022 года лежит там как **«Мастерская дьявола / Devil's Workshop»**
|
||
(`Devils Workshop` → 2 раздачи, bitru + kinozal, sid 0-1). Латиница в выдаче — не тот
|
||
фильм, следить за годом.
|
||
|
||
### Слой 2 (первопричина): FlareSolverr crash-loop ⇒ 3 индексера Prowlarr мертвы
|
||
|
||
```
|
||
RuTracker (id=1) disabledTill = 2026-09-25T10:17:51Z
|
||
Kinozal (id=5) disabledTill = 2026-09-25T10:17:50Z
|
||
Kinozal (M) (id=6) disabledTill = 2026-09-25T10:17:48Z
|
||
EscalationLevel = 9 (максимум — блокировка на 24 ч)
|
||
```
|
||
|
||
Проверка: `GET /prowlarr/1/api?t=search&q=…` → `429 Indexer is disabled till …`.
|
||
Прямой результат несмотря на то, что таблица `IndexerStatus` начиналась 20-го (начальные
|
||
падения) и докатилась до `EscalationLevel=9` именно **24-го в 10:17**.
|
||
|
||
**В логе FlareSolverr:**
|
||
|
||
```
|
||
Error starting Chrome: Message: session not created: cannot connect to chrome at
|
||
127.0.0.1:48249 from chrome not reachable
|
||
Exception: Error getting browser User-Agent. …
|
||
File "/app/flaresolverr.py", line 136, in <module>
|
||
flaresolverr_service.test_browser_installation()
|
||
```
|
||
|
||
Контейнер `flaresolverr` уходит в **crash-loop** (`Restarting (1)`), HTTP `:8191` не
|
||
отвечает (`Connection reset by peer`). Успешных решений челленджа в логе (`Challenge
|
||
solved`) нет — считать их число: `sudo docker logs flaresolverr 2>&1 | grep -c "Challenge solved"`.
|
||
|
||
### Что проверено и исключено (не тратить время повторно)
|
||
|
||
| Гипотеза | Проверка | Итог |
|
||
|---|---|---|
|
||
| Образ обновлён watchtower'ом и сломан | `image inspect` → v3.5.2, `Created=2026-09-12`; в логах watchtower нет строк про flaresolverr | ❌ не при чём |
|
||
| Нехватка RAM | `free`, суммарный RSS всех процессов = 1.9 GB при MemAvailable ~1.8 GB | ⚠️ фон, не корень |
|
||
| **swap мёртв** | `/proc/swaps` **пуст** при живом `/var/swap` (512 MB); `SwapFree` падал до 240 kB | ✅ **реальный баг хоста**, починен |
|
||
| IPv6: `/etc/hosts` отдаёт `::1 localhost` первым, Chrome на IPv6-петле | в контейнере `/etc/hosts` → `::1 localhost` первый; **добавлен `net.ipv6.conf.all.disable_ipv6=1`** (как у prowlarr) → `disable_ipv6=1`, hosts стал `127.0.0.1 localhost` | ❌ **НЕ помогло**, ошибка дословно та же |
|
||
| `/dev/shm` мал (64 MB) | `docker inspect` → `ShmSize=67108864`; в контейнере `df -h /dev/shm` → 64M | ⚠️ **подозреваемый №1 из оставшихся** |
|
||
|
||
🔴 **Chrome запускается вручную, но не под chromedriver.** Проверено:
|
||
`docker exec flaresolverr sh -c '/usr/lib/chromium/chromium --headless=new --no-sandbox
|
||
--disable-dev-shm-usage --disable-gpu --dump-dom about:blank'` → работает, отдаёт
|
||
verbose-лог компонентов; под управлением selenium — `cannot connect to chrome at 127.0.0.1:<port>`.
|
||
В образе **`chromedriver` не в PATH** (`which chromedriver` → not found) — selenium ≥ 4
|
||
использует собственный Selenium Manager.
|
||
|
||
🔴 **Вывод не делать по одной гипотезе.** IPv6 выглядел убедительно (симптом
|
||
«не видит chrome на 127.0.0.1» идеально ложится), но после применения ошибка
|
||
воспроизвелась дословно — то есть причина не в резолве адреса.
|
||
|
||
### ✅ Починено в этой сессии
|
||
|
||
**1. swap был мёртв — восстановлен.** `/proc/swaps` пуст, а `/var/swap` (512 MB) на диске
|
||
есть, ядро держало `SwapFree=256 kB` при нулевом активном swap. Без swap Chrome не
|
||
получает память при пике старта.
|
||
|
||
```bash
|
||
sudo swapon /var/swap # → /proc/swaps: /var/swap file 524272 -2
|
||
cat /proc/swaps # проверка
|
||
grep -E "^(SwapTotal|SwapFree)" /proc/meminfo
|
||
```
|
||
|
||
⚠️ **`swapoff -a && swapon -a` НЕ восстанавливает** — даёт `SwapFree: 0 kB`. Помогает
|
||
только явный `sudo swapon /var/swap`. В `/etc/fstab` строки для swap НЕТ
|
||
(комментарий `dphys-swapfile`), systemd swap-unit не поднят (`systemctl list-units
|
||
--type=swap --all` → 0 units). Автоподъём swap на этом хосте **не настроен** — после
|
||
ребута swap снова может пропасть.
|
||
|
||
**2. `disable_ipv6` добавлен сервису `flaresolverr`** (в `docker/prowlarr/docker-compose.yml`,
|
||
бэкап `docker-compose.yml.bak-20260924-193632`). **Эффекта не дало** — оставлено как
|
||
проверенная-неудачная попытка; при откате вернуть файл из бэкапа.
|
||
|
||
```yaml
|
||
flaresolverr:
|
||
image: ghcr.io/flaresolverr/flaresolverr:latest
|
||
environment:
|
||
- LOG_LEVEL=warning # ← не info: info пишет пароли трекеров
|
||
- BROWSER_TIMEOUT=60000
|
||
ports:
|
||
- 8191:8191
|
||
networks:
|
||
- media_net
|
||
sysctls:
|
||
- net.ipv6.conf.all.disable_ipv6=1 # 2026-09-24: не помогло, см. выше
|
||
shm_size: 1gb # ⏳ НЕ ПРИМЕНЕНО — кандидат на фикс
|
||
```
|
||
|
||
⏳ **Незавершённое:** добавить `shm_size: 1gb` и пересоздать
|
||
(`docker compose up -d --force-recreate flaresolverr`), затем снять блокировки
|
||
`IndexerStatus` для id 1/5/6 (см. секцию выше) и проверить
|
||
`POST http://localhost:8191/v1 {"cmd":"request.get","url":"https://kinozal.me/browse.php?c=0"}`
|
||
→ ожидается `status: ok`. После этого — поиск «Мастерская сатаны» по всем 6 трекерам.
|
||
|
||
### Дополнительные питфоллы сессии
|
||
|
||
1. 🔴 **`escape` из контейнера в crash-loop:** `docker exec` отдаёт
|
||
`OCI runtime exec failed: exec failed: unable to start container process` /
|
||
`cannot exec in a stopped container`. Это признак рестарт-петли, а не «неверный exec» —
|
||
сначала `docker ps -a --filter name=<c> --format "{{.Status}} R={{.RestartCount}}"`.
|
||
2. 🔴 **`sudo docker stats` показывает `0B / 0B` у всех контейнеров.** Метрика врёт
|
||
(недоступна на этом хосте) — считать память по контейнерам через
|
||
`/sys/fs/cgroup/system.slice/docker-<id>.scope/memory.current`, а по процессам — `ps -eo rss`.
|
||
3. 🔴 **`ps aux --sort=-rss | awk` с `%4`/`$6` даёт мусор** — формат `ps aux` на этом
|
||
хосте отдаёт 11 полей, `%MEM` и RSS нужно читать через `ps -eo user,pid,rss,comm,args`.
|
||
4. 🔴 **Долгие ssh-команды в crash-loop контейнера вешают терминал на 400+ с** — резать
|
||
диагностику на короткие скрипты и не ждать готовности контейнера дольше 240 с.
|
||
5. ⚠️ **`sudo` на Кра́кене требует TTY** — `ssh -t kraken '…'`. Без `-t`:
|
||
`sudo: a password is required`.
|
||
|
||
## Источники
|
||
|
||
- Скилл: `torrent-agent-pipeline` (§ Питфоллы), `lampac-kraken-deploy`
|
||
- Живой разбор: [[family/how-to/arr-stack-kraken]] — секции про починку индексера 2026-09-20
|
||
- Слой PidTor/кэш: [[personal/tech/lampac-torrserver]] §«2026-09-24»
|
||
- Каталог/конфиг lampac: [[personal/tech/lampac-deployment]]
|