314 lines
20 KiB
Markdown
Executable File
314 lines
20 KiB
Markdown
Executable File
# FlareSolverr на Тайге + обход блокировок индексеров
|
||
|
||
> ✅ **СОСТОЯНИЕ 2026-09-29: РАБОТАЕТ.** Prowlarr логинится на RuTracker автоматически,
|
||
> поиск отдаёт CF-индексеры. `IndexerStatus` → `[]`, `health` → `[]`, 6/6 индексеров enabled.
|
||
> **Приёмочный замер: `search?query=radiohead&indexerIds=1` → 50 результатов** (RuTracker).
|
||
> FS — штатный образ `ghcr.io/flaresolverr/flaresolverr:latest`, порт `8191`, `media_net`,
|
||
> `shm_size: 1gb`, `PROXY_URL=socks5://vless-proxy:1080`.
|
||
> Compose — `/mnt/RED_2TB/docker/arr/docker-compose.yml` (сервис `flaresolverr`).
|
||
> ⚠️ Открыто: **RuTor** — `no results in the configured categories` (на стороне трекера, не блок).
|
||
> Связка: [[personal/plans/flaresolverr-taiga-deploy]], [[family/projects/media-toolbox-taiga]],
|
||
> [[personal/tech/prowlarr-flaresolverr-cloudflare]], [[family/how-to/arr-stack-kraken]],
|
||
> [[family/plans/lidarr-taiga-deploy]].
|
||
|
||
## 🔴 ИСТИННАЯ ПРИЧИНА «RuTracker не логинится» (найдена 2026-09-29)
|
||
|
||
Три дня диагностики уходили в ложные следы. Настоящая причина — **банальная**:
|
||
|
||
```
|
||
Prowlarr хранит: username = mallexxx (8 символов)
|
||
password = m@n*w$uperP@$$w0rD (18 символов)
|
||
Мои тесты шли с: username = keropjan_88 / I70aj7Igpr ← ДРУГОЙ аккаунт
|
||
```
|
||
|
||
**Капча RuTracker — не блокер, а СИМПТОМ неверного пароля.** Трекер отдаёт форму
|
||
`Вы ввели неверное/неактивное имя пользователя или неверный пароль` + картинку `cap_code`.
|
||
С верной парой логин проходит **без капчи вообще**, автоматически.
|
||
|
||
Проверка (рабочий путь):
|
||
```
|
||
POST /forum/login.php session=<uuid>
|
||
login_username=mallexxx&login_password=<пароль>&login=<Вход>
|
||
→ len=105 510, cookies = [bb_session, bb_ssl, bb_guid, cf_clearance]
|
||
→ bb_session PRESENT, «неверное/неактивное» = 0
|
||
```
|
||
|
||
### 🔴 Кука авторизации RuTracker — `bb_session`, НЕ `bb_data`
|
||
|
||
В этом доке раньше фигурировала `bb_data` как «кука авторизации». **Это неверно.**
|
||
Признак живого логина — наличие **`bb_session`** в `.solution.cookies[]`.
|
||
Без неё Prowlarr пишет `Empty cookies returned by FlareSolverr` (`FlareSolverr.cs:68`).
|
||
|
||
### 🔴 Порядок диагностики «Auth Failed» / «Empty cookies returned» (НЕ переставлять)
|
||
|
||
1. **Сверить пару логин/пароль из БД Prowlarr** с тем, чем тестируешь:
|
||
```bash
|
||
DB=/mnt/RED_2TB/docker/arr/prowlarr/prowlarr.db
|
||
sqlite3 "file:$DB?mode=ro" "SELECT Settings FROM Indexers WHERE Id=1;" # JSON: username/password
|
||
```
|
||
**Это первое, что нужно делать при `Auth Failed`.** Здесь потеряно три дня.
|
||
2. Проверить, что FS-сессия ходит через прокси (см. ниже).
|
||
3. Только потом — карантин в `IndexerStatus` (см. ниже).
|
||
4. И только если п.1–3 чисты — думать про пароль на трекере.
|
||
|
||
## 🔑 ОТКРЫТИЕ: FS-СЕССИЯ НЕ НАСЛЕДУЕТ `PROXY_URL`
|
||
|
||
`PROXY_URL` (глобальный прокси FS) применяется к Chrome **в момент создания сессии**.
|
||
Сессия, созданная **до** появления `PROXY_URL`, остаётся **без прокси** навсегда —
|
||
Chrome получает `net::ERR_NAME_NOT_RESOLVED` (DNS-блок провайдера), Prowlarr
|
||
интерпретирует это как «blocked by CloudFlare Protection» и уходит в retry → карантин.
|
||
|
||
Пруф (сессия создана до `PROXY_URL`):
|
||
```
|
||
request.get kinozal + session:"prowlarr"
|
||
→ {"status":"error","message":"Error solving the challenge.
|
||
Message: unknown error: net::ERR_NAME_NOT_RESOLVED (chrome=152.0.7977.82)"}
|
||
```
|
||
Тот же запрос с сессией, созданной **после** `PROXY_URL` → `Challenge solved! 200`.
|
||
|
||
Подтверждение, что прокси применяется (нужен `LOG_LEVEL=debug`):
|
||
```
|
||
INFO Using proxy URL ENV
|
||
DEBUG Using webdriver proxy: socks5://vless-proxy:1080
|
||
```
|
||
|
||
**Имя сессии Prowlarr — `prowlarr`** (видно через `sessions.list`).
|
||
|
||
**Фикс:** удалить устаревшую сессию — Prowlarr пересоздаёт её сам при следующем запросе,
|
||
уже с рабочим `PROXY_URL`:
|
||
```bash
|
||
curl -s -X POST http://localhost:8191/v1 -H "Content-Type: application/json" \
|
||
-d '{"cmd":"sessions.destroy","session":"prowlarr"}'
|
||
```
|
||
|
||
> 🔴 **Правило:** после ЛЮБОЙ правки `PROXY_URL` в compose — **убить сессию `prowlarr`**.
|
||
> При обычном рестарте контейнера проблема не повторяется (сессия создастся заново).
|
||
|
||
## 🔑 ВТОРОЕ: FlareSolverr сам обязан ходить через прокси (`PROXY_URL`)
|
||
|
||
На Тайге **два блока одновременно**: DNS-блок провайдера (NXDOMAIN на 4 трекера) + Cloudflare.
|
||
FS без прокси сам получит NXDOMAIN и не поможет. Решение — env **`PROXY_URL=socks5://vless-proxy:1080`**.
|
||
|
||
> ⚠️ Имя переменной — **`PROXY_URL`**, НЕ `HTTP_PROXY`/`HTTPS_PROXY`.
|
||
> Схема обязательна (`socks5://host:port`). Приоритет: request > session > global.
|
||
> `PROXY_USERNAME`/`PROXY_PASSWORD` не нужны — `vless-proxy` без авторизации.
|
||
|
||
## 🔴 ПИТФОЛЛ: `IndexerStatus` снимается ТОЛЬКО SQL, тест его ПРОДЛЕВАЕТ
|
||
|
||
Симптом-петля: «снимаю карантин тестом → warning возвращается → тест падает».
|
||
|
||
```
|
||
ProviderId Индексер EscalationLevel DisabledTill
|
||
1 RuTracker 9 2026-09-29 09:46 ← СУТКИ
|
||
5 Kinozal 2 2026-09-28 10:08
|
||
6 Kinozal M 2 —
|
||
```
|
||
|
||
`EscalationLevel` 0–9 (`9` — максимум, `DisabledTill` = +24 ч). Пока запись есть:
|
||
`caps` → **429** → фоновый health-чек Radarr/Sonarr/Lidarr бьёт в 429 → **warning каскадом
|
||
во все четыре сервиса**. Отсюда парадокс «health Radarr пуст, а warning про индексе́ры живёт» —
|
||
он живёт не у Radarr, а в БД Prowlarr.
|
||
|
||
🔴 **`POST /indexer/test` карантин НЕ снимает, а ПРОДЛЕВАЕТ** (наблюдалось `15:44` → `21:45`).
|
||
|
||
### Рабочая процедура снятия
|
||
|
||
```bash
|
||
DB=/mnt/RED_2TB/docker/arr/prowlarr/prowlarr.db
|
||
# 1. БЭКАП через .backup, НЕ cp (БД в WAL-режиме)
|
||
sqlite3 "file:$DB?mode=ro" ".backup /home/truenas_admin/prowlarr.db.bak-$(date +%Y%m%d-%H%M%S)"
|
||
# 2. стоп → удалить строки → старт
|
||
docker stop prowlarr
|
||
sqlite3 "$DB" "DELETE FROM IndexerStatus;"
|
||
sqlite3 "$DB" "PRAGMA quick_check;" # → ok
|
||
docker start prowlarr
|
||
# 3. приёмка
|
||
curl -s -H "X-Api-Key: $PK" http://localhost:9696/api/v1/indexerstatus # → []
|
||
```
|
||
|
||
Таблицы: `Indexers`, `IndexerStatus`, `IndexerProxies`, `ApplicationIndexerMapping`.
|
||
|
||
## 🔴 ПРИЁМКА: `search`, а НЕ `caps` и не `health`
|
||
|
||
Пустой ответ трекера выглядит как успех инфраструктуры. `caps` 200 + пустой `IndexerStatus`
|
||
+ `health: []` **не доказывают**, что поиск работает.
|
||
|
||
```bash
|
||
curl -s -m 240 -H "X-Api-Key: $PK" \
|
||
"http://localhost:9696/api/v1/search?query=radiohead&type=search&indexerIds=1" \
|
||
| python3 -c "import json,sys; print(len(json.load(sys.stdin)))"
|
||
# ожидается: 50
|
||
```
|
||
|
||
## ⚠️ ПИТФОЛЛ: `requestTimeout` = 60 с мало, нужно 180
|
||
|
||
Дефолт прокси-элемента FlareSolverr в Prowlarr = 60 с. Решение CF-челленджа занимает
|
||
**78 с** холодный / **20 с** прогретый, POST-логин — **97 с** → `Error solving the challenge.
|
||
Timeout after 60.0 seconds`. Фикс: `requestTimeout` = **180**
|
||
(`PUT /api/v1/indexerproxy/2`).
|
||
|
||
## ⚠️ ПИТФОЛЛ: тег нужен И на прокси, И на индексере — и на ОБА прокси
|
||
|
||
| Прокси | id | Тег | Назначение |
|
||
|---|---|---|---|
|
||
| `vless-socks5` | 1 | `proxy` (id=1) | обход **DNS-блока** провайдера |
|
||
| `flaresolverr` | 2 | `flaresolverr` (id=2) | обход **Cloudflare** |
|
||
|
||
На CF-индексер вешаются **оба** тега: `tags=[1,2]`. Фактически: id 1, 4, 5, 6 → `[1,2]`;
|
||
id 2, 3 → `[1]` (RuTor/Byrutor живут на одном прокси).
|
||
|
||
## ⚠️ ПИТФОЛЛ: PUT `/api/v1/indexer/N` → 400, нужен `?forceSave=true`
|
||
|
||
Prowlarr при PUT сразу тестирует индексер; если FS не прошёл — `400` с `validationFailures`,
|
||
теги откатываются. **Рабочий путь:** `PUT /api/v1/indexer/N?forceSave=true` → **202**.
|
||
То же для `/indexerproxy/N`.
|
||
|
||
## ⚠️ ПИТФОЛЛ: `test` требует ПОЛНЫЙ объект, не `{id}` → 400
|
||
|
||
Во **всех** arr-сервисах `POST /api/vN/indexer/test` с телом `{"id":N}` → **400**.
|
||
Правильно — полный объект индексера (`GET` → `--data-binary @file`). `{}` + **200** = успех.
|
||
`POST /indexer/testall` в Radarr/Sonarr **не работает** (400, Sonarr виснет >300 с).
|
||
`GET /indexerstatus` есть **только** в Prowlarr (в остальных 404).
|
||
|
||
## ⚠️ ПИТФОЛЛ: параллельный `testall` устраивает гонку за Chrome
|
||
|
||
У FlareSolverr **один** Chrome. При `testall` челленджи сыпятся таймаутами; по одному — проходят.
|
||
**Не верить результату параллельного `testall`.** `curl` без `--max-time` виснет — всегда ставить лимит.
|
||
|
||
## ⚠️ ПИТФОЛЛ: `429` на `caps` = запись в `IndexerStatus` → каскад во все arr
|
||
|
||
`GET http://prowlarr:9696/5/api?t=caps&apikey=…` → **429**, пока индексер в карантине.
|
||
Radarr/Sonarr/Lidarr не могут синкнуть индексер → `Unable to connect to indexer [429]`.
|
||
**Порядок лечения (снизу вверх):** снять карантин в Prowlarr через SQL → проверить
|
||
`indexerstatus` → `PUT /api/v3/indexer/N?forceSave=true` в самом arr (сброс `FailureCount`)
|
||
→ `docker restart radarr sonarr lidarr` (health пересчитывается только при старте).
|
||
|
||
## ⚠️ ПИТФОЛЛ: `proxyBypassFilter` Lidarr без внутренних имён → таймауты к Prowlarr
|
||
|
||
Дефолт `localhost,127.0.0.1,vless-proxy` **не содержит `prowlarr`** → внутренний запрос
|
||
Lidarr → Prowlarr уходит в прокси и падает `Http request timed out`, хотя Prowlarr отвечает 200.
|
||
`proxyBypassLocalAddresses=true` на docker-имена **не действует** (только IP-литералы).
|
||
|
||
**Фикс** — дописать в `config/host.proxyBypassFilter` все docker-имена стека:
|
||
```
|
||
localhost,127.0.0.1,vless-proxy,prowlarr,lidarr,radarr,sonarr,transmission,jellyfin,navidrome,caddy,gluetun,taiga
|
||
```
|
||
После правки — `docker restart lidarr`.
|
||
|
||
> ⚠️ **Правило:** включая приложению прокси-конфиг, сразу прописать в bypass все имена своего стека.
|
||
|
||
## Общий вывод: три РАЗНЫХ механизма прокси на одном хосте
|
||
|
||
| Кто | Механизм | Ключ |
|
||
|---|---|---|
|
||
| Prowlarr (индексеры) | SOCKS5-прокси в Prowlarr + FlareSolverr | теги `[1,2]`, `PROXY_URL` у FS |
|
||
| FlareSolverr (Chrome) | глобальный прокси образа | env `PROXY_URL=socks5://...` (**НЕ** HTTP_PROXY) |
|
||
| Lidarr (SkyHook) | встроенный конфиг приложения | `config/host.proxyEnabled` (**env НЕ работает**) |
|
||
|
||
⚠️ **Не переносить рецепт одного сервиса на другой.** Проверять фактическим запросом.
|
||
|
||
## Compose-сервис (штатный, `/mnt/RED_2TB/docker/arr/docker-compose.yml`)
|
||
|
||
```yaml
|
||
flaresolverr:
|
||
image: ghcr.io/flaresolverr/flaresolverr:latest
|
||
container_name: flaresolverr
|
||
restart: unless-stopped
|
||
environment:
|
||
- LOG_LEVEL=warning # НЕ info — info пишет пароли трекеров в plaintext
|
||
- BROWSER_TIMEOUT=60000
|
||
- TZ=Asia/Novosibirsk
|
||
- PROXY_URL=socks5://vless-proxy:1080
|
||
ports:
|
||
- "8191:8191"
|
||
networks:
|
||
- media_net
|
||
shm_size: 1gb # 64 MB /dev/shm убивает Chrome
|
||
sysctls:
|
||
- net.ipv6.conf.all.disable_ipv6=1
|
||
```
|
||
|
||
Порядок деплоя: бэкап compose → правка **локально** (не sed на сервере) → `scp` →
|
||
`cd /mnt/RED_2TB/docker/arr && docker compose up -d flaresolverr`.
|
||
⚠️ `docker compose up -d` **только из своего каталога**, иначе name-conflict.
|
||
|
||
> **Swap на Тайге = 0** и не нужен: RAM 21 GB, доступно 5.6 GB, FS держит 710 MB, CPU 0.68%.
|
||
|
||
## Практические замечания
|
||
|
||
- **Логи FS пусты при `LOG_LEVEL=warning`** — это правильно: на `info` он пишет
|
||
`login_password` трекеров в plaintext. Поднимать уровень только для диагностики прокси и сразу опускать.
|
||
- `sleep 35` после старта контейнера — Chrome поднимается ~30 с; раньше `:8191` может не отвечать.
|
||
- **`ERR_NAME_NOT_RESOLVED` в ответе FS = сессия без прокси**, а не «FS сломан».
|
||
- **Ключ API ломается многоуровневым экранированием** (ssh → docker → sh). Приём:
|
||
`printf "X-Api-Key: $PK" > /tmp/h.txt` внутри контейнера, затем `curl -H @/tmp/h.txt`.
|
||
- **`python3` внутри контейнера prowlarr НЕТ.** На хосте Тайги `python3` и `sqlite3` есть,
|
||
`curl` — нет. `sqlite3` в контейнерах отсутствует.
|
||
- **Порт `8191` наружу с Mac НЕ проброшен** (`Connection refused`) — скрипты запускать
|
||
на хосте Тайги (`scp` → `ssh ... python3`), не локально.
|
||
- **`shm_size` и `sysctls` — в блок сервиса** compose, не в `environment`.
|
||
|
||
## Индексеры (актуальный состав)
|
||
|
||
| id | Индексер | Теги | Статус |
|
||
|---|---|---|---|
|
||
| 1 | RuTracker | `[1,2]` | ✅ работает (логин проходит) |
|
||
| 2 | RuTor | `[1]` | ⚠️ caps ок, но нет результатов в категориях фильмов/сериалов (дефект трекера) |
|
||
| 3 | Byrutor | `[1]` | ✅ valid |
|
||
| 4 | NoNaMe Club | `[1,2]` | ✅ valid |
|
||
| 5 | Kinozal | `[1,2]` | ✅ valid |
|
||
| 6 | Kinozal (M) | `[1,2]` | ✅ valid (зеркало Kinozal, magnet-приоритет) |
|
||
|
||
## Смежное: Lidarr (SkyHook + путь загрузки)
|
||
|
||
Кратко; полный разбор — [[family/plans/lidarr-taiga-deploy]].
|
||
|
||
- `api.lidarr.audio` за Cloudflare: напрямую **403**, через `socks5h://vless-proxy:1080` **200**.
|
||
Домен **резолвится** — это чисто CF, не DNS-блок. **FlareSolverr тут бесполезен** (обслуживает Prowlarr).
|
||
- 🔴 **env `HTTP_PROXY`/`HTTPS_PROXY` Lidarr НЕ ЧИТАЕТ** — .NET-клиент SkyHook игнорирует env.
|
||
Работает только **встроенный прокси-конфиг** (`config/host`): `proxyEnabled=true`,
|
||
`proxyType=http`, `proxyHostname=vless-proxy`, `proxyPort=1081` (**HTTP**, не 1080 SOCKS5),
|
||
`proxyBypassFilter` — см. питфолл выше. Применяется без рестарта.
|
||
- 🔴 **`musicCategory` обязателен.** Дефолт пустой → Transmission кладёт файл плоско →
|
||
импорт в библиотеку падает. Значение — `lidarr`; каталоги `/mnt/storage/Downloads/{lidarr,radarr,sonarr}`
|
||
созданы, `RemotePathMapping` `/mnt/storage/Downloads/` → `/storage/Downloads/`, rootFolder `/storage/Music`.
|
||
- `monitored=false` у добавленного артиста → Lidarr не начнёт качать без включения мониторинга.
|
||
|
||
## Как проверить, что всё живо (быстрый чек-лист)
|
||
|
||
```bash
|
||
PK=$(grep -o "<ApiKey>[^<]*" /mnt/RED_2TB/docker/arr/prowlarr/config.xml | cut -d">" -f2)
|
||
# 1. FS отвечает
|
||
curl -s -X POST http://localhost:8191/v1 -H "Content-Type: application/json" \
|
||
-d '{"cmd":"sessions.list"}' # → {"status":"ok","version":"3.5.2"}
|
||
# 2. Карантина нет
|
||
curl -s -H "X-Api-Key: $PK" http://localhost:9696/api/v1/indexerstatus # → []
|
||
# 3. ПРИЁМКА — реальный поиск
|
||
curl -s -m 240 -H "X-Api-Key: $PK" \
|
||
"http://localhost:9696/api/v1/search?query=radiohead&indexerIds=1" \
|
||
| python3 -c "import json,sys; print(len(json.load(sys.stdin)))" # → 50
|
||
```
|
||
|
||
## Откат неудачного эксперимента 2026-09-29 (образ `flaresolverr-vnc`)
|
||
|
||
В сессии пробовался свой образ `flaresolverr-vnc` (Xvfb + fluxbox + x11vnc + noVNC, порт 6080)
|
||
под гипотезу «капчу решает человек». **Гипотеза оказалась ложной** (капча была симптомом
|
||
неверного пароля) → **всё снесено**: образ заменён на штатный, порт 6080 и volume `/config`
|
||
убраны из compose, каталог `/mnt/RED_2TB/docker/arr/fs-vnc/` удалён, роут
|
||
`vnc.mallexxx.duckdns.org` убран из Caddyfile, Caddy отключён от `media_net`
|
||
(вернулся в `caddy_default` + `transmission_default`).
|
||
|
||
> ⚠️ **Не переизобретать.** VNC-браузер не нужен: Prowlarr логинится автоматически.
|
||
> Если однажды CF реально потребует ручной вход — сначала **сверить пару логин/пароль в БД**.
|
||
|
||
### Что оказалось мифом (не тратить время повторно)
|
||
|
||
| Версия | Вердикт |
|
||
|---|---|
|
||
| «RuTracker требует капчу, нужен человек» | ❌ Капча = симптом неверного пароля. Верная пара логинится автоматически |
|
||
| «Кука авторизации — `bb_data`» | ❌ Она называется **`bb_session`** |
|
||
| «Капча привязана к холодной сессии, сессия `prowlarr` её обходит» | ❌ Ложный след, оставлен в истории диагностики |
|
||
| «На Кра́кене всё работает, надо сделать так же» | ❌ На Кра́кене **та же запись карантина** (не снятая с 2026-09-27) и **нет** капча-солвера |
|
||
| `CAPTCHA_SOLVER` в FlareSolverr | ❌ Официально non-functional (DeepWiki FS, issue #1452) — адаптеры удалены из образа |
|
||
| Persistent Chrome-профиль спасает cookies | ❌ На Кра́кене `/config` пуст, профиль не используется. Причина была не в профиле |
|