Files
mtproxy_checker/docs/docker.ru.md
T

16 KiB
Raw Blame History

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://… или с флагами — одна проверка, код выхода 04. docker run … ИМЯ_ОБРАЗА без command — долгоживущий API (нужны volume/env к файлу со списком).

HTTP API: mtproxy_checkerd

Лёгкий сервер на стандартной библиотеке Go: читает файл со списком tg:// (как в разделе «файл — одна ссылка на строку»), сразу после старта выполняет первый цикл проверок, затем повторяет с интервалом. Результаты последнего цикла отдаются по HTTP в JSON.

Запуск контейнера в режиме API

Без command / аргументов после образа entrypoint сам запускает mtproxy_checkerd.

Linux / macOS:

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):

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.yamldocker 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 (Linux) или Docker Desktop (Windows/macOS).
  • Для docker pull из приватного registry — логин и токен.

Сборка образа локально

Linux / macOS:

cd /path/to/mtproxy_checker
docker build -t mtproxy_checker:local .

Windows (PowerShell):

cd C:\path\to\mtproxy_checker
docker build -t mtproxy_checker:local .

Проверка, что образ собран (нужен хотя бы один аргумент, иначе стартует API):

docker run --rm mtproxy_checker:local -timeout 5s 2>&1 | head -1
# ожидается сообщение usage CLI (код выхода 2 — нормально без URL)

Запуск под Linux

Один прокси (ссылка tg://)

docker run --rm mtproxy_checker:local \
  'tg://proxy?server=example.com&port=443&secret=eeYOURHEX...'

С флагами утилиты (таймаут, DC):

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):

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.
  • Если имена не резолвятся:
docker run --rm --dns 8.8.8.8 mtproxy_checker:local 'tg://proxy?...'
  • Режим сети хоста (как на машине без NAT; используйте осознанно):
docker run --rm --network host mtproxy_checker:local 'tg://proxy?...'

На Docker Desktop для Windows/macOS --network host ведёт себя иначе, чем на Linux.

Несколько MTProxy ссылок подряд

Утилита принимает ровно один прокси за запуск. Чтобы проверить много tg:// ссылок, обходите их в оболочке.

Вариант 1: цикл в 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...

Скрипт:

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)

Осторожно: одновременно много контейнеров нагружают сеть и целевые прокси.

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»:

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 неудобны; проще двойные:

docker run --rm mtproxy_checker:local `
  "tg://proxy?server=example.com&port=443&secret=eeYOURHEX..."

Несколько ссылок:

$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 (документация).
  2. Создайте токен с правом на пакеты.
  3. Логин:
docker login git.shts.su
# или ваш registry
  1. Сборка workflow публикует теги latest, имя ветки/тега и sha-… — см. .gitea/workflows/docker.yaml.
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:

services:
  mtproxy-check:
    image: mtproxy_checker:local
    build: .
    command: ['tg://proxy?server=example.com&port=443&secret=ee...']
docker compose run --rm mtproxy-check

Для списка прокси удобнее внешний bash-цикл или отдельный command: ["sh","-c","..."] с циклом по файлу, смонтированному в volume.

Использование в CI другого проекта

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 контейнер удаляется сразу после выхода.