300 lines
14 KiB
Markdown
300 lines
14 KiB
Markdown
# 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` если не задано) | Таймаут **всей** одной проверки; для `MTPROXY_PROBE=deep` нужен запас (TLS + drain + ответ DC) |
|
||
| `MTPROXY_PROBE` | *(пусто)* → **`fast`** | `fast` — рукопожатие + init + короткое ожидание как в Telethon TcpMTProxy (#1134): OK, если прокси **не** рвёт TCP сразу после init (входящие байты не обязательны). `deep` — `req_pq`/`resPQ` через DC (строже, дольше) |
|
||
| `MTPROXY_DC_ID` | `2` | DC id (аналог `-dc-id` CLI) |
|
||
| `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` **не** используется |
|
||
|
||
### HTTP
|
||
|
||
| Метод | Путь | Ответ |
|
||
|--------|------|--------|
|
||
| GET | `/health` | `200`, `{"status":"ok"}` |
|
||
| GET | `/api/v1/proxies` | `200`, JSON с полями `cycle_finished_at`, `next_check_after`, массив `proxies` |
|
||
|
||
Элемент `proxies[]`: `raw_line`, при успешном разборе — `url`, `ok`, `exit_code` (`0` OK — при `MTPROXY_PROBE=fast`: рукопожатие + init и прокси не закрыл TCP сразу (как Telethon #1134); при **`deep`**: дополнительно получен `resPQ` от DC через туннель); `1` ошибка проверки, в т.ч. нет `resPQ` за время ожидания в `deep`; `2` ошибка разбора URL/секрета; `3` прокси закрыл соединение; `4` таймаут всего запроса), `error`, при необходимости `parse_error`, `checked_at`.
|
||
|
||
### 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`**: плюс подтверждён `resPQ` от DC |
|
||
| 1 | Ошибка (сеть, протокол; в **`deep`** — в т.ч. нет валидного `resPQ`) |
|
||
| 2 | Неверные аргументы CLI |
|
||
| 3 | Прокси закрыл TCP сразу после начального payload |
|
||
| 4 | Общий таймаут (`-timeout`) |
|
||
|
||
**Не путать с ping:** ICMP не используется.
|
||
|
||
Типичные сообщения:
|
||
|
||
- `connection refused` — порт закрыт или фильтр.
|
||
- `FAIL: timeout` — нет ответа за `-timeout`.
|
||
- `no resPQ from telegram through proxy` — до DC достучаться не удалось или ответ не похож на `resPQ` (часто совпадает с «Недоступен» в клиенте).
|
||
- Ошибки чтения/проверки ServerHello — несовместимый ответ или обрыв соединения.
|
||
|
||
Для отладки без `--rm` можно посмотреть логи контейнера по id; с `--rm` контейнер удаляется сразу после выхода.
|