Files
mtproxy_checker/docs/docker.ru.md
T

304 lines
16 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.
# Docker: образ mtproxy_checker
## Что внутри образа
| Компонент | Описание |
|-----------|----------|
| **База runtime** | Alpine Linux 3.20 |
| **Бинарники** | CLI: `/usr/local/bin/mtproxy_checker`. HTTP-сервис списка: `/usr/local/bin/mtproxy_checkerd` (см. ниже) |
| **Сертификаты** | Пакет `ca-certificates` (для TLS к реальным хостам при необходимости) |
| **ENTRYPOINT** | Скрипт `docker-entrypoint.sh`: **есть аргументы** после имени образа → `mtproxy_checker` (разовая проверка); **нет аргументов**`mtproxy_checkerd` (HTTP API) |
| **Порты** | В режиме CLI ничего не слушает (только исходящий TCP). В режиме API слушает **8080** (или `MTPROXY_HTTP_ADDR`). Образ объявляет **EXPOSE 8080** |
`docker run … ИМЯ_ОБРАЗА tg://…` или с флагами — одна проверка, **код выхода** `0``4`. `docker run … ИМЯ_ОБРАЗА` **без** command — долгоживущий API (нужны volume/env к файлу со списком).
## HTTP API: `mtproxy_checkerd`
Лёгкий сервер на стандартной библиотеке Go: читает файл со списком `tg://` (как в разделе «файл — одна ссылка на строку»), **сразу после старта** выполняет первый цикл проверок, затем повторяет с интервалом. Результаты последнего цикла отдаются по HTTP в JSON.
### Запуск контейнера в режиме API
**Без** `command` / аргументов после образа entrypoint сам запускает `mtproxy_checkerd`.
**Linux / macOS:**
```bash
docker run -d --name mtproxy-api --restart unless-stopped -p 8080:8080 \
-v /path/to/proxies.txt:/data/proxies.txt:ro \
-e MTPROXY_CHECK_INTERVAL=60m \
mtproxy_checker:local
```
**Windows (PowerShell):**
```powershell
docker run -d --name mtproxy-api --restart unless-stopped -p 8080:8080 `
-v "${PWD}/proxies.txt:/data/proxies.txt:ro" `
-e MTPROXY_CHECK_INTERVAL=60m `
mtproxy_checker:local
```
Явно указать демон (редко нужно): `--entrypoint /usr/local/bin/mtproxy_checkerd`.
Готовый пример: **`docker-compose.api.yaml`** — `docker compose -f docker-compose.api.yaml up --build`.
### Переменные окружения
| Переменная | По умолчанию | Назначение |
|------------|--------------|------------|
| `MTPROXY_LIST_FILE` | `/data/proxies.txt` | Путь к файлу: одна `tg://` ссылка на строку; пустые строки и строки с `#` в начале пропускаются |
| `MTPROXY_CHECK_INTERVAL` | `5m` | Интервал между циклами (`time.ParseDuration`, например `5m`, `1h`) |
| `MTPROXY_HTTP_ADDR` | `:8080` | Адрес прослушивания HTTP |
| `MTPROXY_CHECK_TIMEOUT` | `45s` (в демоне по умолчанию; CLI по-прежнему `15s` если не задано) | Таймаут **одной** попытки к выбранному DC; для `MTPROXY_PROBE=deep` нужен запас (TLS + drain + ответ DC). Если задан `MTPROXY_DC_IDS` с несколькими DC, общий бюджет цикла на строку ≈ `таймаут × число_DC` |
| `MTPROXY_PROBE` | *(пусто)***`fast`** | `fast` — рукопожатие + init + короткое ожидание как в Telethon TcpMTProxy (#1134): OK, если прокси **не** рвёт TCP сразу после init (входящие байты не обязательны); после основного 2s-окна — ещё несколько коротких `Read`, чтобы поймать **отложенный** FIN/RST (код выхода `3`, как в `deep`). `deep` — после init отправляется `req_pq`, OK если от DC пришло **любое** валидное unencrypted MTProto-сообщение (туннель реально несёт ответ DC; ICMP «ping» до DC через MTProxy невозможен). `deep-strict` — как раньше: ответ должен быть именно `resPQ` |
| `MTPROXY_DC_ID` | `2` | Один DC id (аналог `-dc-id` CLI), **игнорируется**, если задан непустой `MTPROXY_DC_IDS` |
| `MTPROXY_DC_IDS` | *(пусто)* | Список DC через запятую, например `1,2,3,4,5`: для каждой строки прокси выполняется отдельная проверка на каждый DC **по очереди**. В JSON у записи появляется массив `dcs[]` с результатом по каждому DC. Поле `ok` у строки — **true, если хотя бы один DC прошёл** (как при «есть живой путь к Telegram» при переборе DC) |
| `MTPROXY_ALLOWED_IPS` | *(не задана)* | Если задана непустая строка — доступ к **всем** маршрутам только с перечисленных IP/CIDR; остальные получают **403** и JSON `{"error":"forbidden"}`. Формат: через запятую, пробелы допускаются: `192.168.1.10`, `10.0.0.0/8`, IPv6 и CIDR вида `2001:db8::/32`. Учитывается только **`RemoteAddr`** TCP-соединения; заголовок `X-Forwarded-For` **не** используется |
| `MTPROXY_TDLIB_HELPER` | *(пусто)* | Путь к исполняемому **внешнему** helper: один аргумент — строка `tg://` с прокси. В stdout — строка JSON `{"ok":bool,"error":"…","exit_code":int}`. Рекомендуется нативный `tdlib_ping` (см. `cmd/tdlib_ping/README.md`); при необходимости — обёртка `exec node …/ping.js` из `contrib/tdlib-ping`. Запускается **только** при `MTPROXY_PROBE=fast` (или пусто). Без TDLib в образе по умолчанию |
| `MTPROXY_TDLIB_TIMEOUT` | `45s` | Таймаут **одного** вызова helper на строку прокси |
### HTTP
| Метод | Путь | Ответ |
|--------|------|--------|
| GET | `/health` | `200`, `{"status":"ok"}` |
| GET | `/api/v1/proxies` | `200`, JSON с полями `cycle_finished_at`, `next_check_after`, массив `proxies` |
Элемент `proxies[]`: поля **`standard`** (наша проверка: `ok`, `exit_code`, `error`, `parse_error`) и **`tdlib`** (результат helper: `ran`, при пропуске — `skipped_reason`; при запуске — `ok`, `exit_code`, `error`, `duration_ms`). Для совместимости дублируются корневые `ok`, `exit_code`, `error`, `parse_error` — те же значения, что и в `standard`. `raw_line`, при успешном разборе — `url`, `checked_at`. Смысл кодов в `standard`: при `MTPROXY_PROBE=fast` — рукопожатие + init (+ ловля отложенного закрытия); при **`deep`** / **`deep-strict`** — см. описание `MTPROXY_PROBE`. Если задан `MTPROXY_DC_IDS` с **несколькими** DC — массив `dcs`; корневой `ok` true, если **хотя бы один** DC успешен.
### Whitelist IP и Docker
Процесс видит **IP источника TCP** таким, каким его передал стек в контейнер. При пробросе порта с хоста (`-p 8080:8080`) на Linux часто это адрес **шлюза bridge** или **userland-proxy**, а не «настоящий» IP клиента с хоста. Имеет смысл задавать **CIDR подсети Docker** (например `172.17.0.0/16`), слушать только внутреннюю сеть, использовать **`--network host`** (на Linux) или ограничивать доступ на **обратном прокси** (nginx `allow` и т.п.).
## Требования
- [Docker Engine](https://docs.docker.com/engine/install/) (Linux) или Docker Desktop (Windows/macOS).
- Для `docker pull` из приватного registry — логин и токен.
## Сборка образа локально
**Linux / macOS:**
```bash
cd /path/to/mtproxy_checker
docker build -t mtproxy_checker:local .
```
**Windows (PowerShell):**
```powershell
cd C:\path\to\mtproxy_checker
docker build -t mtproxy_checker:local .
```
Проверка, что образ собран (нужен хотя бы один аргумент, иначе стартует API):
```bash
docker run --rm mtproxy_checker:local -timeout 5s 2>&1 | head -1
# ожидается сообщение usage CLI (код выхода 2 — нормально без URL)
```
## Запуск под Linux
### Один прокси (ссылка `tg://`)
```bash
docker run --rm mtproxy_checker:local \
'tg://proxy?server=example.com&port=443&secret=eeYOURHEX...'
```
С флагами утилиты (таймаут, DC):
```bash
docker run --rm mtproxy_checker:local \
-timeout 25s -dc-id 2 \
'tg://proxy?server=example.com&port=443&secret=eeYOURHEX...'
```
Чтобы не светить секрет в истории shell, прочитайте его из файла и передайте флагами (аргументы уходят в CLI через `docker-entrypoint.sh`):
```bash
SECRET=$(tr -d ' \n' < secret.hex)
docker run --rm mtproxy_checker:local \
--server example.com --port 443 --secret "$SECRET"
```
### Сеть и DNS на Linux
- По умолчанию контейнер использует bridge Docker; нужен исходящий доступ к `server:port` и рабочий DNS.
- Если имена не резолвятся:
```bash
docker run --rm --dns 8.8.8.8 mtproxy_checker:local 'tg://proxy?...'
```
- Режим сети хоста (как на машине без NAT; используйте осознанно):
```bash
docker run --rm --network host mtproxy_checker:local 'tg://proxy?...'
```
На Docker Desktop для Windows/macOS `--network host` ведёт себя иначе, чем на Linux.
## Несколько MTProxy ссылок подряд
Утилита принимает **ровно один** прокси за запуск. Чтобы проверить много `tg://` ссылок, обходите их в оболочке.
### Вариант 1: цикл в bash
```bash
IMAGE=mtproxy_checker:local # или registry/owner/mtproxy_checker:latest
for url in \
'tg://proxy?server=a.example.com&port=443&secret=ee1111...' \
'tg://proxy?server=b.example.com&port=8443&secret=ee2222...' \
'tg://proxy?server=c.example.com&port=443&secret=dd3333...'
do
echo "=== $url ==="
if docker run --rm "$IMAGE" -timeout 20s "$url"; then
echo "OK"
else
echo "FAIL (код $?)"
fi
done
```
### Вариант 2: файл — одна ссылка на строку
`proxies.txt` (пустые строки и строки с `#` в начале можно пропускать):
```
tg://proxy?server=one.example.com&port=443&secret=ee...
tg://proxy?server=two.example.com&port=8443&secret=ee...
```
Скрипт:
```bash
IMAGE=mtproxy_checker:local
while IFS= read -r line || [ -n "$line" ]; do
line="${line#"${line%%[![:space:]]*}"}" # trim leading spaces
[ -z "$line" ] && continue
case "$line" in \#*) continue ;; esac
echo "=== $line ==="
docker run --rm "$IMAGE" -timeout 20s "$line" && echo OK || echo "FAIL ($?)"
done < proxies.txt
```
### Вариант 3: параллельно (`xargs`)
Осторожно: одновременно много контейнеров нагружают сеть и целевые прокси.
```bash
IMAGE=mtproxy_checker:local
grep -v '^\s*$\|^\s*#' proxies.txt | xargs -I{} -P 4 sh -c \
'echo "=== {}"; docker run --rm '"$IMAGE"' -timeout 20s "{}" || echo FAIL'
```
`-P 4` — не более четырёх проверок одновременно; уменьшите при необходимости.
### Вариант 4: итоговый код для CI
Если нужен «провал всего job при любом FAIL»:
```bash
set -e
IMAGE=mtproxy_checker:local
while IFS= read -r url; do
[ -z "$url" ] && continue
case "$url" in \#*) continue ;; esac
docker run --rm "$IMAGE" -timeout 25s "$url"
done < proxies.txt
```
## Запуск из PowerShell (Windows)
Одинарные кавычки с `tg://` в PowerShell неудобны; проще двойные:
```powershell
docker run --rm mtproxy_checker:local `
"tg://proxy?server=example.com&port=443&secret=eeYOURHEX..."
```
Несколько ссылок:
```powershell
$urls = @(
"tg://proxy?server=a.com&port=443&secret=ee...",
"tg://proxy?server=b.com&port=8443&secret=ee..."
)
foreach ($u in $urls) {
Write-Host "=== $u ==="
docker run --rm mtproxy_checker:local -timeout 20s $u
if ($LASTEXITCODE -ne 0) { Write-Host "FAIL exit $LASTEXITCODE" }
}
```
## Загрузка образа из Container Registry Gitea
1. Включите **Packages / Container Registry** на инстансе Gitea ([документация](https://docs.gitea.com/usage/packages/container)).
2. Создайте токен с правом на пакеты.
3. Логин:
```bash
docker login git.shts.su
# или ваш registry
```
4. Сборка workflow публикует теги `latest`, имя ветки/тега и `sha-…` — см. `.gitea/workflows/docker.yaml`.
```bash
docker pull git.shts.su/owner/mtproxy_checker:latest
docker run --rm git.shts.su/owner/mtproxy_checker:latest 'tg://proxy?...'
```
## Пример docker-compose
Одна проверка за `compose run`:
```yaml
services:
mtproxy-check:
image: mtproxy_checker:local
build: .
command: ['tg://proxy?server=example.com&port=443&secret=ee...']
```
```bash
docker compose run --rm mtproxy-check
```
Для списка прокси удобнее внешний bash-цикл или отдельный `command: ["sh","-c","..."]` с циклом по файлу, смонтированному в volume.
## Использование в CI другого проекта
```bash
docker run --rm registry.example.com/owner/mtproxy_checker:latest \
"tg://proxy?server=${MTPROXY_HOST}&port=${MTPROXY_PORT}&secret=${MTPROXY_SECRET}"
```
## Коды выхода и диагностика
| Код | Значение |
|-----|----------|
| 0 | **`fast`**: Fake-TLS/dd + MTProxy init, прокси не закрыл соединение сразу (как Telethon). **`deep`**: плюс ответ DC после `req_pq` (валидное unencrypted MTProto). **`deep-strict`**: ответ должен быть `resPQ` |
| 1 | Ошибка (сеть, протокол; в **`deep`** — нет ответа DC; в **`deep-strict`** — нет `resPQ`) |
| 2 | Неверные аргументы CLI |
| 3 | Прокси закрыл TCP сразу после начального payload |
| 4 | Общий таймаут (`-timeout`) |
**Не путать с ping:** ICMP не используется.
Типичные сообщения:
- `connection refused` — порт закрыт или фильтр.
- `FAIL: timeout` — нет ответа за `-timeout`.
- `no reply from telegram DC through proxy (timeout)` — в режиме **`deep`** за время ожидания не получено ни одного валидного unencrypted-ответа DC после `req_pq`.
- `no resPQ from telegram through proxy (tunnel may be broken)` — только в **`deep-strict`**: ответ не распознан как `resPQ`.
- Ошибки чтения/проверки ServerHello — несовместимый ответ или обрыв соединения.
Для отладки без `--rm` можно посмотреть логи контейнера по id; с `--rm` контейнер удаляется сразу после выхода.