Files
obsidian-vault/personal/tech/prowlarr-flaresolverr-cloudflare.md
T

271 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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]]